#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import time
from logging import getLogger
from logging import root as logging_root
from logging.config import dictConfig as loggingDictConfig

from bluepy.btle import BTLEDisconnectError  # type: ignore
from serial import SerialException  # type: ignore

from .config import get_config, get_consumers, get_interface
from .protocol import ChargeController
from .util import LazyJSON, LoggingDictFilter, Periodical

logger = getLogger("SolarMPPT")


class CommunicationError(BTLEDisconnectError, SerialException, IOError):
    pass


def main():
    conf = get_config()

    loggingDictConfig(conf.get("logging", {}))
    logging_dict_filter = LoggingDictFilter()
    logging_dict_filter.data["service"] = "SolarMPPT"
    logging_root.addFilter(logging_dict_filter)

    consumers = get_consumers(conf)

    per_voltages = Periodical(interval=15)
    per_current_hist = Periodical(interval=60)

    try:
        while True:
            try:
                logger.info("Connecting...")
                with get_interface() as dev:
                    cc = ChargeController(dev)
                    logging_dict_filter.data["srne_model"] = cc.model
                    logging_dict_filter.data["srne_version"] = cc.version
                    logging_dict_filter.data["srne_serial"] = cc.serial

                    logger.info("Connected.")

                    logger.info(f"Controller model: {cc.model}")
                    logger.info(f"Controller version: {cc.version}")
                    logger.info(f"Controller serial: {cc.serial}")
                    for consumer in consumers:
                        consumer.controller = cc

                    # write(dev, construct_request(0, 32))

                    # Memory dump
                    # for address in range(0, 0x10000, 16):
                    #    log(f"Reading 0x{address:04X}...")
                    #    write(wd, construct_request(address, 16))
                    extra = cc.extra
                    days = extra.run_days

                    res = cc.today.as_dict()
                    res.update(extra.as_dict())
                    for consumer in consumers:
                        consumer.write(res)
                    del extra

                    # Historical data isn't actually used anywhere yet
                    # Limit to 4 days for now
                    for i in range(min(days, 4)):
                        hist = cc.get_historical(i)
                        res = hist.as_dict()
                        logger.debug(LazyJSON({i: res}))
                        for consumer in consumers:
                            consumer.write({str(i): res})

                    while True:
                        now = time.time()

                        if per_voltages(now):
                            data = cc.state.as_dict()
                            logger.debug(LazyJSON(data))
                            for consumer in consumers:
                                consumer.write(data)

                        if per_current_hist(now):
                            data = cc.today.as_dict()
                            data.update(cc.extra.as_dict())
                            logger.debug(LazyJSON(data))
                            for consumer in consumers:
                                consumer.write(data)

                        # print(".")
                        for consumer in consumers:
                            consumer.poll()

                        time.sleep(max(0, 1 - (time.time() - now)))

                    # if STATUS.get('load_enabled'):
                    #    write(wd, CMD_DISABLE_LOAD)
                    # else:
                    #    write(wd, CMD_ENABLE_LOAD)

            except CommunicationError:
                logger.error("Disconnected")
                time.sleep(1)

    except (KeyboardInterrupt, SystemExit, Exception) as e:
        for consumer in consumers:
            consumer.exit()

        if type(e) is not KeyboardInterrupt:
            raise


if __name__ == "__main__":
    main()