import asyncio
import socket
import threading
from hash_sender import Hash_Sender
from asyncua import Client, Node
from asyncua.common.subscription import DataChangeNotif, SubHandler
import yaml
import os

if(os.path.isfile("opc_ua_transmitter_config.yaml")):
    yamlfile=open("opc_ua_transmitter_config.yaml")
    data = yaml.load(yamlfile, Loader=yaml.FullLoader)
    server_hash_udp_ip = data["configuration"]["server_hash_udp_ip"]
    server_hash_udp_port = data["configuration"]["server_hash_udp_port"]
    server_udp_ip = data["configuration"]["server_udp_ip"]
    server_udp_port = int(data["configuration"]["server_udp_port"])
    serverAddressPort = (server_udp_ip,server_udp_port)
    ENDPOINT = data["configuration"]["endpoint"]
    NAMESPACE = data["configuration"]["namespace"]

bufferSize = 1024
UDPClientSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
nm_no = ''

class MyHandler(SubHandler):
    def __init__(self):
        self._queue = asyncio.Queue()

    def datachange_notification(self, node: Node, value, data: DataChangeNotif) -> None:
        self._queue.put_nowait([node, value, data])


    async def process(self) -> None:
        try:
            while True:
                [node, value, data] = self._queue.get_nowait()
                datatype = type(value)
                packet = str(datatype) + "&" + str(value) + "&"  + str(node.nodeid.NamespaceIndex) + "&" + str(node.nodeid.Identifier)
                UDPClientSocket.sendto(str.encode(packet), serverAddressPort)

        except asyncio.QueueEmpty:
            pass


variables=[]
async def get_nodes(client,node_objects):
    for sub_obj in node_objects:
        if ("ns="+nm_no+";" in str(sub_obj)):
            node = client.get_node(sub_obj)
            children_nodes = await node.get_children()
            identifier = str(children_nodes[0].nodeid.Identifier) if children_nodes else ""
            node_class = await sub_obj.read_node_class()
            if (identifier not in str(sub_obj)):
                await get_nodes(client, children_nodes)
                if (node_class._name_ == "Variable"):
                    variables.append(sub_obj)
            else:
                if (node_class._name_ == "Variable"):
                    variables.append(sub_obj)
    return variables


hash_changed=False

async def subscribe_node():
    global nm_no
    async with Client(url=ENDPOINT) as client:
        nm_no = str(await client.get_namespace_index(NAMESPACE))
        global hash_changed
        object_root_node = client.get_objects_node()
        objects = await object_root_node.get_children()
        node = await get_nodes(client, objects)
        handler = MyHandler()
        subscription = await client.create_subscription(period=0, handler=handler)
        await subscription.subscribe_data_change(node)
        while True and not hash_changed:
            await handler.process()
            await asyncio.sleep(0.01)
        hash_changed=False

def subscribe_to_node():
    asyncio.run(subscribe_node())

async def main() -> None:
    hs=Hash_Sender()
    global hash_changed
    prev_hash=""
    try:
        t1=threading.Thread(target=hs.send_hash,args=(ENDPOINT,NAMESPACE,server_hash_udp_ip,server_hash_udp_port,))
        t1.start()
        while True:
            hash=hs.hash
            if(hash!=prev_hash and not hash_changed):
                prev_hash=hash
                hash_changed = True
                while(hash_changed):
                    hash_changed = False
                t2=threading.Thread(target=subscribe_to_node,args=())
                t2.start()
    except Exception:
        print("No connection found for opcua server, check if the server is active")

def transmitter_main(_server_hash_udp_ip,_server_hash_udp_port,_server_udp_ip,_server_udp_port,_ENDPOINT,_NAMESPACE):
    global server_hash_udp_ip
    global server_hash_udp_port
    global server_udp_ip
    global server_hash_udp_port
    global server_udp_ip
    global server_udp_port
    global serverAddressPort
    global ENDPOINT
    global NAMESPACE
    server_hash_udp_ip = _server_hash_udp_ip
    server_hash_udp_port = _server_hash_udp_port
    server_udp_ip = _server_udp_ip
    server_udp_port = _server_udp_port
    serverAddressPort = (_server_udp_ip, _server_udp_port)
    ENDPOINT = _ENDPOINT
    NAMESPACE = _NAMESPACE
    asyncio.run(main())

if __name__ == "__main__":
    asyncio.run(main())
