import asyncio
import socket
import aiomqtt
import telnetlib3
import toml
from socket import gethostname

mainBarcoIP = '192.168.192.12'
sideBarcoIP = '192.168.192.13'

# TODO all the checks and stuff
# TODO MAKE THIS CONFIGURALBE

cmdMap = {
    'power': 'POWR',
    'shutter': 'PMUT',
    'freeze': 'FRZE'
}
reverseCmdMap = {v: k for k, v in cmdMap.items()}


def parse_barco_response(raw: str):
    raw = raw[1:-1]  # strip square brackets
    #print(raw)
    if raw.startswith("ERR"):
        return None  # TODO parse type error - "disabled control" is special case which shouldnt normally happen
    cmd, status = raw.split("!", maxsplit=2)
    #print(cmd + " " + status)
    cmd = reverseCmdMap[cmd]
    status = '1' if status == '01' else '0'
    return cmd, status


async def barco_telnet_command(client, writer, select: str):
    onSub = f"p01/projectors/{select}/#"
    #print('TEST', onSub)
    onMatch = f"p01/projectors/{select}/command/+"
    await client.subscribe(onSub)
    #async with client.messages as msgs:
    async for mesg in client.messages:
        # print(mesg.topic.value)
        # print(mesg.payload.decode())
        # print('on', select)

        if mesg.topic.matches(onMatch):
            # print("test")
            cmd = mesg.topic.value.split("/")[-1]
            #val = '1' if mesg.payload.decode() == 'ON' else '0'
            val = '1' if mesg.payload.decode() == '1' else '0' # refactor to direct 0 and 1
            barcoCmd = f"[{cmdMap[cmd]}{val}]"
            print("Received: [" + mesg.topic.value + "] payload: [" + mesg.payload.decode() + "]")
            print("Sending command to Barco: " + barcoCmd)
            writer.write(barcoCmd)
            

async def barco_telnet_read_status(client, reader, select: str):
    while True:
        output = await reader.readuntil(b']')
        raw_response: str = output.decode().strip()  # strip not necessary? needed for local netcat testing though
        print("Received: " + raw_response + " from Barco (" + select + ')')
        parsed = parse_barco_response(raw_response)
        if parsed == None:
            continue  #TODO alert for errors
        print(f"Updating topic [{parsed[0]}] with value [{parsed[1]}]")
        await client.publish(f"p01/projectors/{select}/status/{parsed[0]}", payload=parsed[1])


async def barco_telnet_query_status(writer, select: str):
    while True:
        for val in cmdMap.values():
            print(f"Querying Barco {select} with: [{val}?]")
            writer.write(f"[{val}?]" + '\r\n')  # TODO test if funny CRLF necessary (it probably gets ignored)
            await asyncio.sleep(0.2)  # sleep between writes necessary, otherwise it gets confused.
            # simultaneous commands from control could break this? TODO fix later
        await asyncio.sleep(30)  # TODO find appropriate period


# async def shell(reader, writer):
#     async with aiomqtt.Client('localhost', 1883) as client:
#         task_status_query = asyncio.create_task(barco_telnet_query_status(writer))
#         task_status_reader = asyncio.create_task(barco_telnet_read_status(client, reader))
#         task_control = asyncio.create_task(barco_telnet_command(client, writer))
#         await asyncio.gather(task_status_query, task_status_reader, task_control)


async def main():
    #conf = toml.load('config.toml')
    # mainBarcoIP = conf[gethostname()]['projektor_glavni']
    # sideBarcoIP = conf[gethostname()]['projektor_stranski']
    mainReader, mainWriter = await telnetlib3.open_connection('localhost', 3023)
    sideReader, sideWriter = await telnetlib3.open_connection('localhost', 3024)
    async with aiomqtt.Client('localhost', 1883) as client:
        task_status_query_main = asyncio.create_task(barco_telnet_query_status(mainWriter, 'glavni'))
        task_status_reader_main = asyncio.create_task(barco_telnet_read_status(client, mainReader, 'glavni'))
        task_control_main = asyncio.create_task(barco_telnet_command(client, mainWriter, 'glavni'))
        task_status_query_side = asyncio.create_task(barco_telnet_query_status(sideWriter, 'stranski'))
        task_status_reader_side = asyncio.create_task(barco_telnet_read_status(client, sideReader, 'stranski'))
        task_control_side = asyncio.create_task(barco_telnet_command(client, sideWriter, 'stranski'))

        await asyncio.gather(task_status_query_main, task_status_reader_main, task_control_main,
                             task_status_query_side, task_status_reader_side, task_control_side)


### fuj to, ne tk delat

# if __name__ == '__main__':

#     loop = asyncio.get_event_loop()
#     coro = telnetlib3.open_connection(mainBarcoIP, 3023, shell=shell)
#     coro = telnetlib3.open_connection(mainBarcoIP, 3023, shell=shell)
#     # coro = telnetlib3.open_connection('localhost', 1234, shell=shell)
#     reader, writer  = loop.run_until_complete(coro)
#     loop.run_until_complete(writer.protocol.waiter_closed)

asyncio.run(main())