#!/bin/bash
#
# Fetches weather data from openweathermap.com

# TODO
# - support templates and populate it in one big call
# - support wind directions http://snowfence.umn.edu/Components/winddirectionanddegreeswithouttable3.htm
# - after sunset, use the night fonts instead of the day fonts
# - finish WEATHER_CONDITIONS (http://bluejamesbond.github.io/CharacterMap/)

OPENWEATHERMAP_BASE_URL="https://api.openweathermap.org/data/2.5/"

# tools required
CURL=$(which curl)
JQ=$(which jq)

if [[ -z "${CURL}" ]]; then
  echo "ERROR: curl is not installed."
  exit 1
fi

if [[ -z "${JQ}" ]]; then
  echo "ERROR: jq is not installed."
  exit 1
fi

while getopts ":a:c:d:f:i:l:q:t:u:" opt; do
  case $opt in
    a) API_KEY="${OPTARG}" ;;
    c) CACHE_DIR="${OPTARG}" ;;
    d) DATATYPE="${OPTARG}" ;;
    f) CONFIG="${OPTARG}" ;;
    i) CITY_ID="${OPTARG}" ;;
    l) LANGUAGE="${OPTARG}" ;;
    q) LOCATION="${OPTARG}" ;;
    t) TYPE="${OPTARG}" ;;
    u) UNITS="${OPTARG}" ;;
    \?) echo "Invalid option -${OPTARG}" >&2 ;;
  esac
done

if [[ -z "${CONFIG}" ]]; then
  CONFIG="config.json"
fi

if [[ -f "${CONFIG}" ]]; then
  API_KEY=${API_KEY:-$(jq -r '.api_key' "${CONFIG}")}
  CACHE_DIR=${CACHE_DIR:-$(jq -r '.cache_dir' "${CONFIG}")}
  CITY_ID=${CITY_ID:-$(jq -r '.city_id' "${CONFIG}")}
  LANGUAGE=${LANGUAGE:-$(jq -r '.language' "${CONFIG}")}
  LOCATION=${LOCATION:-$(jq -r '.location' "${CONFIG}")}
  TYPE=${TYPE:-$(jq -r '.type' "${CONFIG}")}
  UNITS=${UNITS:-$(jq -r '.units' "${CONFIG}")}
else
  echo "ERROR: config file '${CONFIG}' not found."
  exit 1
fi

function lock() {
  local lockdir="${CACHE_DIR}/refresh.lock"
  if mkdir "$lockdir"; then
    echo >&2 "successfully acquired lock"
    trap 'rm -rf "$lockdir"' 0    # remove directory when script finishes
  else
    echo >&2 "cannot acquire lock, giving up on $lockdir"
    sleep 0.5
  fi
}

function get_weather_url() {
  local url="${OPENWEATHERMAP_BASE_URL}"
  case $1 in
    current)  url+="weather" ;;
    forecast) url+="forecast" ;;
  esac

  local sep="?"
  if [[ -n "${LOCATION}" ]]; then
    url+="${sep}q=${LOCATION}"
    sep="&"
  fi
  if [[ -n "${CITY_ID}" ]]; then
    url+="${sep}id=${CITY_ID}"
    sep="&"
  fi

  url+="&APPID=${API_KEY}"
  url+="&units=${UNITS}"
  url+="&lang=${LANGUAGE}"
  url+="&type=accurate"

  echo "$url"
}

function get_cache_path() {
  case $TYPE in
    current)  echo "${CACHE_DIR}/current_weather.cache" ;;
    forecast) echo "${CACHE_DIR}/forecast_weather.cache" ;;
  esac
}

#######################################
# Checks the cache file and determines
# if it needs to be refreshed.
# Globals: none
# Returns: 0 if cache is fresh, 1 otherwise
#######################################
function cache_needs_refresh() {
  local cache_path
  cache_path=$(get_cache_path)

  # if the cache does not exist, refresh it
  if [[ ! -f "${cache_path}" ]]; then
    return 1
  fi

  local now last_mod age
  now=$(date +%s)
  case "$(uname -s)" in
    Darwin*) last_mod=$(stat -f %m "${cache_path}") ;;
    *)       last_mod=$(stat -c %Y "${cache_path}") ;;
  esac
  age=$((now - last_mod))

  if [[ "${age}" -gt 120 ]]; then
    return 1
  else
    return 0
  fi
}

function fetch_weather() {
  local url cache_path refresh tmp_file size
  cache_needs_refresh
  refresh=$?
  url=$(get_weather_url "${TYPE}")
  cache_path=$(get_cache_path)

  if [[ "${refresh}" -eq 1 ]]; then
    tmp_file="${cache_path}.$$"
    ${CURL} -s "${url}" > "${tmp_file}"
    # only update the file if we successfully retrieved the JSON
    size=$(wc -c < "${tmp_file}")
    if [[ "${size}" -gt 200 ]]; then
      mv "${tmp_file}" "${cache_path}"
    else
      rm -f "${tmp_file}"
    fi
  fi
}

function create_cache() {
  if [[ ! -d "${CACHE_DIR}" ]]; then
    mkdir -p "${CACHE_DIR}"
  fi
}

function return_field() {
  local data weather_id font tmp_data

  if [[ -z "${DATATYPE}" ]]; then
    echo "ERROR: missing datatype. Please provide it via the -d option." >&2
    return 1
  fi

  local cache_path
  cache_path=$(get_cache_path)

  case ${TYPE} in
    current)
      case ${DATATYPE} in
        all)
          weather_id=$(jq -r '.weather[0].id' "${cache_path}")
          font=$(jq -r --arg id "$weather_id" '.[$id]' ./fonts.json)
          city=$(jq -r '.name' "${cache_path}")
          temp=$(jq -r '.main.temp' "${cache_path}" | xargs printf "%.f")
          clouds=$(jq -r '.weather[0].description' "${cache_path}")
          humidity=$(jq -r '.main.humidity' "${cache_path}")
          pressure=$(jq -r '.main.pressure' "${cache_path}")
          wind_speed=$(jq -r '.wind.speed' "${cache_path}")
          wind_deg=$(jq -r '.wind.deg' "${cache_path}" | xargs printf "%.f")
          wind_dir=$(wind_deg_to_text "${wind_deg}")
          sunrise_time=$(jq -r '.sys.sunrise' "${cache_path}")
          sunset_time=$(jq -r '.sys.sunset' "${cache_path}")
          sunrise=$(date -d @"${sunrise_time}" "+%H:%M")
          sunset=$(date -d @"${sunset_time}" "+%H:%M")
          duration_seconds=$((sunset_time - sunrise_time))
          sun_hours="$((duration_seconds / 3600))h$(((duration_seconds % 3600) / 60))m"

          echo "font:${font}"
          echo "city:${city}"
          echo "temp:${temp}"
          echo "clouds:${clouds}"
          echo "humidity:${humidity}%"
          echo "pressure:${pressure}"
          echo "wind_speed:${wind_speed}"
          echo "wind_dir:${wind_dir}"
          echo "sunrise:${sunrise}"
          echo "sunset:${sunset}"
          echo "sun_hours:${sun_hours}"
          ;;
        condition)
          data=$(jq -r '.weather[] | .main' "${cache_path}" | paste -sd ',' -)
          ;;
        summary)
          data=$(jq -r '.weather[0].description' "${cache_path}")
          ;;
        temp|pressure|humidity|temp_min|temp_max)
          data=$(jq -r ".main.${DATATYPE}" "${cache_path}" | xargs printf "%.f")
          ;;
        clouds)
          data="$(jq -r '.clouds.all' "${cache_path}")%"
          ;;
        visibility)
          data=$(jq -r '.visibility' "${cache_path}")
          ;;
        wind_speed)
          data=$(jq -r '.wind.speed' "${cache_path}")
          ;;
        wind_deg)
          tmp_data=$(jq -r '.wind.deg' "${cache_path}" | xargs printf "%.f")
          data=$(wind_deg_to_text "${tmp_data}")
          ;;
        wind_deg_font)
          tmp_data=$(jq -r '.wind.deg' "${cache_path}" | xargs printf "%.f")
          data=$(wind_deg_to_font "${tmp_data}")
          ;;
        sunrise|sunset)
          tmp_data=$(jq -r ".sys.${DATATYPE}" "${cache_path}")
          data=$(date -d @"${tmp_data}" "+%H:%M")
          ;;
        sun_hours)
          sunrise_time=$(jq -r '.sys.sunrise' "${cache_path}")
          sunset_time=$(jq -r '.sys.sunset' "${cache_path}")
          duration_seconds=$((sunset_time - sunrise_time))
          hours=$((duration_seconds / 3600))
          minutes=$(((duration_seconds % 3600) / 60))
          data="${hours}h${minutes}m"
          ;;
        city)
          data=$(jq -r '.name' "${cache_path}")
          ;;
        font)
          tmp_data=$(jq -r '.weather[0].id' "${cache_path}")
          data=$(jq -r --arg id "$tmp_data" '.[$id]' ./fonts.json)
          ;;
        dt)
          data=$(jq -r '.dt' "${cache_path}")
          data=$(date -d @"${data}")
          ;;
        *)
          data="N/A"
          ;;
      esac
      ;;
    forecast)
      data=$(jq -r '.city.name' "${cache_path}")
      ;;
    *) echo "Unknown type" >&2; return 1 ;;
  esac

  echo "$data"
}

function wind_deg_to_text() {
  local deg=$1
  # N 348.75 - 11.25
  if [[ "${deg}" -ge 348 ]] || [[ "${deg}" -le 11 ]]; then
    echo "N"
  # NNE 11.25 - 33.75
  elif [[ "${deg}" -ge 12 ]] && [[ "${deg}" -lt 34 ]]; then
    echo "NNE"
  # NE 33.75 - 56.25
  elif [[ "${deg}" -ge 34 ]] && [[ "${deg}" -lt 56 ]]; then
    echo "NE"
  # ENE 56.25 - 78.75
  elif [[ "${deg}" -ge 56 ]] && [[ "${deg}" -lt 78 ]]; then
    echo "ENE"
  # E 78.75 - 101.25
  elif [[ "${deg}" -ge 79 ]] && [[ "${deg}" -lt 101 ]]; then
    echo "E"
  # ESE 101.25 - 123.75
  elif [[ "${deg}" -ge 101 ]] && [[ "${deg}" -lt 123 ]]; then
    echo "ESE"
  # SE 123.75 - 146.25
  elif [[ "${deg}" -ge 124 ]] && [[ "${deg}" -lt 146 ]]; then
    echo "SE"
  # SSE 146.25 - 168.75
  elif [[ "${deg}" -ge 147 ]] && [[ "${deg}" -lt 168 ]]; then
    echo "SSE"
  # S 168.75 - 191.25
  elif [[ "${deg}" -ge 169 ]] && [[ "${deg}" -lt 191 ]]; then
    echo "S"
  # SSW 191.25 - 213.75
  elif [[ "${deg}" -ge 192 ]] && [[ "${deg}" -lt 213 ]]; then
    echo "SSW"
  # SW 213.75 - 236.25
  elif [[ "${deg}" -ge 214 ]] && [[ "${deg}" -lt 236 ]]; then
    echo "SW"
  # WSW 236.25 - 258.75
  elif [[ "${deg}" -ge 237 ]] && [[ "${deg}" -lt 258 ]]; then
    echo "WSW"
  # W 258.75 - 281.25
  elif [[ "${deg}" -ge 259 ]] && [[ "${deg}" -lt 281 ]]; then
    echo "W"
  # WNW 281.25 - 303.75
  elif [[ "${deg}" -ge 282 ]] && [[ "${deg}" -lt 303 ]]; then
    echo "WNW"
  # NW 303.75 - 326.25
  elif [[ "${deg}" -ge 304 ]] && [[ "${deg}" -lt 326 ]]; then
    echo "NW"
  # NNW 326.25 - 348.75
  elif [[ "${deg}" -ge 326 ]] && [[ "${deg}" -lt 348 ]]; then
    echo "NNW"
  else
    echo "N/A"
  fi
}

function wind_deg_to_font() {
  local deg=$1
  local text
  text=$(wind_deg_to_text "${deg}")
  jq -r --arg dir "$text" '.[$dir]' ./wind_direction.json
}

if [[ -z "${API_KEY}" ]]; then
  echo "ERROR: missing api_key. Please provide it via -a myapikey option"
  echo "ERROR: Get a free api_key from https://home.openweathermap.org/api_keys"
  exit 1
fi

if [[ -z "${LOCATION}" ]] && [[ -z "${CITY_ID}" ]]; then
  echo "ERROR: location (-q) or city id (-i) must be given." >&2
  exit 1
fi

create_cache
fetch_weather
return_field
