
import argparse
import logging
import signal
import sys
from types import FrameType

from .client import ToloClient
from .command import Command
from .const import DEFAULT_PORT
from .message_info import SettingsInfo, StatusInfo
from .message import Message
from .server import ToloServer
from .simulator import Simulator


logger = logging.getLogger(__name__)

SETTINGS_COMMAND_MAP = {
    # main panel
    'power': Command.SET_POWER_ON,
    'fan': Command.SET_FAN_ON,
    'aroma-therapy': Command.SET_AROMA_THERAPY_ON,
    'lamp': Command.SET_LAMP_ON,
    'sweep': Command.GET_SWEEP_TIMER,
    'salt-bath': Command.SET_SALT_BATH_TIMER,
    # settings panel
    'target-temperature': Command.SET_TARGET_TEMPERATURE,
    'target-humidity': Command.SET_TARGET_HUMIDITY,
    'power-timer': Command.SET_POWER_TIMER,
    'salt-bath-timer': Command.SET_SALT_BATH_TIMER,
    'aroma-therapy-slot': Command.SET_AROMA_THERAPY,
    'sweep-timer': Command.SET_SWEEP_TIMER,
    'lamp-mode': Command.SET_LAMP_MODE,
    'fan-timer': Command.SET_FAN_TIMER
}


def main() -> int:
    argument_parser = argparse.ArgumentParser()
    argument_parser.add_argument('-l', '--log-level', choices=('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'),
                                 default='INFO')

    argument_sub_parser = argument_parser.add_subparsers(title='command', dest='command', required=True)

    server_argument_parser = argument_sub_parser.add_parser('server')
    server_argument_parser.add_argument('-l', '--listen-address', default='')
    server_argument_parser.add_argument('-p', '--port', type=int, default=DEFAULT_PORT)
    server_argument_parser.add_argument('-s', '--simulate', action='store_true')

    discover_argument_parser = argument_sub_parser.add_parser('discover')
    discover_argument_parser.add_argument('-p', '--port', type=int, default=DEFAULT_PORT)

    show_settings_argument_parser = argument_sub_parser.add_parser('show-settings')
    show_settings_argument_parser.add_argument('-a', '--address', type=str)
    show_settings_argument_parser.add_argument('-p', '--port', type=int, default=DEFAULT_PORT)
    show_settings_argument_parser.add_argument('--resend-timeout', type=float, default=1)
    show_settings_argument_parser.add_argument('--retries', type=int, default=3)

    set_argument_parser = argument_sub_parser.add_parser('set')
    set_argument_parser.add_argument('-a', '--address', type=str)
    set_argument_parser.add_argument('-p', '--port', type=int, default=DEFAULT_PORT)
    set_argument_parser.add_argument('--resend-timeout', type=float, default=1)
    set_argument_parser.add_argument('--retries', type=int, default=3)
    set_argument_parser.add_argument('key', choices=SETTINGS_COMMAND_MAP.keys())
    set_argument_parser.add_argument('value')

    args = argument_parser.parse_args()

    library_logger = logging.getLogger(__package__)
    log_handler = logging.StreamHandler(sys.stderr)
    log_formatter = logging.Formatter(fmt='%(asctime)s %(name)-20s [%(levelname)s] %(message)s')
    log_handler.setFormatter(log_formatter)
    library_logger.addHandler(log_handler)
    library_logger.setLevel(logging.getLevelName(args.log_level))

    if args.command == 'server':
        return cmd_server(args)
    elif args.command == 'discover':
        return cmd_discover(args)
    elif args.command == 'show-settings':
        return cmd_show_settings(args)
    elif args.command == 'set':
        return cmd_set(args)
    else:
        return 1


def cmd_server(args: argparse.Namespace) -> int:
    server = ToloServer(address=args.listen_address, port=args.port)

    def stop_on_signal(s: signal.Signals, t: FrameType) -> None:
        logger.debug('received signal %d, shutting down...' % s)
        server.stop()

    signal.signal(signal.SIGINT, stop_on_signal)

    if args.simulate:
        simulator = Simulator(server)
        simulator.start()

    server.run()
    return 0


def cmd_discover(args: argparse.Namespace) -> int:
    # TODO use args
    for message, address in ToloClient.discover():
        info = StatusInfo(message.info)
        print('=== Address: %s:%s' % (address[0], address[1]))
        print(info)
    return 0


def cmd_show_settings(args: argparse.Namespace) -> int:
    message = Message(Command.SETTINGS, Command.SETTINGS.default_value, b'\xFF')
    client = ToloClient(args.address, args.port)
    response = client.send_wait_response(message, resend_timeout=args.resend_timeout, retries=args.retries)
    if response is None:
        print('Did not get response')
        return 1
    else:
        info = SettingsInfo(response.info)
        print(info)
        return 0


def cmd_set(args: argparse.Namespace) -> int:
    command = SETTINGS_COMMAND_MAP.get(args.key)
    if command is None:
        raise ValueError('command not supported')  # TODO change error type

    data = command.command_data_type.to_byte_any(args.value)

    message = Message(command, data, b'\xFF')

    client = ToloClient(args.address, args.port)
    response = client.send_wait_response(message, resend_timeout=args.resend_timeout, retries=args.retries)
    if response is None:
        return 1
    else:
        print(response)
        return 0
