#!/bin/sh

#
# AnsiWeather 1.19.0
# Copyright (c) 2013-2023, Frederic Cambus
# https://github.com/fcambus/ansiweather
#
# Created: 2013-08-29
# Last Updated: 2023-01-09
#
# AnsiWeather is released under the BSD 2-Clause license.
# See LICENSE file for details.
#
# SPDX-License-Identifier: BSD-2-Clause
#

###[ Configuration options ]###################################################

LC_NUMERIC=C; export LC_NUMERIC

if [ -n "$ANSIWEATHERRC" ] ; then
	config_file="$ANSIWEATHERRC"
elif [ -s "$XDG_CONFIG_HOME"/ansiweather/config ] ; then
	config_file="$XDG_CONFIG_HOME"/ansiweather/config
elif [ -s "$HOME"/.config/ansiweather/config ] ; then
	config_file="$HOME"/.config/ansiweather/config
else
	config_file=~/.ansiweatherrc
fi

get_config() {
	ret=""
	if [ -f "$config_file" ]
	then
		ret=$(grep "^$1:" "$config_file" | cut -d: -f2-)
	fi

	if [ "X$ret" = "X" ]
	then
		return 1
	else
		echo "$ret"
	fi
}

fetch_cmd=$(get_config "fetch_cmd" || echo "curl -sf")



###[ Parse the command line ]##################################################

# Get config options from command line flags
while getopts l:u:f:FH:a:s:k:i:w:h:p:d:v option
do
	case "${option}"
	in
		l) location=${OPTARG};;
		u) units=${OPTARG};;
		f) forecast=${OPTARG};;
		F) forecast="5";;
		H) show_feels_like=${OPTARG};;
		a) ansi=${OPTARG};;
		s) symbols=${OPTARG};;
		k) api_key=${OPTARG};;
		i) show_uvi=${OPTARG};;
		w) show_wind=${OPTARG};;
		h) show_humidity=${OPTARG};;
		p) show_pressure=${OPTARG};;
		d) show_daylight=${OPTARG};;
		v) echo "AnsiWeather 1.19.0" && exit 0;;
		\?) exit 64;; # EX_USAGE
	esac
done



###[ Check if bc and jq are installed ]########################################

jqpath=$(command -v jq)
if [ "$jqpath" = "" ]
then
	echo "ERROR: Cannot find jq binary"
	exit 69 # EX_UNAVAILABLE
fi

bcpath=$(command -v bc)
if [ "$bcpath" = "" ]
then
	echo "ERROR: Cannot find bc binary"
	exit 69 # EX_UNAVAILABLE
fi



###[ Set options that are not set from command line ]##########################

# OpenWeatherMap API key
[ -z "$api_key" ] && api_key=$(get_config "api_key" || echo "85a4e3c55b73909f42c6a23ec35b7147")

# Location: example "Rzeszow,PL"
[ -z "$location" ] && location=$(get_config "location" || echo "Rzeszow,PL")

# System of Units: "metric" or "imperial"
[ -z "$units" ] && units=$(get_config "units" || echo "metric")

# Show forecast: How many days, example "5". "0" is standard output
[ -z "$forecast" ] && forecast=$(get_config "forecast" || echo 0)

# Display ANSI colors: "true" or "false"
[ -z "$ansi" ] && ansi=$(get_config "ansi" || echo true)

# Display symbols: "true" or "false" (requires a Unicode capable display)
[ -z "$symbols" ] && symbols=$(get_config "symbols" || echo false)

# Show feels-like: "true" or "false"
[ -z "$show_feels_like" ] && show_feels_like=$(get_config "show_feels_like" || echo false)

# Show UVI: "true" or "false"
[ -z "$show_uvi" ] && show_uvi=$(get_config "show_uvi" || echo true)

# Show wind: "true" or "false"
[ -z "$show_wind" ] && show_wind=$(get_config "show_wind" || echo true)

# Show humidity: "true" or "false"
[ -z "$show_humidity" ] && show_humidity=$(get_config "show_humidity" || echo true)

# Show pressure: "true" or "false"
[ -z "$show_pressure" ] && show_pressure=$(get_config "show_pressure" || echo true)

# Show daylight: "true" or "false"
[ -z "$show_daylight" ] && show_daylight=$(get_config "show_daylight" || echo false)

dateformat=$(get_config "dateformat" || echo "%a %b %d")
timeformat=$(get_config "timeformat" || echo "%b %d %r")



###[ Colors and characters ]###################################################

background=$(get_config "background" || echo "\033[44m")
text=$(get_config "text" || echo "\033[36;1m")
data=$(get_config "data" || echo "\033[33;1m")
delimiter=$(get_config "delimiter" || echo "\033[35m:")
dashes=$(get_config "dashes" || echo "\033[34m-")



###[ Text Labels ]#############################################################

greeting_text=$(get_config "greeting_text" || echo "Weather in")
wind_text=$(get_config "wind_text" || echo "Wind")
feels_like_text=$(get_config "feels_like_text" || echo "Feels like")
humidity_text=$(get_config "humidity_text" || echo "Humidity")
pressure_text=$(get_config "pressure_text" || echo "Pressure")
sunrise_text=$(get_config "sunrise_text" || echo "Sunrise")
sunset_text=$(get_config "sunset_text" || echo "Sunset")
forecast_text=$(get_config "forecast_text" || echo "forecast")



###[ Unicode Symbols for icons ]###############################################

sun=$(get_config "sun" || echo "\033[33;1m\xe2\x98\x80")
moon=$(get_config "moon" || echo "\033[36m\xe2\x98\xbd")
clouds=$(get_config "clouds" || echo "\033[37;1m\xe2\x98\x81")
rain=$(get_config "rain" || echo "\033[37;1m\xe2\x98\x94")
fog=$(get_config "fog" || echo "\033[37;1m\xe2\x96\x92")
mist=$(get_config "mist" || echo "\033[34m\xe2\x96\x91")
haze=$(get_config "haze" || echo "\033[33m\xe2\x96\x91")
snow=$(get_config "snow" || echo "\033[37;1m\xe2\x9d\x84")
thunderstorm=$(get_config "thunderstorm" || echo "\033[33;1m\xe2\x9a\xa1")



###[ Fetch Weather data ]######################################################

api_cmd=$([ "$forecast" != 0 ] && echo "forecast/daily" || echo "weather")

if [ "$location" -gt 0 ] 2> /dev/null
then
	# Location is all numeric
	weather=$($fetch_cmd "https://api.openweathermap.org/data/2.5/$api_cmd?id=$location&units=$units&appid=$api_key")
else
	# Location is a string
	location=$(echo "$location" | sed "s/ /%20/g")
	weather=$($fetch_cmd "https://api.openweathermap.org/data/2.5/$api_cmd?q=$location&units=$units&appid=$api_key")
fi

if [ -z "$weather" ]
then
	echo "ERROR: Cannot fetch weather data"
	exit 75 # EX_TEMPFAIL
fi

status_code=$(echo "$weather" | jq -r '.cod' 2>/dev/null)

if [ "$status_code" != 200 ]
then
	echo "ERROR: Cannot fetch weather data for the given location"
	exit 69 # EX_UNAVAILABLE
fi



###[ Fetch UV data ]###########################################################

if [ "$show_uvi" = true ] && [ "$forecast" = 0 ]
then
	lat=$(echo "$weather" | jq -r '.coord.lat')
	lon=$(echo "$weather" | jq -r '.coord.lon')

	uvdata=$($fetch_cmd "https://api.openweathermap.org/data/2.5/uvi?lat=$lat&lon=$lon&appid=$api_key")
	uvi=$(echo "$uvdata" | jq -r '.value')
fi



###[ Process Weather data ]####################################################

epoch_to_date() {
	if date -j -r "$1" +"%a %b %d" > /dev/null 2>&1; then
		# BSD
		ret=$(date -j -r "$1" +"$dateformat")
	else
		# GNU
		ret=$(date -d "@$1" +"$dateformat")
	fi
	echo "$ret"
}

if [ "$forecast" != 0 ]
then
	city=$(echo "$weather" | jq -r '.city.name')
	flength=$(echo "$weather" | jq '.list | length')
	forecast=$([ "$forecast" -gt "$flength" ] && echo "$flength" || echo "$forecast")
else
	city=$(echo "$weather" | jq -r '.name')
	temperature=$(echo "$weather" | jq '.main.temp' | xargs printf "%.0f")
	humidity=$(echo "$weather" | jq '.main.humidity')
	feels_like=$(echo "$weather" | jq '.main.feels_like' | xargs printf "%.0f")
	pressure=$(echo "$weather" | jq '.main.pressure')
	sky=$(echo "$weather" | jq -r '.weather[0].main')
	sunrise=$(echo "$weather" | jq '.sys.sunrise')
	sunset=$(echo "$weather" | jq '.sys.sunset')
	wind=$(echo "$weather" | jq '.wind.speed')
	azimuth=$(echo "$weather" | jq '.wind.deg')
fi



###[ Process Wind data ]#######################################################

set -- $(get_config "wind_directions" || echo "N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW")

if [ "$forecast" = 0 ]
then
	shift "$(echo "scale=0; ($azimuth + 11.25)/22.5 % 16" | bc)"
	direction=$1
fi



###[ Process Sunrise and Sunset data ]#########################################

epoch_to_time() {
	if date -j -r "$1" +"%r" > /dev/null 2>&1; then
		# BSD
		ret=$(date -j -r "$1" +"$timeformat")
	else
		# GNU
		ret=$(date -d "@$1" +"$timeformat")
	fi
	echo "$ret"
}

if [ "$forecast" = 0 ]
then
	if [ -n "$sunrise" ]
	then
		sunrise_time=$(epoch_to_time "$sunrise")
	fi

	if [ -n "$sunset" ]
	then
		sunset_time=$(epoch_to_time "$sunset")
	fi
fi



###[ Set the period ]##########################################################

now=$(date +%s)

if [ "$forecast" != 0 ]
then
	period="none"
else
	if [ -z "$sunset" ] || [ -z "$sunrise" ]
	then
		period="day"
	elif [ "$now" -ge "$sunset" ] || [ "$now" -le "$sunrise" ]
	then
		period="night"
	else
		period="day"
	fi
fi



###[ Set the scale ]###########################################################

case $units in
	metric)
		scale="°C"
		speed_unit="m/s"
		pressure_unit="hPa"
		pressure=$(echo "$pressure" | xargs printf "%.0f")
		;;
	imperial)
		scale="°F"
		speed_unit="mph"
		pressure_unit="inHg"
		if [ "$forecast" = 0 ]
		then
			pressure=$(echo "$pressure*0.0295" | bc | xargs printf "%.2f")
		fi
		;;
esac



###[ Set icons ]###############################################################

get_icon() {
	case $1 in
		Clear)
			if [ $period = "night" ]
			then
				echo "$moon "
			else
				echo "$sun "
			fi
			;;
		Clouds)
			echo "$clouds "
			;;
		Rain)
			echo "$rain "
			;;
		Fog)
			echo "$fog "
			;;
		Mist)
			echo "$mist "
			;;
		Haze)
			echo "$haze "
			;;
		Snow)
			echo "$snow "
			;;
		Thunderstorm)
			echo "$thunderstorm "
			;;
	esac
}



###[ Display current Weather ]#################################################

if [ "$forecast" != 0 ]
then
	output="$background$text $city $forecast_text$text$delimiter "

	i=0
	while [ $i -lt "$forecast" ]
	do
		day=$(echo "$weather" | jq ".list[$i]")
		date=$(epoch_to_date "$(echo "$day" | jq -r '.dt')")
		low=$(echo "$day" | jq -r '.temp.min' | xargs printf "%.0f")
		high=$(echo "$day" | jq -r '.temp.max' | xargs printf "%.0f")

		icon=""
		if [ "$symbols" = true ]
		then
			sky=$(echo "$day" | jq -r '.weather[0].main')
			icon=$(get_icon "$sky")
		fi

		output="$output$text$date$delimiter $data$high$text/$data$low $scale $icon"
		if [ $i -lt $((forecast-1)) ]
		then
			output="$output$dashes "
		fi

		i=$((i + 1))
	done
else
	if [ "$symbols" = true ]
	then
		icon="$(get_icon "$sky")"
	fi
	output="$background$text $greeting_text $city$delimiter$data $temperature $scale $icon"

        if [ "$show_feels_like" = true ]
        then
                output="$output$dashes$text $feels_like_text$delimiter$data $feels_like $scale "
        fi

	if [ "$show_uvi" = true ]
	then
		output="$output$dashes$text UVI$delimiter$data $uvi "
	fi

	if [ "$show_wind" = true ]
	then
		output="$output$dashes$text $wind_text$delimiter$data $wind $speed_unit $direction "
	fi

	if [ "$show_humidity" = true ]
	then
		output="$output$dashes$text $humidity_text$delimiter$data $humidity%% "
	fi

	if [ "$show_pressure" = true ]
	then
		output="$output$dashes$text $pressure_text$delimiter$data $pressure $pressure_unit "
	fi

	if [ "$show_daylight" = true ]
	then
		output="$output$dashes$text $sunrise_text$delimiter$data $sunrise_time $dashes$text $sunset_text$delimiter$data $sunset_time "
	fi
fi

if [ "$ansi" = true ]
then
	env printf "$output\033[0m\n"
else
	env printf "$output\n" | sed "s/$(printf '\033')\[[0-9;]*m//g"
fi