    #!/bin/zsh

    # Parts of the code contained in this file are:
    # Copyright 2020 Armin Briegel, Scripting OS X
    #
    #    Licensed under the Apache License, Version 2.0 (the "License");
    #    you may not use this file except in compliance with the License.
    #    You may obtain a copy of the License at
    #
    #        http://www.apache.org/licenses/LICENSE-2.0
    #
    #    Unless required by applicable law or agreed to in writing, software
    #    distributed under the License is distributed on an "AS IS" BASIS,
    #    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    #    See the License for the specific language governing permissions and
    #    limitations under the License.

    ################################
    # Required Variables
    ################################
    # The four required variables are
    #
    # • name: The display name of the installed application without the .app extensions.
    #
    # • type: The type of installation. Possible values:
    #
    #     dmg: application in disk image file (drag'n drop installation)
    #     zip: application in zip archive (zip extension)
    #     tbz: application in tbz archive (tbz extension)
    #     appInDmgInZip: an app in a dmg file that has been zip'ed
    #
    #     pkg: flat pkg download
    #     pkgInDmg: a pkg file inside a disk image
    #     pkgInZip: a pkg file inside a zip

    # • downloadURL: The URL from which to download the archive. The URL can be generated by a series of commands, for example when you need to parse an xml file for the latest URL. (See labels bbedit, desktoppr, or omnigraffle for examples.) Sometimes version differs between Intel and Apple Silicon versions. (See labels brave,  obsidian, omnidisksweeper, or notion.)
    #
    # • expectedTeamID: The 10-character Developer Team ID with which the application or pkg is signed and notarized.
    #
    ################################
    # Strongly Recommended Variables
    ################################
    # appNewVersion: Version of the downloaded software.
    #    When given, it will be compared to installed version. This can prevent unnecessary downloads and installations.
    #
    # versionKey (optional): When the version is being compared to the locally installed app, sometimes we need another field than CFBundleShortVersionString. Often CFBundleVersion is the right one, but techically it could be another field. It's usually dependant on what number the web sites most easily returns.
    #
    # packageID (optional, but recommended for pkgs without an app) This variable is for pkg bundle IDs. Very usefull if a pkg only install command line tools, or the like that does not install an app. (See label desktoppr, golang, installomator_st, odrive, or teamviewerhost.)
    #
    ################################
    # Optional Variables
    ################################
    # archiveName: (optional) The name of the downloaded file. When not given the archiveName is set to $name.$type
    #
    # appName: (optional) File name of the app bundle in the dmg to verify and copy (include the .app). When not given, the appName is set to $name.app. This is also the name of the app that will get reopned, if we closed any blockingProcesses (see further down)
    #
    # appCustomVersion : (optional) This function can be added to your label, if a specific custom mechanism has to be used for getting the installed version. (Instead of checking the .app version on disk, or checking a pkg receipt) Commonly used to check the output of a binary command, like /usr/bin/MyApp --version. Example: See labels zulujdk11, zulujdk13, zulujdk15.
    #
    # targetDir: (optional) dmg or zip: Applications will be copied to this directory. Default value is '/Applications' for dmg and zip installations. pkg: targetDir is used as the install-location. Default is '/'.
    #
    # blockingProcesses: (optional) Array of process names that will block the installation or update. If no blockingProcesses array is given the default will be: blockingProcesses=( $name ) When a package contains multiple applications, all should be listed, e.g: blockingProcesses=( "Keynote" "Pages" "Numbers" ) When a workflow has no blocking processes or it should not be checked (if the pkg will handle this), use blockingProcesses=( NONE )
    #
    # pkgName: (optional, only used for dmgInPkg and dmgInZip) File name of the pkg file inside the dmg or zip. When not given the pkgName is set to $name.pkg.
    #
    # CLIInstaller:
    #
    # CLIArguments: If the downloaded dmg is actually an installer that we can call using CLI, we can use these two variables for what to call. We need to define name for the installed app (to be version checked), as well as installerTool for the installer app (if named differently that name. Installomator will add the path to the folder/disk image with the binary, and it will be called like this: $CLIInstaller $CLIArguments For most installations CLIInstaller should contain the installerTool for the CLI call (if it’s the same). We can support a whole range of other software titles by implementing this. (See label adobecreativeclouddesktop.)
    #
    # installerTool: Introduced as part of CLIInstaller. If the installer in the DMG or ZIP is named differently than the installed app, then this variable can be used to name the installer that should be located after mounting/expanding the downloaded archive. (See label adobecreativeclouddesktop.)
    #
    # curlOptions (optional): If the downloadURL cannot work directly using curl-command, then we can usually add some options to that command, and the server will return what we need. For instance if the server automatically detects platform due to header fields, we might need to add a header field -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15". This variable should be an array, so it should be surrounded by (). Se labels mmhmm and mochatelnet, or canva for even more headings in a curl request.
    ################################
    # FUNCTIONS FUNCTIONS FUNCTIONS
    ################################

    # will get the latest release download from a github repo
    downloadURLFromGit() { # $1 git user name, $2 git repo name
        gitusername=${1?:"no git user name"}
        gitreponame=${2?:"no git repo name"}

        if [[ $type == "pkgInDmg" ]]; then
            filetype="dmg"
        elif [[ $type == "pkgInZip" ]]; then
            filetype="zip"
        else
            filetype=$type
        fi

        if [ -n "$archiveName" ]; then
            downloadURL=$(curl -sfL "https://api.github.com/repos/$gitusername/$gitreponame/releases/latest" | awk -F '"' "/browser_download_url/ && /$archiveName\"/ { print \$4; exit }")
            if [[ "$(echo $downloadURL | grep -ioE "https.*$archiveName")" == "" ]]; then
                #downloadURL=https://github.com$(curl -sfL "https://github.com/$gitusername/$gitreponame/releases/latest" | tr '"' "\n" | grep -i "^/.*\/releases\/download\/.*$archiveName" | head -1)
                downloadURL="https://github.com$(curl -sfL "$(curl -sfL "https://github.com/$gitusername/$gitreponame/releases/latest" | tr '"' "\n" | grep -i "expanded_assets" | head -1)" | tr '"' "\n" | grep -i "^/.*\/releases\/download\/.*$archiveName" | head -1)"
            fi
        else
            downloadURL=$(curl -sfL "https://api.github.com/repos/$gitusername/$gitreponame/releases/latest" | awk -F '"' "/browser_download_url/ && /$filetype\"/ { print \$4; exit }")
            if [[ "$(echo $downloadURL | grep -ioE "https.*.$filetype")" == "" ]]; then
                #downloadURL=https://github.com$(curl -sfL "https://github.com/$gitusername/$gitreponame/releases/latest" | tr '"' "\n" | grep -i "^/.*\/releases\/download\/.*\.$filetype" | head -1)
                downloadURL="https://github.com$(curl -sfL "$(curl -sfL "https://github.com/$gitusername/$gitreponame/releases/latest" | tr '"' "\n" | grep -i "expanded_assets" | head -1)" | tr '"' "\n" | grep -i "^/.*\/releases\/download\/.*\.$filetype" | head -1)"
            fi
        fi
        if [ -z "$downloadURL" ]; then
            cleanupAndExit 14 "could not retrieve download URL for $gitusername/$gitreponame" ERROR
        else
            echo "$downloadURL"
            return 0
        fi
    }

    versionFromGit() {
        # credit: Søren Theilgaard (@theilgaard)
        # $1 git user name, $2 git repo name
        gitusername=${1?:"no git user name"}
        gitreponame=${2?:"no git repo name"}

        #appNewVersion=$(curl -L --silent --fail "https://api.github.com/repos/$gitusername/$gitreponame/releases/latest" | grep tag_name | cut -d '"' -f 4 | sed 's/[^0-9\.]//g')
        appNewVersion=$(curl -sLI "https://github.com/$gitusername/$gitreponame/releases/latest" | grep -i "^location" | tr "/" "\n" | tail -1 | sed 's/[^0-9\.]//g')
        if [ -z "$appNewVersion" ]; then
            printlog "could not retrieve version number for $gitusername/$gitreponame" WARN
            appNewVersion=""
        else
            echo "$appNewVersion"
            return 0
        fi
    }

    # Handling of differences in xpath between Catalina and Big Sur
    xpath() {
        # the xpath tool changes in Big Sur and now requires the `-e` option
        if [[ $(sw_vers -buildVersion) > "20A" ]]; then
            /usr/bin/xpath -e $@
            # alternative: switch to xmllint (which is not perl)
            #xmllint --xpath $@ -
        else
            /usr/bin/xpath $@
        fi
    }

    # from @Pico: https://macadmins.slack.com/archives/CGXNNJXJ9/p1652222365989229?thread_ts=1651786411.413349&cid=CGXNNJXJ9
    getJSONValue() {
        # $1: JSON string OR file path to parse (tested to work with up to 1GB string and 2GB file).
        # $2: JSON key path to look up (using dot or bracket notation).
        printf '%s' "$1" | /usr/bin/osascript -l 'JavaScript' \
            -e "let json = $.NSString.alloc.initWithDataEncoding($.NSFileHandle.fileHandleWithStandardInput.readDataToEndOfFile$(/usr/bin/uname -r | /usr/bin/awk -F '.' '($1 > 18) { print "AndReturnError(ObjC.wrap())" }'), $.NSUTF8StringEncoding)" \
            -e 'if ($.NSFileManager.defaultManager.fileExistsAtPath(json)) json = $.NSString.stringWithContentsOfFileEncodingError(json, $.NSUTF8StringEncoding, ObjC.wrap())' \
            -e "const value = JSON.parse(json.js)$([ -n "${2%%[.[]*}" ] && echo '.')$2" \
            -e 'if (typeof value === "object") { JSON.stringify(value, null, 4) } else { value }'
    }

    # Define a dummy printlog function to suppress errors
    printlog() { :; }

    ################################
    # MAIN  MAIN  MAIN  MAIN  MAIN
    ################################

    if [[ -z "$1" ]]; then
        echo "Label file path required"
        exit 1
    fi

    fullPathToLabel="$1"

    # Ensure the label file exists
    if [[ ! -f "$fullPathToLabel" ]]; then
        echo "Label file does not exist: $fullPathToLabel"
        exit 1
    fi

    # get current arch
    current_arch=$(/usr/bin/arch)

    # echo $fullPathToLabel
    label=$fullPathToLabel:t:r
    # echo $label
    pathOnly=$fullPathToLabel:h
    # echo $pathOnly
    guid=${pathOnly##*/}     # Extract the last part of the path
    guid=${guid##*_}         # Extract everything after the last underscore (_)
    # echo $guid

    if [[ "$current_arch" == "i386" ]]; then
        arch_alt="_i386"
    fi

    # Surpress helper tools from running when labels are evaluated
    INSTALL="force"

    # Read contents of the label into variable
    labelFile=$(/bin/cat "${fullPathToLabel}")

    # Load all values in label into respective variables
    eval 'case "$label" in '"$labelFile"'; esac' >/dev/null 2>&1

    downloadFile=$downloadURL:t
    labelPath=$fullPathToLabel:h

    # Get icon path if available
    if [[ -f "${labelPath}/${label}.png" ]]; then
        labelIcon="${labelPath}/${label}.png"
    elif [[ -f "${labelPath}/${label}.jpg" ]]; then
        labelIcon="${labelPath}/${label}.jpg"
    elif [[ -f "${labelPath}/${label}.gif" ]]; then
        labelIcon="${labelPath}/${label}.gif"
    fi


    # Output the json of all available values
    # printf '{ "appName": "%s", "appNewVersion": "%s", "archiveName": "%s", "blockingProcesses": "%s", "CLIInstaller": "%s", "CLIArguments": "%s", "curlOptions": "%s", "description": "%s", "downloadFile": "%s", "downloadURL": "%s", "expectedTeamID": "%s", "icon": "%s", "installerTool": "%s", "label": "%s", "name": "%s", "packageID": "%s", "pkgName": "%s", "targetDir": "%s", "type": "%s", "versionKey": "%s" }\n' "$appName" "$appNewVersion" "$archiveName" "$blockingProcesses" "$CLIInstaller" "$CLIArguments" "$curlOptions" "$labelDescription" "$downloadFile" "$downloadURL" "$expectedTeamID" "$labelIcon" "$installerTool" "$label" "$name" "$packageID" "$pkgName" "$targetDir" "$type" "$versionKey"

    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "appName" "$appName" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "appName" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "appNewVersion" "$appNewVersion" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "appNewVersion" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "archiveName" "$archiveName" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "archiveName" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "blockingProcesses" "$blockingProcesses" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "blockingProcesses" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "CLIInstaller" "$CLIInstaller" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "CLIInstaller" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "CLIArguments" "$CLIArguments" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "CLIArguments" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "curlOptions" "$curlOptions" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "curlOptions" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "Arch" "$current_arch" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "curlOptions" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "downloadFile" "$downloadFile" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "downloadFile" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "downloadURL" "$downloadURL" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "downloadURL" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "expectedTeamID" "$expectedTeamID" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "expectedTeamID" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "guid" "$guid" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "guid" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "labelIcon" "$labelIcon" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "labelIcon" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "installerTool" "$installerTool" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "installerTool" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "label" "$label" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "label" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "name" "$name" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "name" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "packageID" "$packageID" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "packageID" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "pkgName" "$pkgName" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "pkgName" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "targetDir" "$targetDir" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "targetDir" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "type" "$type" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "type" "" 2>/dev/null
    /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "versionKey" "$versionKey" 2>/dev/null || /usr/bin/defaults write "${pathOnly}/${label}${arch_alt}.plist" "versionKey" "" 2>/dev/null

    chmod +r "${pathOnly}/${label}${arch_alt}.plist"

    if [[ -e "${pathOnly}/${label}${arch_alt}.plist" ]]; then
        echo -n "Plist created"
    else
        echo -n "Plist failed"
    fi
