#!/usr/bin/env bash

# Sanitize functions to prepare stats values for backend

TEMP_MIN=-100
TEMP_MAX=200
TEMP_FALLBACK_MIN=0
TEMP_FALLBACK_MAX=0

OCTOFAN_TEMP_MIN=-100
OCTOFAN_TEMP_MAX=255
OCTOFAN_VOLTAGE_AC_MIN=0
OCTOFAN_VOLTAGE_AC_MAX=400
OCTOFAN_AMPERAGE_AC_MIN=0
OCTOFAN_AMPERAGE_AC_MAX=22
OCTOFAN_POWER_AC_MIN=0
OCTOFAN_POWER_AC_MAX=5000
OCTOFAN_VOLTAGE_DC_MIN=0
OCTOFAN_VOLTAGE_DC_MAX=40
OCTOFAN_AMPERAGE_DC_MIN=0
OCTOFAN_AMPERAGE_DC_MAX=420
OCTOFAN_POWER_DC_MIN=0
OCTOFAN_POWER_DC_MAX=5000
OCTOFAN_ID_MIN=0
OCTOFAN_ID_MAX=9
OCTOFAN_BME_HUMID_MIN=0
OCTOFAN_BME_HUMID_MAX=100
OCTOFAN_BME_PRESS_MIN=0
OCTOFAN_BME_PRESS_MAX=2000
OCTOFAN_TEMP_FALLBACK_MIN=0
OCTOFAN_TEMP_FALLBACK_MAX=0
OCTOFAN_VOLTAGE_AC_FALLBACK_MIN=0
OCTOFAN_VOLTAGE_AC_FALLBACK_MAX=0
OCTOFAN_AMPERAGE_AC_FALLBACK_MIN=0
OCTOFAN_AMPERAGE_AC_FALLBACK_MAX=0
OCTOFAN_POWER_AC_FALLBACK_MIN=0
OCTOFAN_POWER_AC_FALLBACK_MAX=0
OCTOFAN_VOLTAGE_DC_FALLBACK_MIN=0
OCTOFAN_VOLTAGE_DC_FALLBACK_MAX=0
OCTOFAN_AMPERAGE_DC_FALLBACK_MIN=0
OCTOFAN_AMPERAGE_DC_FALLBACK_MAX=0
OCTOFAN_POWER_DC_FALLBACK_MIN=0
OCTOFAN_POWER_DC_FALLBACK_MAX=0
OCTOFAN_ID_FALLBACK_MIN=null
OCTOFAN_ID_FALLBACK_MAX=null
OCTOFAN_BME_HUMID_FALLBACK_MIN=0
OCTOFAN_BME_HUMID_FALLBACK_MAX=0
OCTOFAN_BME_PRESS_FALLBACK_MIN=0
OCTOFAN_BME_PRESS_FALLBACK_MAX=0
OCTOFAN_MODEL_LENGTH=10

FAN_MIN=0
FAN_MAX=100
FAN_FALLBACK_MIN=0
FAN_FALLBACK_MAX=100

POWER_MIN=0
POWER_MAX=1500 # B200 can do over 1kW
POWER_FALLBACK_MIN=0
POWER_FALLBACK_MAX=0

HASHRATE_MIN=0
HASHRATE_MAX=9999999999999
HASHRATE_FALLBACK_MIN=0
HASHRATE_FALLBACK_MAX=999999999999

MINER_VER_LENGTH=50

# function log() {
#   echo $@ >> san.log
# }

function sanitize_value() {
  local v=`echo "$1" | awk '{printf "%.8f", $1}'`
  if (( $(echo "$v < $2" | bc -l) )); then
    echo "$4"
  elif (( $(echo "$v > $3" |bc -l) ));then
    echo "$5"
  else
    echo $v
  fi
}

sanitize_hash_value() {
  #local v=`echo "$1" | awk '{ printf "%.8f", $1 }'`
  # round to 5 significant digits
  local v=`echo "$1" | awk '{if ($1 >= 1e5 || $1 == 0) printf "%.0f",$1; else if ($1 < 1e-4) printf "%.8f",$1; else printf "%.5g",int($1*1e8)/1e8;}'`
  if (( $(echo "$v * $2 < $3" | bc -l) )); then
    echo "$5 * $2" | bc
  elif (( $(echo "$v * $2 > $4" | bc -l) )); then
    echo "$6 * $2" | bc
  else
    echo "$v"
  fi
}

function sanitize_temp() {
  local s_values=
  local a_values
  local i_value
  readarray -t a_values < <( echo "$@" | jq -rc '.[ ]' )
  for i_value in "${a_values[@]}"; do
    [[ -z "$i_value" || "$i_value" == "null" ]] &&
      s_values+=${s_values:+,}"null" ||
      s_values+=${s_values:+,}`sanitize_value "$i_value" $TEMP_MIN $TEMP_MAX $TEMP_FALLBACK_MIN $TEMP_FALLBACK_MAX`
  done
  echo "[$s_values]"
}

function sanitize_fan() {
  local s_values=
  local a_values
  local i_value
  readarray -t a_values < <( echo "$@" | jq -rc '.[ ]' )
  for i_value in "${a_values[@]}"; do
    [[ -z "$i_value" || "$i_value" == "null" ]] &&
      s_values+=${s_values:+,}"null" ||
      s_values+=${s_values:+,}`sanitize_value "$i_value" $FAN_MIN $FAN_MAX $FAN_FALLBACK_MIN $FAN_FALLBACK_MAX`
  done
  echo "[$s_values]"
}

function sanitize_power() {
  local s_values=
  local a_values
  local i_value
  readarray -t a_values < <( echo "$@" | jq -rc '.[ ]' )
  for i_value in "${a_values[@]}"; do
    [[ -z "$i_value" || "$i_value" == "null" ]] &&
      s_values+=${s_values:+,}"null" ||
      s_values+=${s_values:+,}`sanitize_value "$i_value" $POWER_MIN $POWER_MAX $POWER_FALLBACK_MIN $POWER_FALLBACK_MAX`
  done
  echo "[$s_values]"
}

function sanitize_bus_number() {
  local s_values=
  local a_values
  local i_value
  readarray -t a_values < <( echo "$@" | jq -rc '.[ ]' )
  for i_value in "${a_values[@]}"; do
    if [[ -z "$i_value" || "$i_value" == "null" ]]; then
      s_values+=${s_values:+,}"null"
    elif [[ "$i_value" -lt 0 ]]; then
      s_values+=${s_values:+,}$((256 + "$i_value"))
    else
      s_values+=${s_values:+,}"$i_value"
    fi
  done
  echo "[$s_values]"
}

function sanitize_octofan_temp() {
  local i_value
  local s_values=
  for i_value in `echo "$@" | jq -rc '.[ ]'`; do
    s_values+=${s_values:+,}`sanitize_value $(echo "$i_value" | awk '{ printf "%.0f", $1 }') $OCTOFAN_TEMP_MIN $OCTOFAN_TEMP_MAX $OCTOFAN_TEMP_FALLBACK_MIN $OCTOFAN_TEMP_FALLBACK_MAX`
  done
  echo "[$s_values]"
}

function sanitize_octofan_voltage_ac() {
  sanitize_value "$1" $OCTOFAN_VOLTAGE_AC_MIN $OCTOFAN_VOLTAGE_AC_MAX $OCTOFAN_VOLTAGE_AC_FALLBACK_MIN $OCTOFAN_VOLTAGE_AC_FALLBACK_MAX
}

function sanitize_octofan_amperage_ac() {
  sanitize_value "$1" $OCTOFAN_AMPERAGE_AC_MIN $OCTOFAN_AMPERAGE_AC_MAX $OCTOFAN_AMPERAGE_AC_FALLBACK_MIN $OCTOFAN_AMPERAGE_AC_FALLBACK_MAX
}

function sanitize_octofan_power_ac() {
  sanitize_value "$1" $OCTOFAN_POWER_AC_MIN $OCTOFAN_POWER_AC_MAX $OCTOFAN_POWER_AC_FALLBACK_MIN $OCTOFAN_POWER_AC_FALLBACK_MAX
}

function sanitize_octofan_voltage_dc() {
  sanitize_value "$1" $OCTOFAN_VOLTAGE_DC_MIN $OCTOFAN_VOLTAGE_DC_MAX $OCTOFAN_VOLTAGE_DC_FALLBACK_MIN $OCTOFAN_VOLTAGE_DC_FALLBACK_MAX
}

function sanitize_octofan_amperage_dc() {
  sanitize_value "$1" $OCTOFAN_AMPERAGE_DC_MIN $OCTOFAN_AMPERAGE_DC_MAX $OCTOFAN_AMPERAGE_DC_FALLBACK_MIN $OCTOFAN_AMPERAGE_DC_FALLBACK_MAX
}

function sanitize_octofan_power_dc() {
  sanitize_value "$1" $OCTOFAN_POWER_DC_MIN $OCTOFAN_POWER_DC_MAX $OCTOFAN_POWER_DC_FALLBACK_MIN $OCTOFAN_POWER_DC_FALLBACK_MAX
}

function sanitize_octofan_id() {
  sanitize_value "$1" $OCTOFAN_ID_MIN $OCTOFAN_ID_MAX $OCTOFAN_ID_FALLBACK_MIN $OCTOFAN_ID_FALLBACK_MAX
}

function sanitize_octofan_humid() {
  sanitize_value "$1" $OCTOFAN_BME_HUMID_MIN $OCTOFAN_BME_HUMID_MAX $OCTOFAN_BME_HUMID_FALLBACK_MIN $OCTOFAN_BME_HUMID_FALLBACK_MAX
}

function sanitize_octofan_press() {
  sanitize_value "$1" $OCTOFAN_BME_PRESS_MIN $OCTOFAN_BME_PRESS_MAX $OCTOFAN_BME_PRESS_FALLBACK_MIN $OCTOFAN_BME_PRESS_FALLBACK_MAX
}

function sanitize_octofan_power_units() {
  local result_json=

  while IFS= read  id &&
        IFS= read  model &&
        IFS= read  power_ac &&
        IFS= read  voltage_ac &&
        IFS= read  voltage_dc
  do
    id=`sanitize_octofan_id $id`
    [[ $model != 'null' ]] && model=\"${model:0:$OCTOFAN_MODEL_LENGTH}\"
    power_ac=`sanitize_octofan_power_ac $power_ac`
    voltage_ac=`sanitize_octofan_voltage_ac $voltage_ac`
    voltage_dc=`sanitize_octofan_voltage_dc $voltage_dc`

    result_json+=${result_json:+,}'{"id": '$id',
                                  "model": '$model',
                                  "power_ac": '$power_ac',
                                  "voltage_ac": '$voltage_ac',
                                  "voltage_dc": '$voltage_dc'}'
  done < <(jq -r '.[] | (.id, .model, .power_ac, .voltage_ac, .voltage_dc)' <<< "$@")

  echo "[$result_json]"
}

function sanitize_octofan_bme() {
  local result_json

  while IFS= read  id &&
        IFS= read  model &&
        IFS= read  humid &&
        IFS= read  press
  do
    id=`sanitize_octofan_id $id`
    [[ $model != 'null' ]] && model=\"${model:0:$OCTOFAN_MODEL_LENGTH}\"
    humid=`sanitize_octofan_humid $humid`
    press=`sanitize_octofan_press $press`

    result_json+=${result_json:+,}'{"id": '$id',
                                  "model": '$model',
                                  "humid": '$humid',
                                  "press": '$press'}'
  done < <(jq -r '.[] | (.id, .model, .humid, .press)' <<< "$@")

  echo "[$result_json]"
}

get_hs_multiplier() {
  case $1 in
    "hs" )
      echo 0.001
      ;;
    "mhs" )
      echo 1000
      ;;
    "ghs" )
      echo 1000000
      ;;
    * )
      echo 1
  esac
}

function sanitize_hashrate() {
  local s_values=
  local hs_multiplier=`get_hs_multiplier "$2"`
  for i_value in `echo "$1" | jq -rc '.[ ]'`; do
    s_values+=${s_values:+,}`sanitize_hash_value "$i_value" $hs_multiplier $HASHRATE_MIN $HASHRATE_MAX $HASHRATE_FALLBACK_MIN $HASHRATE_FALLBACK_MAX`
  done
  echo "[$s_values]"
}

function sanitize_total_hashrate() {
  sanitize_hash_value "$1" 1 $HASHRATE_MIN $HASHRATE_MAX $HASHRATE_FALLBACK_MIN $HASHRATE_FALLBACK_MAX
}

function sanitize_ar() {
  local i_value
  local s_values=
  local i=0
  for i_value in `echo "$@" | jq -rc '.[ ]'`; do
    [[ i -ne 3 ]] && s_values+=${s_values:+,}"$i_value" || s_values+=${s_values:+,}"\"$i_value\""
    ((i++))
  done
  echo "[$s_values]"
}

function sanitize_shares_gpu() {
  local i_value
  local s_values=
  local i=0
  for i_value in `echo "$@" | jq -rc '.[ ]'`; do
    s_values+=${s_values:+,}"$i_value"
    ((i++))
  done
  echo "[$s_values]"
}

function sanitize_shares() {
  sanitize_value "$1" 0 2147483647 0 0
}

function sanitize_ver() {
  echo "\"${1:0:$MINER_VER_LENGTH}\""
}

# globals: $stats, $value
# json_value ".jq_selector" "add_stats_value_name" "sanitize_func"
function json_value() {
  value=`echo "$stats" | jq -rc "$1"`
  #echo "$1: $value" >&2
  if [[ $? -eq 0 && ! -z "$value" && "$value" != '[]' && "$value" != 'null' ]]; then
    [[ ! -z "$3" ]] && value=`$3 "$value"`
    [[ ! -z "$2" ]] && stats=`echo "$stats {\"$2\": $value}" | jq -sc '.[0] * .[1]'`
    return 0
  fi
  value=
  return 1
}

function sanitize_miner_stats() {
  local stats="$@"
  local value

  json_value ".shares"
  if [[ $? -eq 0 ]]; then
    # process subarray
    stats="$value"
    value=

    json_value ".a" "a" "sanitize_shares"
    json_value ".r" "r" "sanitize_shares"
    json_value ".i" "i" "sanitize_shares"
    json_value ".latency" "latency"
    json_value ".ag" "ag" "sanitize_shares_gpu"
    json_value ".rg" "rg" "sanitize_shares_gpu"
    json_value ".ig" "ig" "sanitize_shares_gpu"

    # merge to main array
    stats=`echo "$@ {\"shares\": $stats}" | jq -sc '.[0] * .[1]'`
  fi

  json_value ".hs_units" &&
    hs_units="$value" ||
    hs_units="khs"

  json_value ".hs" "hs" "sanitize_hashrate" "$hs_units"
  json_value ".total_khs" "total_khs" "sanitize_total_hashrate"
  json_value ".temp" "temp" "sanitize_temp"
  json_value ".fan" "fan" "sanitize_fan"
  json_value ".uptime" "uptime"
  json_value ".ar" "ar" "sanitize_ar"
  json_value ".ver" "ver" "sanitize_ver"
  json_value ".bus_numbers" "bus_numbers" "sanitize_bus_number"

  #sanitize dual stats
  json_value ".shares2"
  if [[ $? -eq 0 ]]; then
    # process subarray
    stats="$value"
    value=

    json_value ".a" "a" "sanitize_shares"
    json_value ".r" "r" "sanitize_shares"
    json_value ".i" "i" "sanitize_shares"
    json_value ".latency" "latency"
    json_value ".ag" "ag" "sanitize_shares_gpu"
    json_value ".rg" "rg" "sanitize_shares_gpu"
    json_value ".ig" "ig" "sanitize_shares_gpu"

    # merge to main array
    stats=`echo "$@ {\"shares\": $stats}" | jq -sc '.[0] * .[1]'`
  fi

  json_value ".hs_units2" &&
    hs_units="$value" ||
    hs_units="khs"

  json_value ".hs2" "hs2" "sanitize_hashrate" "$hs_units"
  json_value ".total_khs2" "total_khs2" "sanitize_total_hashrate"
  json_value ".temp2" "temp2" "sanitize_temp"
  json_value ".fan2" "fan2" "sanitize_fan"
  json_value ".uptime2" "uptime"
  json_value ".ar2" "ar2" "sanitize_ar"
  json_value ".ver2" "ver2" "sanitize_ver"

  echo "$stats"
}

function sanitize_fan_controller_stats() {
  local stats="$@"
  local value

  json_value ".psu"
  if [[ $? -eq 0 ]]; then
    # process subarray
    stats="$value"
    value=

    json_value ".units" "units" "sanitize_octofan_power_units"

    json_value ".voltage_ac" "voltage_ac" "sanitize_octofan_voltage_ac"
    json_value ".amperage_ac" "amperage_ac" "sanitize_octofan_amperage_ac"
    json_value ".power_ac" "power_ac" "sanitize_octofan_power_ac"
    json_value ".voltage_dc" "voltage_dc" "sanitize_octofan_voltage_dc"
    json_value ".amperage_dc" "amperage_dc" "sanitize_octofan_amperage_dc"
    json_value ".power_dc" "power_dc" "sanitize_octofan_power_dc"

    # merge to main array
    stats=`echo "$@ {\"psu\": $stats}" | jq -sc '.[0] * .[1]'`
  fi

  json_value ".thermosensors" "thermosensors" "sanitize_octofan_temp"
  json_value ".casefan" "casefan" "sanitize_fan"
  json_value ".bme" "bme" "sanitize_octofan_bme"

  echo "$stats"
}

#sanitize_total_hashrate $1
#sanitize_miner_stats $@
#sanitize_miner_stats '{"hs":[8.5,7.4],"hs_units":"hs","temp":["48","53"],"fan":[80,80],"uptime":150399,"ar":["2151","0"],"bus_numbers":[1,2],"algo":"150_5","ver":"1.42"}'
#sanitize_miner_stats '{"hs":[8.5,7.4],"hs_units":"hs","temp":["48","53"],"fan":[80,80],"uptime":150399,"bus_numbers":[1,2],"algo":"150_5","ver":"1.42","shares":{"a":"3219","r":11,"i":10,"ag":[3000,219],"rg":[10,1],"ig":[8,2],"latency":"32"}}'
#sanitize_miner_stats '{ "total_khs": "0.143", "hs": [ 0.0715, 0.0715 ], "algo": "randomhash", "temp": [ 44, 44 ], "uptime": 11204, "ar": [ 23, 0, 0 ], "ver": "1.5.1-oldgencpu" }'
#sanitize_fan_controller_stats '{ "casefan": [ 100, 100, 100 ], "thermosensors": [ 16, 24, 1.34648e+2, 849.5 ], "psu": { "power_ac": "17245.0", "voltage_ac": "227.4", "voltage_dc": "12.28" } }'
#sanitize_fan_controller_stats '{ "casefan": [ 100, 1.34648e+2, 100 ], "thermosensors": [ 16, 24, 1.34648e+2, 849.5 ], "psu": { "power_ac": "17245.0", "voltage_ac": "227.4"} }'
#[ "12.0 - ETH", "2", "29858;3;0", "14779;15078", "49763;0;0", "24632;25131", "48;100;56;100", "eu-eth.hiveon.net:4444;dcr.coinmine.pl:2222", "0;0;0;0", "1;2", "0;0", "0;0", "0;0", "0;0", "0;0", "1;2", "sia" ]
#{ "total_khs": 26991, "hs": [ 13467, 13524 ], "temp": [ "47", "55" ], "fan": [ "101", "98" ], "uptime": 60, "algo": "ethash", "ar": [ "0", "0", "0", "0;0" ], "ver": "12.0 - ETH", "bus_numbers": [ 1, 2 ], "total_khs2": 44985, "hs2": [ 22445, 22540 ], "algo2": "dcr", "ar2": [ "0", "0", "0", "0;0" ] }
#{"total_khs":26757,"hs":[13245,13511],"temp":[45,53],"fan":[98,100],"uptime":79260,"algo":"ethash","ar":[520,0,0,"0;0"],"ver":"12.0 - ETH","bus_numbers":[1,2],"total_khs2":44594,"hs2":[22075,22519],"algo2":"dcr","ar2":["75","1","0","0;0"]}
#sanitize_fan_controller_stats '{"casefan":[106,96,94,100,108],"thermosensors":[28.67,28.71,38.03,46.53],"psu":{"units":[{"id":0,"model": "DPS1200","power_ac":59,"voltage_ac":233.6,"voltage_dc":12.23},{"id":11,"model": "DPS120013123213","power_ac":59,"voltage_ac":236.2,"voltage_dc":12.23},{"id":2,"power_ac":96,"voltage_ac":233.6,"voltage_dc":12.19}],"power_ac":214,"voltage_ac":234.4,"voltage_dc":12.2},"bme":[{"id": 0, "model": "BME280", "humid": 26.54, "press": 1014.12}, {"id": 1, "model": "BME280", "humid": 28.72, "press": 1013.49}]}'
#sanitize_miner_stats '{"hs":[8.5,7.4],"hs_units":"hs","temp":["48","53"],"fan":[80,80],"uptime":150399,"ar":["2151","0"],"bus_numbers":[-125,2],"algo":"150_5","ver":"1.42"}'
#sanitize_miner_stats '{"hs":[8.5,7.4],"hs_units":"hs","temp":["48","53"],"fan":[80,80],"uptime":150399,"ar":["2151","0"],"bus_numbers":[1,-29],"algo":"150_5","ver":"1.42"}'