#!/usr/bin/env python3

""" Tool to send and receive data messages to/from a microapp. """

import asyncio
import logging
from os import path
import re

from bluenet_logs import BluenetLogs
from crownstone_ble import CrownstoneBle, BleEventBus, BleTopics
from crownstone_core.packets.microapp import MicroappMessagePacket
from crownstone_uart import CrownstoneUart, UartEventBus, UartTopics

from tools.util.config import getToolConfig, loadKeysFromConfig, setupDefaultCommandLineArguments, macFilterPassed

tool_version = "1.0.0"

argParser = setupDefaultCommandLineArguments('Send a data message to a microapp, and print data messages from a microapp.')
argParser.add_argument('-a', '--bleAddress',
                       default=None,
                       help='To send a message via BLE: the MAC address/handle of the Crownstone you want to connect to. Example: 11:22:33:AA:BB:CC')
argParser.add_argument('-d', '--device',
                       default=None,
                       help='The UART device of the Crownstone you want to connect to. Example: /dev/ttyACM0')
argParser.add_argument('--verbose', '-v',
                       dest="verbose",
                       action='store_true',
                       help='Show verbose output')
argParser.add_argument('--logStringsFile', '-l',
                       default="",
                       dest='logStringsFileName',
                       metavar='path',
                       type=str,
                       help='The path of the file with the extracted firmware logs on your system.')
argParser.add_argument('--message', '-m',
                       default=None,
                       help='The message data to send to the microapp.'
                            'Can be in the form of:\n'
                            '- ascii text: hello\n'
                            '- integer: 123\n'
                            '- hex array: 0x89AB12\n'
                            '- int array: [12, 34, 56]')

try:
    file_path = path.dirname(path.realpath(__file__))
    [tool_config, args] = getToolConfig(file_path, argParser)
except Exception as e:
    print("ERROR", e)
    quit()

if args.verbose:
    logging.basicConfig(format='%(asctime)s %(levelname)-7s: %(message)s', level=logging.DEBUG)

core = None
bluenetLogs = None
useBle = False

# The preferred chunk size. The library or the crownstone may make it a smaller size.
maxChunkSize = 256

# The index where we want to put our microapp.
appIndex = 0

# The microapp upload protocol version
protocol = 1

def onMicroappMessage(packet: MicroappMessagePacket):
    print(f"Received message: {packet.payload}")

if args.bleAddress is not None:
    # Use BLE for communication.
    useBle = True

    # Create the library instance.
    print(f'Initializing tool with bleAdapterAddress={tool_config["bleAdapterAddress"]}')
    core = CrownstoneBle(bleAdapterAddress=tool_config["bleAdapterAddress"])

    # Load the encryption keys into the library.
    loadKeysFromConfig(core, tool_config)

else:
    # Use UART for communication.

    # Create the library instance.
    print(f'Initializing tool with device={args.device}')
    core = CrownstoneUart()
    core.initialize_usb_sync(port=args.device)

    UartEventBus.subscribe(UartTopics.microappMessage, onMicroappMessage)

    if args.logStringsFileName:
        bluenetLogs = BluenetLogs()
        bluenetLogs.setLogStringsFile(args.logStringsFileName)


intMatchRegex = re.compile('\d+')
intArrayMatchRegex = re.compile('\[\s*(\d+\s*,\s*)+(\d+\s*)\]')
intArrayExtractRegex = re.compile('\d+')

def parseMessage() -> list:
    if args.message is None:
        return []

    # [12, 34, 56]
    if intArrayMatchRegex.match(args.message):
        arrayText = intArrayExtractRegex.findall(args.message)
        array = []
        for item in arrayText:
            array.append(int(item))
        return array

    # 0xAB1295
    if args.message.startswith('0x'):
        return list(bytearray.fromhex(args.message[2:]))

    # 123
    if intMatchRegex.match(args.message):
        value = int(args.message)
        return [value]

    # ascii string
    return list(args.message.encode('ascii'))

async def sendMessage():
    message = parseMessage()

    if len(message) == 0:
        print("No message to send")
        return

    await core.microapp.sendMessage(appIndex, protocol, message)
    print(f"Sent message: {message}")


async def main():
    if useBle:
        await core.connect(args.bleAddress)

    await sendMessage()

    if useBle:
        await core.disconnect()
        await core.shutDown()

    # Simply keep the program running.
    while True:
        await asyncio.sleep(100)

try:
    # asyncio.run does not work here.
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
except KeyboardInterrupt:
    print("Stopping")

if not useBle:
    core.stop()
