'''
Copyright 2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved.

Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject
to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com.

You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant
to the PySimpleGUI License Agreement.
'''

import PySimpleGUI as sg
import datetime
import base64
from urllib import request
import json
import sys
import webbrowser

"""
    A Current Weather Widget

    Adapted from the weather widget originally created and published by Israel Dryer that you'll find here:
    https://github.com/israel-dryer/Weather-App

    BIG THANKS goes out for creating a good starting point for other widgets to be build from.

    A true "Template" is being developed that is a little more abstracted to make creating your own
    widgets easy.  Things like the settings window is being standardized, the settings file format too.

    You will need a key (APPID) from OpenWeathermap.org in order to run this widget. It's free, it's easy:
    https://home.openweathermap.org/

    Your initial location is determined using your IP address and will be used if no settings file is found

    This widget is an early version of a PSG Widget so it may not share the same names / constructs as the templates. 

"""

SETTINGS_PATH = '.'

API_KEY = ''  # Set using the "Settings" window and saved in your config file

sg.theme('Light Green 6')
ALPHA = 0.8

BG_COLOR = sg.theme_text_color()
TXT_COLOR = sg.theme_background_color()

APP_DATA = {
    'City': 'New York',
    'Country': 'US',
    'Postal': 10001,
    'Description': 'clear skys',
    'Temp': 101.0,
    'Feels Like': 72.0,
    'Wind': 0.0,
    'Humidity': 0,
    'Precip 1hr': 0.0,
    'Pressure': 0,
    'Updated': 'Not yet updated',
    'Icon': None,
    'Units': 'Imperial'
}


def load_settings():
    global API_KEY
    settings = sg.UserSettings(path=SETTINGS_PATH)
    API_KEY = settings['-api key-']
    if not API_KEY:
        sg.popup_quick_message('No valid API key found... opening setup window...', keep_on_top=True, background_color='red', text_color='white',
                               auto_close_duration=3, non_blocking=False)
        change_settings(settings)
    return settings


def change_settings(settings, window_location=(None, None)):
    global APP_DATA, API_KEY

    try:
        nearest_postal = json.loads(request.urlopen('http://ipapi.co/json').read())['postal']
    except Exception as e:
        print('Error getting nearest postal', e)
        nearest_postal = ''

    layout = [[sg.T('Enter Zipcode or City for your location')],
              [sg.I(settings.get('-location-', nearest_postal), size=(15, 1), key='-LOCATION-')],
              [sg.I(settings.get('-api key-', ''), size=(32, 1), key='-API KEY-')],
              [sg.B('Ok', border_width=0, bind_return_key=True),  sg.B('Register For a Key', border_width=0, k='-REGISTER-'), sg.B('Cancel', border_width=0)], ]

    window = sg.Window('Settings', layout, location=window_location, no_titlebar=True, keep_on_top=True, border_depth=0)
    event, values = window.read()
    window.close()

    if event == '-REGISTER-':
        sg.popup('Launching browser so you can signup for the "Current Weather" service from OpenWeatherMap.org to get a Free API Key', 'Click OK and your browser will open', r'Visit https://home.openweathermap.org/ for more information', location=window_location)
        # Register to get a free key
        webbrowser.open(r'https://home.openweathermap.org/users/sign_up')


    if event == 'Ok':
        user_location = settings['-location-'] = values['-LOCATION-']
        API_KEY = settings['-api key-'] = values['-API KEY-']
    else:
        API_KEY = settings['-api key-']
        user_location = settings['-location-']

    if user_location is not None:
        if user_location.isnumeric() and len(user_location) == 5 and user_location is not None:
            APP_DATA['Postal'] = user_location
            APP_DATA['City'] = ''
        else:
            APP_DATA['City'] = user_location
            APP_DATA['Postal'] = ''

    return settings


def update_weather():
    if APP_DATA['City']:
        request_weather_data(create_endpoint(2))
    elif APP_DATA['Postal']:
        request_weather_data(create_endpoint(1))


def create_endpoint(endpoint_type=0):
    """ Create the api request endpoint
    {0: default, 1: zipcode, 2: city_name}"""
    if endpoint_type == 1:
        try:
            endpoint = f"http://api.openweathermap.org/data/2.5/weather?zip={APP_DATA['Postal']},us&appid={API_KEY}&units={APP_DATA['Units']}"
            return endpoint
        except ConnectionError:
            return
    elif endpoint_type == 2:
        try:
            endpoint = f"http://api.openweathermap.org/data/2.5/weather?q={APP_DATA['City'].replace(' ', '%20')},us&APPID={API_KEY}&units={APP_DATA['Units']}"
            return endpoint
        except ConnectionError:
            return
    else:
        return


def request_weather_data(endpoint):
    """ Send request for updated weather data """
    global APP_DATA

    if endpoint is None:
        sg.popup_error('Could not connect to api.  endpoint is None', keep_on_top=True)
        return
    else:
        try:
            response = request.urlopen(endpoint)
        except request.HTTPError:
            sg.popup_error('ERROR Obtaining Weather Data',
                           'Is your API Key set correctly?',
                           API_KEY, keep_on_top=True)
            return

    if response.reason == 'OK':
        weather = json.loads(response.read())
        APP_DATA['City'] = weather['name'].title()
        APP_DATA['Description'] = weather['weather'][0]['description']
        APP_DATA['Temp'] = "{:,.0f}°F".format(weather['main']['temp'])
        APP_DATA['Humidity'] = "{:,d}%".format(weather['main']['humidity'])
        APP_DATA['Pressure'] = "{:,d} hPa".format(weather['main']['pressure'])
        APP_DATA['Feels Like'] = "{:,.0f}°F".format(weather['main']['feels_like'])
        APP_DATA['Wind'] = "{:,.1f} m/h".format(weather['wind']['speed'])
        APP_DATA['Precip 1hr'] = None if not weather.get('rain') else "{:2} mm".format(weather['rain']['1h'])
        APP_DATA['Updated'] = 'Updated: ' + datetime.datetime.now().strftime("%B %d %I:%M:%S %p")
        APP_DATA['Lon'] = weather['coord']['lon']
        APP_DATA['Lat'] = weather['coord']['lat']

        icon_url = "http://openweathermap.org/img/wn/{}@2x.png".format(weather['weather'][0]['icon'])
        APP_DATA['Icon'] = base64.b64encode(request.urlopen(icon_url).read())


def metric_row(metric):
    """ Return a pair of labels for each metric """
    return [sg.Text(metric, font=('Arial', 10), pad=(15, 0), size=(9, 1)),
            sg.Text(APP_DATA[metric], font=('Arial', 10, 'bold'), pad=(0, 0), size=(9, 1), key=metric)]


def create_window(win_location):
    """ Create the application window """
    col1 = sg.Column(
        [[sg.Text(APP_DATA['City'], font=('Arial Rounded MT Bold', 18), pad=((10, 0), (50, 0)), size=(18, 1), background_color=BG_COLOR, text_color=TXT_COLOR,
                  key='City')],
         [sg.Text(APP_DATA['Description'], font=('Arial', 12), pad=(10, 0), background_color=BG_COLOR, text_color=TXT_COLOR, key='Description')]],
        background_color=BG_COLOR, key='COL1')

    col2 = sg.Column(
        [[sg.Text('×', font=('Arial Black', 16), pad=(0, 0), justification='right', background_color=BG_COLOR, text_color=TXT_COLOR, enable_events=True,
                  key='-QUIT-')],
         [sg.Image(data=APP_DATA['Icon'], pad=((5, 10), (0, 0)), size=(100, 100), background_color=BG_COLOR, key='Icon')]],
        element_justification='center', background_color=BG_COLOR, key='COL2')

    col3 = sg.Column(
        [[sg.Text(APP_DATA['Updated'], font=('Arial', 8), background_color=BG_COLOR, text_color=TXT_COLOR, key='Updated')]],
        pad=(10, 5), element_justification='left', background_color=BG_COLOR, key='COL3')

    col4 = sg.Column(
        [[sg.Text('Settings', font=('Arial', 8, 'italic'), background_color=BG_COLOR, text_color=TXT_COLOR, enable_events=True, key='-CHANGE-'),
          sg.Text('Refresh', font=('Arial', 8, 'italic'), background_color=BG_COLOR, text_color=TXT_COLOR, enable_events=True, key='-REFRESH-')]],
        pad=(10, 5), element_justification='right', background_color=BG_COLOR, key='COL4')

    top_col = sg.Column([[col1, col2]], pad=(0, 0), background_color=BG_COLOR, key='TopCOL')

    bot_col = sg.Column([[col3, col4]], pad=(0, 0), background_color=BG_COLOR, key='BotCOL')

    lf_col = sg.Column(
        [[sg.Text(APP_DATA['Temp'], font=('Haettenschweiler', 90), pad=((10, 0), (0, 0)), justification='center', key='Temp')]],
        pad=(10, 0), element_justification='center', key='LfCOL')

    rt_col = sg.Column(
        [metric_row('Feels Like'), metric_row('Wind'), metric_row('Humidity'), metric_row('Precip 1hr'), metric_row('Pressure')],
        pad=((15, 0), (25, 5)), key='RtCOL')

    layout = [[top_col],
              [lf_col, rt_col],
              [bot_col]]

    window = sg.Window(layout=layout, title='Weather Widget', margins=(0, 0), finalize=True, location=win_location,
                       element_justification='center', keep_on_top=True, no_titlebar=True, grab_anywhere=True, alpha_channel=ALPHA, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, auto_save_location=True)

    for col in ['COL1', 'COL2', 'TopCOL', 'BotCOL', '-QUIT-']:
        window[col].expand(expand_y=True, expand_x=True)

    for col in ['COL3', 'COL4', 'LfCOL', 'RtCOL']:
        window[col].expand(expand_x=True)

    window['-CHANGE-'].set_cursor('hand2')
    window['-QUIT-'].set_cursor('hand2')
    window['-REFRESH-'].set_cursor('hand2')

    return window


def update_metrics(window):
    """ Adjust the GUI to reflect the current weather metrics """
    metrics = ['City', 'Temp', 'Feels Like', 'Wind', 'Humidity', 'Precip 1hr',
               'Description', 'Icon', 'Pressure', 'Updated']
    for metric in metrics:
        if metric == 'Icon':
            window[metric].update(data=APP_DATA[metric])
        else:
            window[metric].update(APP_DATA[metric])


def main(refresh_rate, win_location):
    """ The main program routine """
    refresh_in_milliseconds = refresh_rate * 60 * 1000

    # Load settings from config file. If none found will create one
    settings = load_settings()
    location = settings['-location-']
    if location is not None:
        if location.isnumeric() and len(location) == 5 and location is not None:
            APP_DATA['Postal'] = location
            APP_DATA['City'] = ''
        else:
            APP_DATA['City'] = location
            APP_DATA['Postal'] = ''
        update_weather()
    else:
        sg.popup_error('Having trouble with location.  Your location: ', location)
        exit()

    window = create_window(win_location)

    while True:  # Event Loop
        event, values = window.read(timeout=refresh_in_milliseconds)
        if event in (None, '-QUIT-', 'Exit'):
            break
        if event == '-CHANGE-':
            x, y = window.current_location()
            settings = change_settings(settings, (x + 405, y))
        elif event == '-REFRESH-':
            sg.popup_quick_message('Refreshing...', keep_on_top=True, background_color='red', text_color='white',
                                   auto_close_duration=3, non_blocking=False)
        elif event == 'Edit Me':
            sg.execute_editor(__file__)
        elif event == 'Version':
            sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)
        update_weather()
        update_metrics(window)
    window.close()


if __name__ == '__main__':
    if len(sys.argv) > 1:
        location = sys.argv[1].split(',')
        location = (int(location[0]), int(location[1]))
    else:
        location = (None, None)
    main(refresh_rate=1, win_location=location)
