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

import sys
import time
from datetime import datetime
import argparse
import hoymiles
from RF24 import RF24, RF24_PA_LOW, RF24_PA_MAX, RF24_250KBPS, RF24_CRC_DISABLED, RF24_CRC_8, RF24_CRC_16
import paho.mqtt.client
import yaml
from yaml.loader import SafeLoader

parser = argparse.ArgumentParser(description='Ahoy - Hoymiles solar inverter gateway')
parser.add_argument("-c", "--config-file", nargs="?",
    help="configuration file")
global_config = parser.parse_args()

if global_config.config_file:
    with open(global_config.config_file) as yf:
        cfg = yaml.load(yf, Loader=SafeLoader)
else:
    with open(global_config.config_file) as yf:
        cfg = yaml.load('ahoy.yml', Loader=SafeLoader)

radio = RF24(22, 0, 1000000)
hmradio = hoymiles.HoymilesNRF(device=radio)
mqtt_client = None

command_queue = {}

hoymiles.HOYMILES_TRANSACTION_LOGGING=True
hoymiles.HOYMILES_DEBUG_LOGGING=True

def main_loop():
    inverters = [
            inverter for inverter in ahoy_config.get('inverters', [])
            if not inverter.get('disabled', False)]

    for inverter in inverters:
        if hoymiles.HOYMILES_DEBUG_LOGGING:
            print(f'Poll inverter {inverter["serial"]}')
        poll_inverter(inverter)

def poll_inverter(inverter):
    inverter_ser = inverter.get('serial')
    dtu_ser = ahoy_config.get('dtu', {}).get('serial')

    if len(command_queue[str(inverter_ser)]) > 0:
        payload = command_queue[str(inverter_ser)].pop(0)
    else:
        payload = hoymiles.compose_set_time_payload()

    payload_ttl = 4
    while payload_ttl > 0:
        payload_ttl = payload_ttl - 1
        com = hoymiles.InverterTransaction(
                radio=hmradio,
                dtu_ser=dtu_ser,
                inverter_ser=inverter_ser,
                request=next(hoymiles.compose_esb_packet(
                    payload,
                    seq=b'\x80',
                    src=dtu_ser,
                    dst=inverter_ser
                    )))
        response = None
        while com.rxtx():
            try:
                response = com.get_payload()
                payload_ttl = 0
            except Exception as e:
                print(f'Error while retrieving data: {e}')
                pass

    if response:
        dt = datetime.now()
        print(f'{dt} Payload: ' + hoymiles.hexify_payload(response))
        decoder = hoymiles.ResponseDecoder(response,
                request=com.request,
                inverter_ser=inverter_ser
                )
        result = decoder.decode()
        if isinstance(result, hoymiles.decoders.StatusResponse):
            data = result.__dict__()
            if hoymiles.HOYMILES_DEBUG_LOGGING:
                print(f'{dt} Decoded: {data["temperature"]}', end='')
                phase_id = 0
                for phase in data['phases']:
                    print(f' phase{phase_id}=voltage:{phase["voltage"]}, current:{phase["current"]}, power:{phase["power"]}, frequency:{data["frequency"]}', end='')
                    phase_id = phase_id + 1
                string_id = 0
                for string in data['strings']:
                    print(f' string{string_id}=voltage:{string["voltage"]}, current:{string["current"]}, power:{string["power"]}, total:{string["energy_total"]/1000}, daily:{string["energy_daily"]}', end='')
                    string_id = string_id + 1
                print()

            if mqtt_client:
                mqtt_send_status(mqtt_client, inverter_ser, data)


def mqtt_send_status(broker, interter_ser, data):
    topic = f'ahoy/{inverter_ser}'

    # AC Data
    phase_id = 0
    for phase in data['phases']:
        broker.publish(f'{topic}/emeter/{phase_id}/power', phase['power'])
        broker.publish(f'{topic}/emeter/{phase_id}/voltage', phase['voltage'])
        broker.publish(f'{topic}/emeter/{phase_id}/current', phase['current'])
        phase_id = phase_id + 1

    # DC Data
    string_id = 0
    for string in data['strings']:
        broker.publish(f'{topic}/emeter-dc/{string_id}/total', string['energy_total']/1000)
        broker.publish(f'{topic}/emeter-dc/{string_id}/power', string['power'])
        broker.publish(f'{topic}/emeter-dc/{string_id}/voltage', string['voltage'])
        broker.publish(f'{topic}/emeter-dc/{string_id}/current', string['current'])
        string_id = string_id + 1
    # Global
    broker.publish(f'{topic}/frequency', data['frequency'])
    broker.publish(f'{topic}/temperature', data['temperature'])

def mqtt_on_command():
    """
    Handle commands to topic
        ahoy/{inverter_ser}/command
    frame it and put onto command_queue
    """
    raise NotImplementedError('Receiving mqtt commands is yet to be implemented')

if __name__ == '__main__':
    ahoy_config = dict(cfg.get('ahoy', {}))

    mqtt_config = ahoy_config.get('mqtt', [])
    if mqtt_config.get('disabled', True):
        mqtt_client = paho.mqtt.client.Client()
        mqtt_client.username_pw_set(mqtt_config.get('user', None), mqtt_config.get('password', None))
        mqtt_client.connect(mqtt_config.get('host', '127.0.0.1'), mqtt_config.get('port', 1883))
        mqtt_client.loop_start()

    if not radio.begin():
        raise RuntimeError('Can\'t open radio')

    #command_queue.append(hoymiles.compose_02_payload())
    #command_queue.append(hoymiles.compose_11_payload())
    
    inverters = [inverter.get('serial') for inverter in ahoy_config.get('inverters', [])]
    for inverter_ser in inverters:
        command_queue[str(inverter_ser)] = []

    loop_interval = ahoy_config.get('interval', 1)
    try:
        while True:
            main_loop()
            if loop_interval:
                time.sleep(time.time() % loop_interval)

    except KeyboardInterrupt:
        radio.powerDown()
        sys.exit()