import os
from copy import deepcopy
from pprint import pprint
from datetime import datetime

from ww import f

from .spec import Signal, Message, Device, Common, Spec, make_sid


def date():
    return str(datetime.now())


def decode_signature(device, message, signal):
    id = make_sid(device.id, message.id)
    return f("decode_{id}_{signal.name}")


def decode_signal_sig(device, message, signal):
    return f("{signal.type} {decode_signature(device, message, signal)} (CANdata msg);")


def msg_struct_sig(device, message):
    return f("msg_{device.name}_{message.name}_t")


def device_struct_sig(device):
    return f("dev_{device.name}_t")


def spec_struct_sig(spec):
    return "can_t"


def build_msg_sig(device):
    for message in device.msgs.values():
        message.type = msg_struct_sig(device, message)


def multiplexor(signal, message):
    if signal.mux == "" or signal.mux == None:
        signal.mux_str = ""
        return

    sig = message.get_signal(signal.mux)
    signal.mux_str = f("[{sig.max_value - sig.min_value + 1}]")


def dst_type_decide(signal):
    if signal.scale != 1:
        if signal.length == 32:
            return "float"
        elif signal.length == 64:
            return "double"

    if signal.type == "signed":
        if signal.length <= 8:
            return "int8_t"
        elif signal.length <= 16:
            return "int16_t"
        elif signal.length <= 32:
            return "int32_t"
        elif signal.length <= 64:
            return "int64_t"

    if signal.length <= 8:
        return "uint8_t"
    elif signal.length <= 16:
        return "uint16_t"
    elif signal.length <= 32:
        return "uint32_t"
    elif signal.length <= 64:
        return "uint64_t"


def build_devices(spec, device, tpl):
    for message in device.msgs.values():
        message.multiplexor = ""
        message.mux_count = 0
        message.full_id = make_sid(device.id, message.id)
        for signal in message.signals.values():
            signal.dst_type = dst_type_decide(signal)
            multiplexor(signal, message)
            if signal.mux != "":
                message.multiplexor = signal.mux
            if signal.mux_count != 1:
                message.mux_count = signal.mux_count

    build_msg_sig(device)

    device.signature = device_struct_sig(device)

    h = tpl.tpl["h.jinja"].render(device=device, date=date())
    c = tpl.tpl["c.jinja"].render(
        device=device,
        f_msgs=[msg for msg in device.msgs.values() if msg.frequency != 0],
        date=date(),
    )

    return device.name, h, c


def build_can_ids(spec, tpl):
    for device in spec.devices.values():
        device.signature = device_struct_sig(device)

    spec.signature = spec_struct_sig(spec)

    build_msg_sig(spec.common)
    spec.common.signature = device_struct_sig(spec.common)

    logs = list(sorted(spec.logs.values(), key=lambda x: x.id))
    can_ids_h = tpl.tpl["can_ids_h.jinja"].render(
        devices=sorted(spec.devices.values(), key=lambda x: x.id),
        spec=spec,
        logs=logs,
        date=date(),
    )

    can_ids_c = tpl.tpl["can_ids_c.jinja"].render(
        devices=sorted(spec.devices.values(), key=lambda x: x.id),
        spec=spec,
        logs=logs,
        date=date(),
    )

    return can_ids_c, can_ids_h


def build_common(spec, tpl):
    for message in spec.common.msgs.values():
        sigs = message.signals
        for signal in message.signals.values():
            signal.dst_type = dst_type_decide(signal)
            multiplexor(signal, message)

    build_msg_sig(spec.common)
    spec.common.signature = device_struct_sig(spec.common)

    common_c = tpl.tpl["common_c.jinja"].render(spec=spec, date=date())
    common_h = tpl.tpl["common_h.jinja"].render(spec=spec, date=date())

    return common_c, common_h
