#!/bin/bash

# client version, calls server for actual API info
usage() {
    cat <<EOF
usage: $0 [-a KEY][-s QUERY | -z ZIPCODE][-u <c|f>][-p][-i][-l][-h]

Queries OpenWeatherMapAPI for weather or lat/long
options:
  -a, --apikey      set the api key to use to KEY
  -l, --location    print the location being used
  -p, --pretty      enables nerdfont symbols
  -d, --desc        prints the conditions text as well
  -z, --zipcode     returns the weather for ZIPCODE
  -s, --search      returns the weather for QUERY
  -u, --units       sets the temperature units
  -i, --icon_test   tests the icons used
  -r, --radar       displays the radar for a given location
  -f, --forecast    displays the forecast a given location
  -h, --help        show this message

ZIPCODE sets the location to the given zipcode
to narrow results, include the ISO country code
ex) -z "02139,US"

QUERY sets the location to the search
QUERY format is "CITY,REGION,COUNTRY" where
EOF
exit $1
}

WORKING_DIR="$HOME/.local/share/weather"
WEATHER_FILE=".env"

TIMEOUT=60 # timeout for info
TIME=$(date +%s)


if [[ ! -d "$WORKING_DIR" ]] ; then
    mkdir -p "$WORKING_DIR"
fi

# going to weather directory
cd "$WORKING_DIR"

print_weather() {
    # prints the weather
    
    # check to see if it is expired
    check_expiration

    # checking if description enabled
    if [[ "$DESCRIPTION" = true ]] ; then
        DESC=$(echo "$CONDITIONS" | awk '{for(i=1;i<=NF;i++){ $i=toupper(substr($i,1,1)) substr($i,2) }}1')
        DESC=$(printf '%s ' "$DESC")
    fi

    if [[ "$PRETTY" = true ]] ; then
        # print with NF icons
        icon=$(get_icon "$WEATHER_ICON")
        humidity_icon=$(echo -e '\ue373')

        # checking units
        if [[ "$UNIT" == "C" ]] ; then
            temp_icon=$(echo -e '\ufa03')
        else
            temp_icon=$(echo -e '\ufa04')
        fi
    else
        # default printing
        humidity_icon="% RH"
        if [[ "$UNIT" == "C" ]] ; then
            temp_icon="*C"
        else
            temp_icon="*F"
        fi
    fi

    # printing
    printf '%s%s %.1f%s %d%s in %s\n' "$DESC" "$icon" $TEMPERATURE "$temp_icon" $HUMIDITY "$humidity_icon" "$CITY"

    # save before exiting
    save_info
}

print_location() {
    # prints the location being used for the weather

    # uses reverse geocoding
    LOCATION_URL="https://api.openweathermap.org/geo/1.0/reverse?lat=$LAT&lon=$LON&appid=$API_KEY"
    LOCATION=$(curl --silent -fL "$LOCATION_URL" | gojq '.[0] | del(.local_names)')

    name=$(echo "$LOCATION" | gojq '.name' | tr -d '"')
    state=$(echo "$LOCATION" | gojq '.state' | tr -d '"')
    country=$(echo "$LOCATION" | gojq '.country' | tr -d '"')

    printf 'Location is set to %s in %s, %s\n' "$name" "$state" "$country"
}

check_expiration() {
    # checks if the info is expired
    if [[ $TIME -gt $EXPIRATION ]] ; then
        # expired
        get_weather 
    fi
}

get_location() {
    # searching for user location
    if [[ -n "$SEARCH" ]] ; then
        # search
        LOCATION_URL="https://api.openweathermap.org/geo/1.0/$SEARCH&appid=$API_KEY&limit=5"
        RESULTS=$(curl --silent -fL "$LOCATION_URL")
        # check if its an array
        is_array=$(echo "$RESULTS" | gojq 'if type=="array" then 1 else 0 end')

        if [[ $is_array -eq 1 ]] ; then
            # returned result is an array
            
            NUM_RESULTS=$(echo "$RESULTS" | gojq '. | length')
            if [[ $NUM_RESULTS -gt 1 ]] ; then
                # provide menu for multiple results
                for ((i=0; i < $NUM_RESULTS; i++)); do
                    # adding options
                    res=$(echo "$RESULTS" | gojq ".[$i]")
                    name=$(echo "$res" | gojq '.name' | tr -d '"')
                    state=$(echo "$res" | gojq '.state' | tr -d '"')
                    country=$(echo "$res" | gojq '.country' | tr -d '"')
                    opt=$(printf '%s %s, %s' "$name" "$state" "$country")
                    OPTIONS+=($(($i+1)) "$opt")
                done

            # executing dialog menu
                exec 3>&1
                SELECTION=$(dialog \
                    --title 'Multiple Locations Found!' \
                    --clear \
                    --cancel-label 'Exit' \
                    --ok-label 'Select' \
                    --menu 'Please select the desired location. If none of the options look correct, try making refining your search by including a country code and/or region' 0 0 4 \
                    "${OPTIONS[@]}" \
                    2>&1 1>&3)
                exit_status=$?
                exec 3>&-
                clear

                if [[ $exit_status -eq 1 || $exit_satus -eq 255 ]] ; then
                    # canceled or escaped
                    echo "Location not changed"
                    exit 0
                fi

                # fix index
                SELECTION=$(($SELECTION - 1))
            fi
            # updating result
            RESULTS=$(echo "$RESULTS" | gojq ".[$SELECTION]")
        fi

        # Update info
        LAT=$(echo "$RESULTS" | gojq ".lat")
        LON=$(echo "$RESULTS" | gojq ".lon")
        CITY=$(echo "$RESULTS" | gojq ".name" | tr -d '"')
    else 
        # no search, default to user IP
        
        url="http://ip-api.com/csv/?fields=252"
        res=$(curl --silent -fL "$url")
        LAT=$(awk -F , '{print $5}' <<<"$res")
        LON=$(awk -F , '{print $6}' <<<"$res")
        CITY=$(awk -F , '{print $3}' <<<"$res")
    fi

    # check 
    if [[ -z "$LAT" || -z "$LON" ]] ; then
        echo "No location found!" >&2
        exit 1
    fi

    # force refresh
    EXPIRATION=0
}

get_weather() {
    # calls api for weather based on $LAT, $LONG
    
    if [[ -z "$LAT" || -z "$LON" ]] ; then
        # no lat or lon
        get_location
    fi

    if [[ "$UNIT" =~ ^[Cc]$ ]] ; then
        # units set to metric
        UNIT="C"
        UNITS="metric"
    elif [[ "$UNIT" =~ ^[Ff]$ || -z "$UNIT" ]] ; then
        # imperial (default)
        UNIT="F"
        UNITS="imperial"
    else
        printf 'Unit %s unrecognized\n' "$UNIT" >&2
        exit 1
    fi

    WEATHER_URL="https://api.openweathermap.org/data/2.5/weather?lat=$LAT&lon=$LON&appid=$API_KEY&units=$UNITS"

    WEATHER=$(curl --silent -fL "$WEATHER_URL")

    CONDITIONS=$(echo $WEATHER | gojq -r '.weather[0].description')
    TEMPERATURE=$(echo $WEATHER | gojq -r '.main.temp')
    HUMIDITY=$(echo $WEATHER | gojq -r '.main.humidity')
    WEATHER_ICON=$(echo $WEATHER | gojq -r '.weather[0].icon')
    EXPIRATION=$(($TIME+$TIMEOUT))
}

update_key() {
    # updates the API key used

    printf 'Testing API key %s... ' "$API_KEY"
    WEATHER_URL="https://api.openweathermap.org/data/2.5/weather?lat=42.3736&lon=71.1097&appid=$API_KEY"
    CODE=$(curl --silent "$WEATHER_URL" | gojq '.cod' | tr -d '"')

    if [[ $CODE -eq 401 ]] ; then
        # API_KEY error
        printf 'Error: Invalid API Key "%s"!\n' "$API_KEY" >&2
        exit 1
    fi
    printf 'Success: Key Updated!\n'

    # saving new key
    save_info
}

save_info() {
    # saves set env vars to file
    printf '%s="%s"\n' \
"API_KEY" "$API_KEY" \
"PARSER" "$PARSER" \
"LAT" "$LAT" \
"LON" "$LON" \
"CITY" "$CITY" \
"TEMPERATURE" "$TEMPERATURE" \
"HUMIDITY" "$HUMIDITY" \
"UNIT" "$UNIT" \
"WEATHER_ICON" "$WEATHER_ICON" \
"CONDITIONS" "$CONDITIONS" \
"EXPIRATION" "$EXPIRATION" > "$WEATHER_FILE"
}

load_info() {
    # loads env vars
    if [[ ! -e "$WEATHER_FILE" ]] ; then
        # generates blank info on fresh installs
        
        # test for gojq
        PARSER=gojq
        if ! command -v $PARSER ; then
            # test for jq
            PARSER=jq
            if ! command -v $PARSER ; then
                echo "$1 depends on jq or gojq"
                exit 1
            fi
        fi
        save_info
    fi
    source $WEATHER_FILE

}

display_forecast() {
    # two step process, gets weather station from NWS, then mpv to play radar
    if [[ -z "$LAT" || -z "$LON" ]] ; then
        echo "Location not found!"
        exit 1
    fi
    echo "Loading weather..."
    if [[ -n "$PRETTY" ]] ; then
        # nerdfont
        curl -fsSL "https://v2d.wttr.in/${LAT},${LON}?F" | less -R
    else
        curl -fsSL "https://wttr.in/${LAT},${LON}?F" | less -R
    fi

    exit 0
}

display_radar() {
    # two step process, gets weather station from NWS, then mpv to play radar
    if [[ -z "$LAT" || -z "$LON" ]] ; then
        echo "Location not found!"
        exit 1
    fi
    # grabbing nws station
    NWS=$(curl -fsSL "https://api.weather.gov/points/$LAT,$LON")
    STATION=$(echo "$NWS" | gojq '.properties.radarStation' | tr -d '"')

    # validation
    if [[ -z "$STATION" ]] ; then
        echo "NWS Station error!"
        exit 1
    fi
    # mpv to play weather in fullscreen
    echo "Fetching weather for NWS Station $STATION..."
    mpv --fs --loop-file "https://radar.weather.gov/ridge/standard/${STATION}_loop.gif" >/dev/null 2>&1

    exit 0
}

get_icon() {
    # sets NF symbols
    tod=$(echo "$1" | sed --expression='s/[0-9]//g')
    conditions=$(echo "$1" | sed --expression='s/[^0-9]//g')

    # getting icon
    if [[ "$tod" == "d" ]] ; then
        # day icons
        case "$conditions" in
            "01") echo -e '\ue30d' ;; # clear
            "02") echo -e '\ue30c' ;; # scattered clouds
            "03") echo -e '\ue302' ;; # broken clouds
            "04") echo -e '\ue312' ;; # cloudy
            "09") echo -e '\ue309' ;; # showers
            "10") echo -e '\ue308' ;; # rain
            "11") echo -e '\ue30f' ;; # thunderstorm
            "13") echo -e '\uf2dc' ;; # snow
            "50") echo -e '\ue303' ;; # mist
            *   ) echo -e '\ue374' ;; # unknown
        esac
    elif [[ "$tod" == "n" ]] ; then
        # night icons
        case "$conditions" in
            "01") echo -e '\ue32b' ;; # clear
            "02") echo -e '\ue379' ;; # scattered clouds
            "03") echo -e '\ue37e' ;; # broken clouds
            "04") echo -e '\ue312' ;; # cloudy
            "09") echo -e '\ue326' ;; # showers
            "10") echo -e '\ue325' ;; # rain
            "11") echo -e '\ue32a' ;; # thunderstorm
            "13") echo -e '\uf2dc' ;; # snow
            "50") echo -e '\ue346' ;; # mist
            *   ) echo -e '\ue374' ;; # unknown
        esac
    else
        echo "TOD not recognized"
        exit 1
    fi
}

icon_test() {
    #tests icons
    printf 'Testing weather icons\nIf any look broken, check that NerdFont is installed\n'

    printf '\nDay:\n'
    printf 'Clear:          \ue30d\n'
    printf 'Partly Cloudy:  \ue30c\n'
    printf 'Cloudy:         \ue302\n'
    printf 'Very Cloudy:    \ue312\n'
    printf 'Showers:        \ue309\n'
    printf 'Rain:           \ue308\n'
    printf 'Thunderstorm:   \ue30f\n'
    printf 'Snow:           \uf2dc\n'
    printf 'Fog:            \ue303\n'
    # night icons
    printf '\nNight:\n'
    printf 'Clear:          \ue32b\n'
    printf 'Partly Cloudy:  \ue379\n'
    printf 'Cloudy:         \ue37e\n'
    printf 'Very Cloudy:    \ue312\n'
    printf 'Showers:        \uf2dc\n'
    printf 'Rain:           \ue325\n'
    printf 'Thunderstorm:   \ue32a\n'
    printf 'Snow:           \uf2dc\n'
    printf 'Fog:            \ue346\n'

    printf '\nAssorted:\n'
    printf 'Degrees (F)     \ufa04\n'
    printf 'Degrees (C)     \ufa03\n'
    printf '%% Humidity      \ue373\n'
    exit 0
}

# shifting longform
for arg in "$@"; do
    shift
    case "$arg" in
        '--zipcode')    set -- "$@" "-z"    ;;
        '--location')   set -- "$@" "-l"    ;;
        '--apikey')     set -- "$@" "-a"    ;;
        '--coords')     set -- "$@" "-c"    ;;
        '--search')     set -- "$@" "-s"    ;;
        '--units')      set -- "$@" "-u"    ;;
        '--pretty')     set -- "$@" "-p"    ;;
        '--pretty')     set -- "$@" "-p"    ;;
        '--icon_test')  set -- "$@" "-i"    ;;
        '--radar')      set -- "$@" "-r"    ;;
        '--forecast')   set -- "$@" "-f"    ;;
        '--help')       set -- "$@" "-h"    ;;
        *)              set -- "$@" "$arg"  ;;
    esac
done

# loading the info
load_info

while getopts "chpldrfiz:s:u:a:" opt; do
    case "$opt" in
        'a' )
            API_KEY="$OPTARG"
            update_key
            ;;
        'u' )
            UNIT="$OPTARG"
            ;;
        'l' )
            print_location
            exit 0
            ;;
        'z' )
            [ -n "$SEARCH" ] && usage 1 || SEARCH="/zip?zip=$OPTARG"
            ;;
        's' )
            [ -n "$SEARCH" ] && usage 1 || SEARCH="/direct?q=$OPTARG"
            ;;
        'p' )
            PRETTY=true
            ;;
        'd' )
            DESCRIPTION=true
            ;;
        'i' )
            icon_test
            ;;
        'r' )
            display_radar
            ;;
        'f' )
            display_forecast
            ;;
        'h' )
            usage 0
            ;;
        '?' )
            usage 1
            ;;
    esac
done

# test for APIKEY
if [[ -z "$API_KEY" ]] ; then
    echo "No API Key found!"
    exit 1
fi

if [[ -n "$SEARCH" ]] ; then
    # perform search
    get_location    
fi

if [[ -z "$QUIET" ]] ; then
    print_weather
fi
