# Copyright 3D Control Systems, Inc. All Rights Reserved 2017-2019.
# Built in San Francisco.

# This software is distributed under a commercial license for personal,
# educational, corporate or any other use.
# The software as a whole or any parts of it is prohibited for distribution or
# use without obtaining a license from 3D Control Systems, Inc.

# All software licenses are subject to the 3DPrinterOS terms of use
# (available at https://www.3dprinteros.com/terms-and-conditions/),
# and privacy policy (available at https://www.3dprinteros.com/privacy-policy/)

import os.path
import http.client
import json
import time
import threading

import version
import config

APP_NAME = '3DPrinterOS Client v.' + version.version
ICON_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), '3dprinteros-client.ico')
ICON_PATH_PNG = os.path.join(os.path.dirname(os.path.abspath(__file__)), '3dprinteros-client.png')
ICON_PATH_BMP = os.path.join(os.path.dirname(os.path.abspath(__file__)), '3dprinteros-client.bmp')
port = config.get_settings()['web_interface']['port']

def open_browser_tab():
    import webbrowser
    address = "http://%s:%d" % (config.LOCAL_IP, config.get_settings()['web_interface']['port'])
    webbrowser.open(address, 2)

def http_request_to_app(method, path, payload=None):
    try:
        connection = http.client.HTTPConnection("127.0.0.1", port=port, timeout=2)
        connection.connect()
        connection.request(method, path, payload)
        resp = connection.getresponse()
        received = resp.read()
        connection.close()
    except Exception as e:
        print("Tray HTTP error: ", method, path, payload)
        print('Error in tray_common: unable to do request to app, because of error - ' + str(e))
        return None, None
    else:
        return resp.status == http.client.OK and resp.reason == "OK", received

def stop_app():
    return http_request_to_app("GET", "/quit")

def get_3dprinteros_status():
    status, _ = http_request_to_app("GET", "/")
    return status


class StatusChecker:

    PATH_PREFIX = '/tray/'
    STATUS_CHECK_TIMEOUT = 1
    LOOP_SLEEP_TIME = 3
    LOOP_SLEEP_STEPS = 10
    MESSAGE_SHOW_TIME = 3

    def __init__(self, balloon_func):
        self.show_balloon = balloon_func
        self.notifications = config.get_settings()['tray_icon']['notifications_enabled']
        self.identical_printers_status = False
        self.updates_available_status = False
        self.last_printer_statuses = []
        self.stop_flag = False
        self.prev_state = ''
        self.main_loop = threading.Thread(target=self.start_main_loop, daemon=True)
        self.main_loop.start()

    def stop(self):
        self.stop_flag = True

    def toggle_notifications(self, value):
        print('Notifications: ' + str(value))
        self.notifications = value
        settings = config.get_settings()
        settings['tray_icon']['notifications_enabled'] = self.notifications
        config.Config.instance().save_settings(settings)

    def form_status_messages(self):
        _, data = http_request_to_app('POST', self.PATH_PREFIX + 'get_active_printers')
        if data:
            messages = []
            try:
                data = json.loads(data)
            except (ValueError, TypeError):
                return
            for printer_info in data:
                body = printer_info['temps']
                state = printer_info['status_report']['state']
                if state == 'printing':
                    percent = str(int(printer_info['status_report'].get('percent', 0)))
                    state += ' ' + percent + '%'
                elif state == 'error' and printer_info['error']:
                    body = printer_info['error']
                title = printer_info['name'] + ' - ' + state
                messages.append([title, body])
            if not messages:
                messages.append(['No printers detected', 'Please do a power cycle for printers and then ensure '
                                               'your printers are connected to power outlet and usb cord'])
            return messages

    def show_status_balloons(self, *messages):
        if not messages:
            messages = self.form_status_messages()
        if messages:
            for title, body in messages:
                if not body:
                    body = ' '
                self.show_balloon(title, body)

    def show_new_status_balloons(self):
        messages = self.form_status_messages()
        if messages:
            last_printer_statuses = []
            for message in messages:
                if 'printing' in message[0]:
                    message[0] = message[0][:-2] + '0%'
                last_printer_statuses.append(message[0])
                if not message[1]:
                    message[1] = ' '
                if not message[0] in self.last_printer_statuses:
                    self.show_balloon(*message)
            self.last_printer_statuses = last_printer_statuses

    def form_identical_printers_message(self):
        _, data = http_request_to_app('POST', self.PATH_PREFIX + 'confirm_identical_printers')
        if data:
            identical_printers_status = json.loads(data)
            if identical_printers_status and identical_printers_status != self.identical_printers_status:
                self.identical_printers_status = identical_printers_status
                return 'Warning! Identical printers detected!',\
                       'You have an identical printers connected to your machine'

    def form_updates_available_message(self):
        _, data = http_request_to_app('POST', self.PATH_PREFIX + 'updates_available')
        if data:
            updates_available_status = json.loads(data)
            if updates_available_status and updates_available_status != self.updates_available_status:
                self.updates_available_status = updates_available_status
                return 'Updates available!',\
                       'Press "Get updates" on main page to proceed'

    def check_status(self):
        self.show_new_status_balloons()
        for message in (self.form_identical_printers_message(), self.form_updates_available_message()):
            if message:
                self.show_balloon(*message)

    def start_main_loop(self):
        while not self.stop_flag:
            if self.notifications:
                self.check_status()
            steps_left = self.LOOP_SLEEP_STEPS
            while steps_left:
                if self.stop_flag:
                    return
                time.sleep(self.LOOP_SLEEP_TIME / self.LOOP_SLEEP_STEPS)
                steps_left -= 1


if __name__ == "__main__":

    def show_balloon(*args):
        print(args)
    status_checker = StatusChecker(show_balloon)
    time.sleep(10)
    stop_app()
