import json
import logging
import time

import attr

from dzcb.model import AnalogChannel, Contact, DigitalChannel, GroupList
import dzcb.munge

logger = logging.getLogger(__name__)

NAME_MAX = 16

value_replacements = {
    None: "None",
    False: "No",
    True: "Yes",
}

Contact_name_map = dict(
    name="Name",
    dmrid="CallID",
    kind="CallType",
)


def Contact_to_dict(c):
    d = dict(
        CallReceiveTone="No",
    )
    d.update(
        {
            Contact_name_map[k]: value_replacements.get(v, str(v))
            for k, v in attr.asdict(c).items()
            if k in attr.fields_dict(Contact)
        }
    )
    return d


GroupList_name_maps = dict(
    name="Name",
    contacts="Contact",
)


def GroupList_to_dict(g):
    return {
        "Name": g.name,
        "Contact": [tg.name for tg in g.contacts]
    }


def ScanList_to_dict(s):
    return dict(
        Name=dzcb.munge.zone_name(s.name, NAME_MAX),
        Channel=[ch.short_name for ch in s.channels],
        # Default settings
        PriorityChannel1="Selected",
        PriorityChannel2="None",
        PrioritySampleTime="2000",
        SignallingHoldTime="200",
        TxDesignatedChannel="Last Active Channel",
    )


DefaultChannel = dict(
    AdmitCriteria="Color code",
    AllowTalkaround=False,
    Autoscan=False,
    ColorCode=1,
    ContactName=None,
    CtcssDecode=None,
    CtcssEncode=None,
    DCDMSwitch=False,
    DataCallConfirmed=False,
    Decode1=False,
    Decode2=False,
    Decode3=False,
    Decode4=False,
    Decode5=False,
    Decode6=False,
    Decode7=False,
    Decode8=False,
    DisplayPTTID=False,
    EmergencyAlarmAck=False,
    EmergencySystem=None,
    GPSSystem=None,
    GroupList=None,
    InCallCriteria="Follow Admit Criteria",
    LeaderMS=False,
    LoneWorker=False,
    Privacy=None,
    PrivacyNumber=1,
    PrivateCallConfirmed=False,
    QtReverse=180,
    ReceiveGPSInfo=False,
    RepeaterSlot=1,
    ReverseBurst=False,
    RxOnly=False,
    RxRefFrequency="Medium",
    RxSignallingSystem=False,
    ScanList=None,
    SendGPSInfo=False,
    Squelch=0,
    Talkaround=False,
    Tot=120,
    TotRekeyDelay=0,
    TxRefFrequency="Medium",
    TxSignallingSystem=False,
    Vox=False,
)

AnalogChannel_name_maps = dict(
    name="Name",
    frequency="RxFrequency",
    offset="TxFrequencyOffset",
    power="Power",
    rx_only="RxOnly",
    bandwidth="Bandwidth",
    squelch="Squelch",
    tone_encode="CtcssEncode",
    tone_decode="CtcssDecode",
)


def AnalogChannel_to_dict(c):
    d = DefaultChannel.copy()
    d.update(
        {
            "ChannelMode": "Analog",
            "ScanList": dzcb.munge.zone_name(c.scanlist, NAME_MAX),
        }
    )
    d.update(
        {
            AnalogChannel_name_maps[k]: v
            for k, v in attr.asdict(c).items()
            if k in attr.fields_dict(AnalogChannel) and k in AnalogChannel_name_maps
        }
    )
    d["Name"] = c.short_name
    if d["CtcssEncode"]:
        if d["CtcssEncode"].startswith("D"):
            d["CtcssEncode"] += "N"
    else:
        d["CtcssEncode"] = "None"
    if d["CtcssDecode"]:
        if d["CtcssDecode"].startswith("D"):
            d["CtcssDecode"] += "N"
    else:
        d["CtcssDecode"] = "None"
    d["Bandwidth"] = str(round(d["Bandwidth"], 1)).replace(".0", "")
    return d


DigitalChannel_name_maps = dict(
    name="Name",
    frequency="RxFrequency",
    offset="TxFrequencyOffset",
    power="Power",
    rx_only="RxOnly",
    bandwidth="Bandwidth",
    color_code="Color Code",
)


def DigitalChannel_to_dict(c):
    d = DefaultChannel.copy()
    d.update(
        {
            "ChannelMode": "Digital",
            "RepeaterSlot": str(c.talkgroup.timeslot) if c.talkgroup else 1,
            "ContactName": str(c.talkgroup.name) if c.talkgroup else "Parrot 1",
            "GroupList": str(c.grouplist.name) if c.grouplist else None,
            "ScanList": dzcb.munge.zone_name(c.scanlist, NAME_MAX),
        }
    )
    d.update(
        {
            DigitalChannel_name_maps[k]: v
            for k, v in attr.asdict(c).items()
            if k in attr.fields_dict(DigitalChannel) and k in DigitalChannel_name_maps
        }
    )
    d["Name"] = c.short_name
    return d


Channel_value_replacements = {
    None: "None",
    False: "Off",
    True: "On",
}


def Channel_to_dict(c):
    d = None
    if isinstance(c, AnalogChannel):
        d = AnalogChannel_to_dict(c)
    elif isinstance(c, DigitalChannel):
        d = DigitalChannel_to_dict(c)
    if d is None:
        raise ValueError("Unknown type: {}".format(c))
    return {k: Channel_value_replacements.get(v, str(v)) for k, v in d.items()}


def Zone_to_dict(z):
    return {
        "Name": dzcb.munge.zone_name(z.name, NAME_MAX),
        "ChannelA": [ch.short_name for ch in z.channels_a],
        "ChannelB": [ch.short_name for ch in z.channels_b],
    }


def Codeplug_to_json(cp, based_on=None):
    cp_dict = {}
    if based_on is not None:
        cp_dict = json.load(based_on)
    # determine supported frequency range from BasicInformation
    ranges = []
    basic_info = cp_dict.get("BasicInformation", {})
    if "LowFrequency" in basic_info:
        ranges.append((basic_info["LowFrequency"], basic_info["HighFrequency"]))
    elif "LowFrequencyA" in basic_info:
        ranges.append((basic_info["LowFrequencyA"], basic_info["HighFrequencyA"]))
        ranges.append((basic_info["LowFrequencyB"], basic_info["HighFrequencyB"]))
    if ranges:
        cp = cp.filter_frequency_range(*ranges)
    cp_dict.update(
        dict(
            Contacts=[Contact_to_dict(c) for c in cp.contacts],
            Channels=[Channel_to_dict(c) for c in cp.channels],
            GroupLists=[GroupList_to_dict(c) for c in cp.grouplists],
            ScanLists=[ScanList_to_dict(c) for c in cp.scanlists],
            Zones=[Zone_to_dict(c) for c in cp.zones],
        )
    )
    # Set the programming date in intro text
    general_settings = cp_dict.setdefault("GeneralSettings", {})
    general_settings["IntroScreenLine1"] = time.strftime("%Y-%m-%d")
    logger.info(
        "Assemble JSON for %s",
        basic_info.get("Model", "Unknown. (probably won't work!)"),
    )
    return json.dumps(cp_dict, indent=2)
