#!/bin/bash

# This Install script is used by the app-manager program to install and uninstall
# the app's dependencies.
#
# app-manager requires this script to accept the following command
# line arguments:
#
#   install - installs any dependancies
#   postinstall - performs any needed post install actions
#   remove - uninstalls any dependancies
#
# This script can be fully customized if you need to install non-ipk
# dependancies or no dependancies at all.
#
# This default template will read the p_manifest.json file that must be
# located in the provisioning directory of the application.
# It will process the p_manifest.json file and install any .ipk files listed
# that exist in the provisioning directory.  The format of the dependency
# manifiest is:
#
# {
#   pkgs: [
#	    { “FileName” : “file1.ipk”, “type” : “ipk”, “PkgName” : “name” },
#	    { “FileName” : “file2.ipk”, “type” : “ipk”, “PkgName : “name” }
#   ]
# }
#
# FileName : the name of the .ipk file to install
# type : for now just "ipk" is supported
# PkgName : name of the ipk package that appears when opkg list-packages is run


# Firmware upgrade canary indicator (from /etc/init.d/customapp)
# This file is created by the gateway after successful firmware upgrade.
# The existence of this file instructs the gateway to reinstall all of the
# custom apps. In this case we do not want to reinstall the app because there
# is no guarantee that the db.json will be compatible with the new firmware.
# | WARNING
# | If the install script reboots the gateway before this file is removed then
# | the gateway will be stuck in a never ending reboot loop.
UPGRADE_OCCURRED_PATH="/var/config/upgrade_occurred"


# The provisioning (dependency manifest)
DIR_PATH=$(dirname "$0")
MANIFEST="${DIR_PATH}/provisioning/p_manifest.json"

APPTYPE="SKF Myrina"

function log {
    msg="$1"
    echo "$msg"
    logger -t Install "$msg"
}

function run {
    cmd="$1"

    eval "${cmd}"
    retval=$?
    if [ $retval != 0 ]; then
        log "Command '$cmd' failed with status $retval"
        exit $retval
    fi
}

function clean_lora_database {
    # As a first step remove the existing LoRa Network Server database.
    # This is due to a bug in 5.2.1 firmware that fills up all disk space 
    # in /var/config/ by filling the `nonces` table lora-network-server.db
    log "Remove LoRa database"
    rm -rf /var/config/lora
}

function replace_configuration {
    log "Copy gateway configuration"
    cp -f $DIR_PATH/deviceConfigs/db.json /var/config/db.json

    # Overwriting the config file is not enough for changes to take effect.
    # Calling /save and /save_apply is not useful here because the config
    # file was written to disk, and saving only commits new settings that
    # were changed in memory. /revert will load the configuration from disk.
    log "Commit gateway configuration"
    RESULT=$(curl -s -X POST --data "" http://localhost/api/command/revert)
    if [[ $RESULT != *"200"* ]]; then
        log "WARNING: Failed to commit configuration."
        log $RESULT
    fi
}

function restart_gateway {
    # Loading the configuration from db.json will not automatically
    # start all of the stopped services. Instead of starting them
    # one by one we simply reboot and let the gateway do it using
    # the new configuration.
    log "Saving OEM configuration"
    RESULT=$(curl -s -X POST --data "" http://localhost/api/command/save_oem)
    if [[ $RESULT != *"200"* ]]; then
        log "WARNING: Failed to save oem configuration."
        log $RESULT
    fi
    log "Restarting gateway..."
    curl -s -X POST --data "" http://localhost/api/command/restart
}

function SKFSetup {
    # A script run by DeviceHQ and the Nordcloud Gateway Setup Tool on
    # the gateway to copy configuration and setup files in place
    log "Running ${APPTYPE} setup script..."

    log "Install dependencies"
    install_packages

    log "Copy certificates and keys"
    mkdir -p /var/config/ca-certificates
    cp -a $DIR_PATH/deviceConfigs/ca-certificates/. /var/config/ca-certificates

    log "${APPTYPE} setup script finished"
}

# This function is called via the install argument during app installation. It
# reads the p_manifest.json file in the provisioning directory of the
# application, and executes an "opkg install" on each .ipk package file listed
# in the p_manifest.

function install_packages {
    if [ ! -f "$MANIFEST" ]; then
        log "Manifest doesn't exists on ${MANIFEST}"
        return
    fi
    JSONTXT=$(<"$MANIFEST")
    PACKAGES=$(echo "$JSONTXT" | jsparser --count -p /pkgs)

    RETURNCODE=$?
    if [ $RETURNCODE -ne 0 ]; then
      log "Failed to parse p_manifest.json as json"
      exit $RETURNCODE
    fi

    for ((i=0; i < PACKAGES; i++)); do
        PKG=$(echo "$JSONTXT" | jsparser --jsobj -p /pkgs/$i)
        PKGNAME=$(echo "$PKG" | jsparser -p /PkgName)
        PKGFILE=$(echo "$PKG" | jsparser -p /FileName)
        PKGTYPE=$(echo "$PKG" | jsparser -p /type)
        log "Installing '${PKGNAME}' with type '${PKGTYPE}'"

        if [ "$PKGTYPE" == "ipk" ]; then
            run "opkg install ${DIR_PATH}/provisioning/${PKGFILE}"
        fi

        if [ "$PKGTYPE" == "sh" ]; then
            run "mkdir -p ${DIR_PATH}/provisioning/$PKGNAME"
            run "tar xzf ${DIR_PATH}/provisioning/${PKGFILE} -C ${DIR_PATH}/provisioning/$PKGNAME"
            run "${DIR_PATH}/provisioning/${PKGNAME}/install.sh"
        fi

        log "Package '${PKGNAME}' successfully installed"
    done
}

# This function is called via the remove argument during app uninstall. It
# reads the p_manifest.json file in the provisioning direcgtory of the
# application and executes an "opkg remove" on each package listed
# in the p_manifest.json.

function remove_packages {
    log "Removing dependencies..."
    if [ ! -f "$MANIFEST" ]; then
        log "provisioning manifest file not found"
        return
    fi
    JSONTXT=$(<./provisioning/p_manifest.json)
    PACKAGES=$(echo "$JSONTXT" | jsparser --count -p /pkgs)

    RETURNCODE=$?
    if [ $RETURNCODE -ne 0 ]; then
      log "Failed to parse p_manifest.json as json"
      exit $RETURNCODE
    fi

    for ((i=0; i < PACKAGES; i++))
    do
        PKG=$(echo "$JSONTXT" | jsparser --jsobj -p /pkgs/$i)
        PKGNAME=$(echo "$PKG" | jsparser -p /PkgName)
        PKGFILE=$(echo "$PKG" | jsparser -p /FileName)
        PKGTYPE=$(echo "$PKG" | jsparser -p /type)
        log "Uninstalling '${PKGNAME}' with type '${PKGTYPE}'"

        if [ "$PKGTYPE" == "ipk" ]; then
            run "opkg remove --force-depends ${PKGNAME}"
        fi

        if [ "$PKGTYPE" == "sh" ]; then
            run "mkdir -p ${DIR_PATH}/provisioning/$PKGNAME"
            run "tar xzf ${DIR_PATH}/provisioning/${PKGFILE} -C ${DIR_PATH}/provisioning/$PKGNAME"
            run "${DIR_PATH}/provisioning/${PKGNAME}/uninstall.sh"
        fi

        log "Package '${PKGNAME}' successfully removed"
    done
}

# install is invoked by app-manager while installing a custom app.
function install {
    log "Running ${APPTYPE} app install..."
    if [ -f $UPGRADE_OCCURRED_PATH ]; then
        # Upgrading firmware may leave the app in an inconsistent state.
        # The app manager will reinstall all the custom apps after upgrade.
        log "Firmware upgrade detected."
        log "Performing a partial app install."
        SKFSetup
    else
        # The gateway is being configured by user request, we assume the
        # latest configuration was provided and clean any existing sessions.
        log "Performing a full app install."
        clean_lora_database
        SKFSetup
        replace_configuration
    fi
}

# postinstall is invoked by app-manager immediately after installing a custom app.
# TODO: maybe use this step to check that all files are in place
function postinstall {
    log "Running ${APPTYPE} app post install..."
    if [ -f $UPGRADE_OCCURRED_PATH ]; then
        log "Firmware upgrade detected."
    else
        restart_gateway
    fi
}

# remove is invoked by app-manager while uninstalling a custom app.
function remove {
    log "Removing ${APPTYPE} Dependencies..."
    remove_packages
}

function usage {
  echo "Usage: $0 {install|postinstall|remove}" >&2
  exit 1
}

case "$1" in
  install)
    install
    ;;
  postinstall)
    postinstall
    ;;
  remove)
    remove
    ;;
  *)
    usage
    ;;
esac

exit 0
