"""
Name: apc_subscriber
Title: APC Subscriber
Author: Cooper
Date: Fri 12/07/2024

Desc:  This is a test script to interface with the IRMA APC

"""
import time
import http.client
import threading
import _thread
from urllib.parse import urljoin

import xmltodict
from bottle import request, Bottle, HTTPResponse

from hanip.itxpt import module_inventory_service
from hanip.itxpt import DNS_SD

class ITxPTAPC(object):
    def __init__(self, config_dict, hw_dict, conf_dir, data_dir):
        self.config_dict = config_dict
        self.hw_dict = hw_dict
        self.conf_dir = conf_dir
        self.data_dir = data_dir

        self.apc_service_found = False
        self.service_discovery_running = False

        self.server_port = 9000

        self.service_ip = None  # IP address of the service provider
        self.service_port = None  # Port of the service provider
        self.service_root_path = None  # Request HTTP path

        self.apc_operation_details = {
            "subscribe_path": "/apc/passengerdoorcount",
            "reply_path": "/apc",
            "subscribed": False,
            "new_data": False,
            "raw_xml_data": None,
            "timeout": self.config_dict.get("APC_timeout", 0),
            "timer": 0,
        }

        self.service_discover = DNS_SD.DNSSD_Discover("_itxpt_http._tcp.local.", "apc")

    def setup_http_server(self):
        """
        Sets up an HTTP server that listens on all interfaces with the port number defined in the config file.  This HTTP
        server is run as a thread.
        """
        self.http_server = Bottle()

        self.http_server.route("/", method="POST", callback=self.handle_data_post)
        self.http_server.route("/apc", method="POST", callback=self.handle_data_post)
        self.http_server.route("/apc/passengerdoorcount", method="POST", callback=self.handle_data_post)


        self.http_thread = threading.Thread(target=self.http_server.run, kwargs=dict(host="0.0.0.0",
                                                                                port=self.server_port,
                                                                                debug=False
                                                                                ))

        self.http_thread.daemon = True
        self.http_thread.start()

    def look_for_service(self):
        """
        If enabled, this will wait forever until a service is found, unless a timeout is configured.  Otherwise it will
        use values defined in the config file as a timeout.  Once a service has been discovered it will set the
        server_found flag to True and then terminate
        """
        print("APC: Looking for service")
        self.avms_server_found = False
        service_timeout = time.time()
        if not self.service_discovery_running:
            self.service_discover.run()
            self.service_discovery_running = True

        while 1:
            # As the service discovery runs in its own thread this checks if it has found an IP yet.
            if self.service_discover.serviceIP == "":
                print("APC: Looking for service  ...still")

                # try:
                #     timeout = int(self.config_dict.get("AVMS_timeout", 30)) # Default to no timeout if incorrect parameter
                # except ValueError:
                #     timeout = 0

                # if timeout == 0:
                #     time.sleep(1)
                #     continue
                # else:
                #     if (time.time() - service_timeout) > int(self.config_dict["AVMS_timeout"]):
                #         print("AVMS: Timeout reached")
                #         return 1

                time.sleep(1)

            else:
                print("APC: Service found!")
                self.service_ip = self.service_discover.serviceIP
                self.service_port = self.service_discover.servicePort
                print(self.service_ip, self.service_port)

                self.avms_server_found = True
                return 0

    def send_subscription_request(self):
        """
        This creates the AVMS subscription message, it appears all the subscription requests are identical no matter
        which operation is being subscribed,  the only differences are the reply path, and the path where this is POSTed
        :return:
        """
        subscription_request = {
            "SubscribeRequest": {
                "Client-IP-Address": self.hw_dict["unit_IP"],
                "ReplyPort": self.server_port,
                "ReplyPath": self.apc_operation_details["reply_path"]
            }
        }

        subscribe_xml = xmltodict.unparse(subscription_request, pretty=True)

        if self.post_subscription_message(subscribe_xml, self.apc_operation_details["subscribe_path"]):
            print("AAPC: subscription success")
            self.apc_operation_details["timer"] = time.time()
            self.apc_operation_details["subscribed"] = True

    def post_subscription_message(self, data, path):
        """
        This routine deals with the HTTP POST

        The header is for completeness sake, not entirely necessary but for this packet to show up in wireshark as HTTP
        then it is needed.
        It would also seem that the parameter name is case sensitive in INEOs implementation, official HTTP spec seems
        to indicate otherwise.
        :param data:
        :param path:
        :return:
        """
        print("Sending request to %s:%s%s" % (self.service_ip, self.service_port, path))

        header = {
            "Content-Type": "text/xml"
        }

        # Try 5 times to connect to a service before going back
        self.http_error = "AttemptingConnection"

        for tries in range(5):
            self.http_error = None
            try:
                conn = http.client.HTTPConnection(self.service_ip, self.service_port, timeout=5)
                conn.request("POST", path, data, header)
                reply = conn.getresponse()
            except ConnectionRefusedError as e:
                print("ConnectionRefusedError", e)
                self.http_error = "ConnectionRefusedError"
                return None
            except TimeoutError as e:
                print("TimeoutError", e)
                self.http_error = "TimeoutError"
                return None
            except OSError as e:
                print("OSError", e)
                self.http_error = "OSError"
                return None
            except http.client.HTTPException as e:
                print("HTTPError", e)
                self.http_error = "HTTPError"
                return None

            self.http_error = "None"
            status = reply.status
            body = reply.read().decode("utf-8")

            print("HTTP reply:")
            print(status, body, "\n")

            if reply.status == 200:  # 200 is HTTP for OK
                if "true" in body.lower():  # Quick hack, is there any need to properly parse the response?
                    return 1
                else:
                    time.sleep(3)
            else:
                time.sleep(3)

        return 0

    def handle_data_post(self):
        """

        """
        xmldata = request.body.read().decode("utf-8")
        print(xmldata)

        return HTTPResponse(status=200)

    def run(self):
        """

        """
        self.look_for_service()
        self.send_subscription_request()

        while 1:
            time.sleep(5)

if __name__ == "__main__":
    hw_dict = {
        "unit_IP": "10.104.0.246"
    }

    config_dict = {
        "APC_timeout": 0
    }

    apc = ITxPTAPC(config_dict, hw_dict, None, None)
    apc.run()
