"""air.py - is the air safe today?

Chiang Mai has a burning season. This program asks the internet how dirty
the air is right now, and tells you what that means for going outside.

The data is free. There is no sign-up, no password and no API key.

    pip3 install requests

    python3 air.py                 # Chiang Mai
    python3 air.py --place doi     # somewhere else in the list
    python3 air.py --hours 12      # what is coming later today
"""
import argparse
import sys

import requests

if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(encoding="utf-8", errors="replace")

API = "https://air-quality-api.open-meteo.com/v1/air-quality"

# Add your own place here. Find the numbers on any map website.
PLACES = {
    "chiangmai": ("Chiang Mai",     18.7883, 98.9853),
    "doi":       ("Doi Suthep",     18.8047, 98.9216),
    "sanpatong": ("San Pa Tong",    18.6262, 98.8942),
    "lamphun":   ("Lamphun",        18.5744, 99.0087),
    "bangkok":   ("Bangkok",        13.7563, 100.5018),
}

# What the numbers mean. Each row: up to this PM2.5, a word, and advice.
BANDS = [
    (12.0,  "GOOD",      "Go outside. The air is clean."),
    (35.4,  "OK",        "Most people are fine. Play outside."),
    (55.4,  "NOT GOOD",  "If you cough or have asthma, stay in."),
    (150.4, "BAD",       "Everyone should stay inside. Close the windows."),
    (250.4, "VERY BAD",  "Do not go outside. Wear an N95 mask if you must."),
    (99999, "DANGEROUS", "Stay inside. Keep the windows shut. Use a filter."),
]


def band_for(pm25):
    """Find the row that this PM2.5 number falls into."""
    for limit, word, advice in BANDS:
        if pm25 <= limit:
            return word, advice
    return BANDS[-1][1], BANDS[-1][2]


def bar(pm25, width=30):
    """Draw a simple bar so you can SEE the number, not just read it.

    One block is 5 ug/m3, so a clean day and a smoky day look different.
    Anything past 150 fills the bar; by then the exact number does not
    change the advice.
    """
    filled = min(int(pm25 / 5), width)
    return "#" * max(filled, 1) + "." * (width - max(filled, 1))


def get_air(lat, lon, hours):
    """Ask the website. It answers with JSON."""
    reply = requests.get(API, timeout=20, params={
        "latitude": lat,
        "longitude": lon,
        "current": "pm2_5,pm10,us_aqi",
        "hourly": "pm2_5",
        "forecast_days": 2,
        "timezone": "Asia/Bangkok",
    })
    reply.raise_for_status()      # stop here if the website said no
    return reply.json()


def main():
    parser = argparse.ArgumentParser(description="Check the air where you live.")
    parser.add_argument("--place", default="chiangmai", choices=sorted(PLACES))
    parser.add_argument("--hours", type=int, default=0,
                        help="also show this many hours ahead")
    args = parser.parse_args()

    name, lat, lon = PLACES[args.place]

    try:
        data = get_air(lat, lon, args.hours)
    except requests.exceptions.ConnectionError:
        print("No internet. This program needs it to ask about the air.")
        return 1
    except requests.exceptions.HTTPError as problem:
        print("The website said no: %s" % problem)
        return 1

    now = data["current"]
    pm25 = now["pm2_5"]
    word, advice = band_for(pm25)

    print()
    print("  %s   %s" % (name, now["time"].replace("T", "  ")))
    print("  " + "-" * 46)
    print("  PM2.5   %6.1f  %s" % (pm25, data["current_units"]["pm2_5"]))
    print("          %s" % bar(pm25))
    print("  PM10    %6.1f" % now["pm10"])
    print("  AQI     %6d" % now["us_aqi"])
    print()
    print("  %s" % word)
    print("  %s" % advice)
    print()

    if args.hours > 0:
        times = data["hourly"]["time"]
        values = data["hourly"]["pm2_5"]
        start = times.index(now["time"]) if now["time"] in times else 0
        print("  Next %d hours" % args.hours)
        for when, value in zip(times[start:start + args.hours],
                               values[start:start + args.hours]):
            if value is None:
                continue
            print("   %s  %6.1f  %s" % (when[11:16], value, bar(value, 24)))
        print()

    return 0


if __name__ == "__main__":
    sys.exit(main())
