#!/bin/sh

FILE_RETRIES="/tmp/flexa-retries"

# gets the api-version
# 
# parameters:
#  $1: path to json-received from service portal
#
# returns:
#  1: old
#  2: new

api_version() {
    if [ ! -f $1 ]; then
        return 0
    fi
    grep "configUrl" $1 > /dev/null
    if [ $? -eq 0 ]; then
        return 1
    else
        return 2
    fi
}


# checks if the directory and the config files 
# are in place
#
# parameters:
#  $1: service-json
#  $2: app-json
#
# returns:
#  0: ok
#  1: not ok

check_flexa_directories()
{
  if [ ! -f "$1" ] || [ ! -f "$2" ]; then
    return 1
  fi

  return 0
}

# gets the version of the installed application
# 
# parameters:
#  $1: service-json file

get_app_version() {
 
  version=$( jq -r '.version' $1 ) 
  echo ${version}
}

# sends post request
#
# parameters:
#   $1: parameters in json format
#   $2: output file
#   $3: url 
#
# returns:
#   responscode
# creates:
#   output file containing the response

post_request() {
  logger "sending request $1 to $3 ..."
  logger "$1"
  #logger " POST -H \"Content-Type: application/json\" -d $1 -s -o $2 -w \"%{http_code}\" $3"
  response=$( curl \
    -X POST \
    -H "Content-Type: application/json" \
    -d "$1" \
    --max-time 10 \
    -s \
    -o $2 \
    -w "%{http_code}" \
    $3 ) 
  #logger "sending request $1 to $3 ... Done: ${response}"
  if [ ${response} == "200" ] && [ -f ${FILE_RETRIES} ]; then
      rm -f ${FILE_RETRIES}
  fi
  echo ${response}
}


# sends post request based on the new API
# Note: new api contains the POST in the
#       Json's url filed
#
# parameters:
#   $1: parameters in json format
#   $2: output file
#   $3: url 
# returns:
#    responsecode
# creates:
#    output file containing the response    

post_request_new_api() {
  logger "sending request $1 to $3 ..."
  response=$( curl \
    -H "Content-Type: application/json" \
    -d $1 \
    --max-time 10 \
    -s \
    -o $2 \
    -w "%{http_code}" \
    -X $3 )
  logger "sending request $1 to $3 ... Done: ${response}"
  echo ${response}
}

# checks whether a command exits or not
# Returns True or False
# parameters:
#   $1: name_of_the_program/command example <gedit> , <grep> etc

exists()
{
  command -v "$1" >/dev/null 2>&1
}


# Function for generating the data in json format to send to Flexa-registry.
# parameters:
#   $1: regId

generate_data(){
    VERSION=$(cat /barix/info/VERSION)
    DEVICE=$(cat /proc/sys/kernel/hostname)
    RUNTIME_VERSION=$(cat /barix/info/RUNTIME_VERSION)
    # getting Hardware
    if exists qiba-spi-get-production-info.sh ; then
        HW=$(qiba-spi-get-production-info.sh -w)
    MAC_ETH0=$(cat /sys/class/net/eth0/address)
    ADDR_ETH0=$(ifconfig eth0 | grep 'inet' | grep -v 'inet6' | awk '{ print $2}')
cat <<EOF
{"regId":"$1", "DeviceInfo" :{ "Firmware version": "$VERSION" , "Device": "$DEVICE" , "HW":${HW}, 
"RuntimeVersion": "$RUNTIME_VERSION", "MAC eth0": "$MAC_ETH0", "Address eth0": "$ADDR_ETH0"}
}
EOF
# In case accessing the device hardware is not possible
else
cat <<EOF
{"regId":"$1", "DeviceInfo" :{ "Firmware version": "$VERSION" , "Device": "$DEVICE", 
"RuntimeVersion": "$RUNTIME_VERSION"}
}
EOF
fi
}

schedule_agent() {
    retries=0
    if [ -f "${FILE_RETRIES}" ]; then
        retries=$(cat "${FILE_RETRIES}")
    fi

    # The first 3 retries are done every 5 minutes. After that, every hour.
    if [ "$retries" -lt 3 ]; then
        submit_cronjob "/usr/bin/flexa-agent" minutes 5 1
    else
        submit_cronjob "/usr/bin/flexa-agent" hours 1
    fi

    retries=$((retries + 1))
    echo "$retries" > "${FILE_RETRIES}"
}

# launches script that uses the date from HTTP headers
# to update date and time of the device
update_date_from_http() {
  ps aux | grep -v grep | grep get-portal-date.sh
  if [ $? -eq 1 ]; then
    /usr/bin/get-portal-date.sh >/dev/null 2>&1 &
  fi
}



# requests information from registry and stores 
# received config
#
# parameters:
#   $1: storage path for registry-config file
#   $2: regId
#   $3: registry-url

request_registry_info() {
  DATA_JSON=$(generate_data $2)
  logger "flexa-config-lib: Contacting Registry for service URL"
  for i in 1 2 3; do 
    registry_response=$( post_request "${DATA_JSON}" $1 $3 ) 
    if [ "${registry_response}" = "200" ]; then
      echo "${registry_response}"
      local extension="/GetURL"	
      service_url="$( jq -r '.url' $1 )${extension}"
      logger "flexa-config-lib: Contacting flexa-registry... Done!"
      reset_flexa_agent_config
      uci set flexa_agent.service.service_url="${service_url}"
      uci commit
      REG_DONE=1
      break
    else
      logger "flexa-config-lib: Contacting flexa-registry failed! Attempt: $i / 10"
      # 000 means a transport layer error
      # it could be a certificate error, so need to update the clock
      # by other means than NTP
      if [ "${registry_response}" == "000" ]; then
        update_date_from_http
      else
        # errors from the service (4XX) -> don't need to retry
        break
      fi
    fi
  done

  # If contacting registry failed
  if [ -z "$REG_DONE" ]; then
    schedule_agent
  fi
}

# downloads the new application package, if 
# necessary
#
# parameters:
#  $1: new_version
#  $2: package-url
#  $3: target-path to store the package
# Edit : to deal with spaces in package url ${package_url// /%20}

get_package() {
  #echo "get package!"
  logger $1 $2 $3
  old_version=$( uci get flexa_agent.service.package_version )
  #echo "version comparison: ${old_version} $1"
  if [ ${old_version} != "$1" ]; then
    logger "downloading new package from $2 ..."

    if curl --max-time 60 "${2// /%20}" -o $3; then 
      logger "downloading new package from $2 ... Done!"
      return 0
    else
      logger "downloading application package from ${package_url} TO $3 ... failed!"
      return 1
    fi
  fi
  return 2
}


# gets the application configuration from the
# service portal 
#
# parameters
#  $1: storage path for config file 
#  $2: regId
#  $3: config-url

get_config() {
    logger "Contacting flexa-service for configuration..."

    for i in 1 2 3; do
        config_response=$( post_request '{"regId":"'$2'"}' $1 $3 )
        if [ "${config_response}" = "200" ]; then
            create_sshtunnel $1
            app_param_to_uci $1
            update_rate_to_uci $1
            logger "Contacting flexa-service for configuration... Done!"
            return 0
        else
            logger "Contacting flexa-service for configuration... Failed! Attempts $i / 3"
        fi
    done

    # error condition
    return 1
}


# requests information from service and stores 
# received config, checks if there is a new 
# package and requests config
#
# parameters:
#   $1: storage path for registry-config file
#   $2: regId
#   $3: service-url

request_service_info() {
  logger "Contacting flexa-service..."
  RET=1

  for i in 1 2 3; do
    DATA_JSON=$(generate_data $2)

    service_response=$( post_request "${DATA_JSON}" $1 $3 )
    #echo -e  "\n\n Requesting service info"
    echo ${service_response}

    if [ "${service_response}" = "200" ]; then
      api_version $1
      if [ $? -eq 2 ]; then
        api_version="new"
        #uci set flexa_agent.service.api_version='new'
        #echo "NEW API ENCOUNTERED"
        package_url=$( jq -r '.packageUrl' $1 | awk '{ print $0 }' ) 
        setStatusUrl=$( jq -r '.setStatusUrl' $1 | awk '{ print $2 }' )
        methodsetStatusUrl=$( jq -r '.setStatusUrl' $1 | awk '{ print $1 }' )
        status_url=${setStatusUrl}
        uci set flexa_agent.service.status_url="${status_url}"
        getConfigUrl=$( jq -r '.getConfigUrl' $1 | awk '{ print $2 }' )
        methodgetConfigUrl=$( jq -r '.getConfigUrl' $1 | awk '{ print $1 }' )
        #echo "bla: ${getConfigUrl}"
        config_url=${getConfigUrl}
        new_version=$(cat $1 | jq -r '.version')
      else
        #echo -e "\n\n\nold_api"
        api_version="old"
        package_url=$( cat $1 | jq -r '.packageUrl' ) 
        config_url=$( cat $1 | jq -r '.configUrl' ) 
        new_version=$(cat $1 | jq -r '.version')
        #echo -e "url : ${url} \nconfig url ${config_url}"
      fi	
      #echo "getting here"
      logger "Contacting service... Done!"
      uci set flexa_agent.service.config_url="${config_url}"
      uci set flexa_agent.service.package_url="${package_url}"
      uci set flexa_agent.service.api_version="${api_version}"	
      	
      # why is it commented?
      #uci set flexa_agent.service.new_version="${new_version}"
      uci commit
      #echo " Parameters successfully saved"
      RET=0
      break
    else
      if [ "${service_response}" == "000" ]; then
        # Certificate error
        update_date_from_http
      else
        # errors from the service (4XX) -> don't need to retry
        if [ "${service_response}" == "404" ]; then
            RET=2
        fi
        break
      fi
      logger "Contacting service... Failed! Attempt $i / 3"
      echo -e "\n\nCouldn't get service info"
    fi
  done

  if [ $RET -eq 1 ]; then
    schedule_agent
  fi

  return $RET
}


# checks the config state via flexa-api
#
# parameters:
#   $1: storage path for status file
#   $2: regId
#   $3: status-url

request_status_info() {
    logger "Contacting flexa-service to check config state..."

    for i in 1 2 3; do
        if [ $( uci get flexa_agent.service.api_version ) = "new" ]; then
            status_url=$( uci -q get flexa_agent.service.status_url )
            if [ ! $status_url ]; then
                logger "Status URL is empty!"
                return 2
            fi
            logger "NEW API Status url : ${status_url}"
        else
            status_url=$3
            status_url=${status_url%/GetURL}/SetStatus	
        fi
        status_response=$( post_request '{"regId":'\"$2\"',"status":"Alive"}' $1 ${status_url} )
        logger "status response: "$status_response

        if [ "${status_response}"  = "200" ]; then
            state=$( jq -r '.config' $1 )
            logger "Contacting flexa-service to check config state... Done!"
            return 0
        elif [ "${status_response}" = "404" ]; then
            return 2
        else
            logger "Error on SetStatus request:  ${status_response}"
        fi
    done

    # if reaches here then it's an error
    return 1
}


# requests information from service and stores 
# received config, checks if there is a new 
# package and requests config. Uses the new api
#
# parameters:
#   $1: storage path for registry-config file
#   $2: regId
#   $3: service-url

contact_service() {
  logger "Contacting flexa-service..."

  for i in 1 2 3; do
    service_response=$( post_request_new_api '{"regId":"'$2'"}' $1 $3 )
    echo ${service_response}
    if [ "${registry_response}" = "200" ]; then
      package_url=$( jq -r '.packageUrl' $1 )	
      new_version=$( jq -r '.version' $1 )

      if [[ $( grep "configUrl" $1) ]]; then
        config_url=$( jq -r '.configUrl' $1 )
      else
        config_url=$( jq -r '.getConfigUrl' $1 )
      fi

      logger "Contacting service... Done!"
      uci set flexa_agent.service.config_url="${config_url}"
      uci set flexa_agent.service.package_url="${package_url}"
      #uci set flexa_agent.service.new_version="${new_version}"
      uci commit
      break
    else
      logger "Contacting service... Failed! Attempt $i / 3"
    fi
  done
}

# creates a cronjob for the flexa-agent based
# on received configration and removes old 
# cron jobs for the same application
#
# parameters:
#   $1: application name
#   $2: time units (minutes or hours)
#   $2: update rate in time units 

submit_cronjob() {
  local app_name=$1
  local time_unit=$2
  local time_value=$3
  local no_replace=$4

  # if app already exists on crontab and no_replace is set, dont do nothing
  crontab -l | grep -q $app_name
  if [ $? -eq 0 ] && [ ! -z ${no_replace} ]; then
      return
  fi

 # remove cronjobs for application $1:
  crontab -l | grep -v $app_name | crontab -

  # cron job should be executed every $2 hours
  # get the current time (only minutes)
  if [ "$time_unit" == "minutes" ]; then
    CRON_CONF="*/${time_value} * * * * PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin ${app_name}"
  elif [ "$time_unit" == "hours" ]; then
    MM=$( date "+%M" )
    CRON_CONF="${MM} */${time_value} * * * PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin ${app_name}"
  else
    echo "Wrong time unit when setting cronjob"
    return
  fi

  echo "adding cronjob: "
  echo "${CRON_CONF}"
  echo " "
  # add job to crontab. Cron job runs on a very basic environment setup, so we need to set PATH 
  # to access some commands in flexa-agent 
  crontab -l | { cat; echo "${CRON_CONF}"; } | crontab -
}


# converts json to uci-config
#
# parameters:
#  $1: json-file
#  $2: uci-module

json_to_uci() {


  n_keys=$( echo $1 | jq length )
  i="0"

  while [ $i -lt ${n_keys} ]; do
    key=$( echo $1 | jq keys[$i] | tr -d '"')
    value=$( echo $1 | jq .$key | tr -d '"')
    
    uci set "${2}.${key}"="${value}"
    
    i=$(( $i+1 ))
  done
  uci commit 

}


# passes the .AppParam part of the flexa-app config
# to json_to_uci
#
# parameters:
#  $1: json-file

app_param_to_uci() {

  input=$( jq .AppParam $1 )
  json_to_uci "${input}" "flexa_app.AppParam"

}


update_rate_to_uci() {
  update_rate=$( jq .UpdateRate $1 )
  submit_cronjob /usr/bin/flexa-agent hours ${update_rate}
  uci -q set flexa_agent.service.update_rate="${update_rate}"
  uci -q commit flexa_agent
}


create_sshtunnel() {
    ssh=$(jq -er .AppParam.sshTunnel $1)
    if [ $? -eq 0 ] && [ -n ${ssh} ]; then
        # check is something has changed
        local ssh_current=$(uci -q get flexa_app.AppParam.sshTunnel)
        if [ "${ssh_current}" != "${ssh}" ]; then
            echo "creating sshtunnel: $ssh"
            uci -q set sshtunnel.general.server=$(echo "${ssh}" | cut -d";" -f1)
            uci -q set sshtunnel.general.port=$(echo "${ssh}" | cut -d";" -f2)
            uci -q set sshtunnel.general.user=$(echo "${ssh}" | cut -d";" -f3)
            uci -q del sshtunnel.general.forward_ports
            for ports in $(echo "${ssh}" | cut -d";" -f4- | sed 's/;/ /g'); do
                uci -q add_list sshtunnel.general.forward_ports=$ports
            done
            uci -q set sshtunnel.general.key_path=$(echo "${ssh}" | cut -d";" -f5)
            uci -q set sshtunnel.general.enabled="true"
            uci -q commit sshtunnel
            /barix/hooks/application/sshtunnel.app-hook restart
        else
            echo "sshtunnel: nothing has changed"
        fi

    else
        uci -q set sshtunnel.general.enabled="false"
        /barix/hooks/application/sshtunnel stop
    fi
}


# erases the flexa-app uci-config
# and resets the file
# 
# parameters:
#   $1: path to template
#   $2: path to flexa_app config

reset_flexa_app_config() {

  cp $1 $2  

}

reset_flexa_agent_config() {
  logger "resetting the flexa-agent's uci config"
  uci -q set flexa_agent.service.service_url="changeme"
  uci -q set flexa_agent.service.config_url="changeme"
  uci -q set flexa_agent.service.package_url="changeme"
  uci -q delete flexa_agent.service.status_url
  uci -q commit flexa_agent
  set_package_version 0
}

set_package_version() {
    version="$1"
    if [ -n "$version" ]; then
        uci -q set flexa_agent.service.package_version="${version}"
        uci -q commit flexa_agent
    fi
}
