from collections import namedtuple
from operator import attrgetter

from c3nav.mapdata.models.geometry.space import RangingBeacon


def get_mesh_uplink_group(address):
    return 'mesh_uplink_%s' % address.replace(':', '-')


MESH_ALL_UPLINKS_GROUP = "mesh_uplink_all"
MESH_ALL_OTA_GROUP = "mesh_ota_all"
UPLINK_PING = 5
UPLINK_TIMEOUT = UPLINK_PING+5


def indent_c(code):
    return "    "+code.replace("\n", "\n    ").replace("\n    \n", "\n\n")


def get_node_names():
    from c3nav.mesh.models import MeshNode
    from c3nav.mesh.messages import MeshMessageType
    return {
        **{node.address: node.name for node in MeshNode.objects.prefetch_last_messages(MeshMessageType.CONFIG_NODE)},
        'ff:ff:ff:ff:ff:ff': "broadcast",
        '00:00:00:ff:ff:ff': "direct parent",
        '00:00:00:00:00:00': "root",
    }


def group_msg_type_choices(msg_types):
    msg_types = sorted(msg_types, key=attrgetter('value'))
    choices = {}
    for msg_type in msg_types:
        choices.setdefault(msg_type.name.split('_')[0].lower(), []).append(
            (msg_type.name, msg_type.pretty_name)
        )
    return tuple(choices.items())


NodesAndBeacons = namedtuple("NodesAndBeacons", ("beacons", "nodes", "nodes_for_beacons"))


def get_nodes_and_ranging_beacons():
    from c3nav.mesh.models import MeshNode
    from c3nav.mesh.messages import MeshMessageType
    beacons = {beacon.id: beacon for beacon in RangingBeacon.objects.all().select_related("space")}
    nodes = {
        node.address: node
        for node in MeshNode.objects.all().prefetch_last_messages().prefetch_ranging_beacon()
    }
    nodes_for_beacons = {
        node.ranging_beacon.id: node
        for node in nodes.values()
        if node.ranging_beacon and node.ranging_beacon.id in beacons
    }
    # todo: throw warnings if duplicates somewhere
    for ranging_beacon_id, node in nodes_for_beacons.items():
        ranging_beacon = beacons[ranging_beacon_id]
        ranging_beacon.save = None

        if not ranging_beacon.wifi_bssid:
            ranging_beacon.wifi_bssid = node.address
        if not ranging_beacon.bluetooth_address:
            ranging_beacon.bluetooth_address = node.address[:-2] + hex(int(node.address[-2:], 16)+1)[2:]

        ibeacon_msg = node.last_messages[MeshMessageType.CONFIG_IBEACON]
        if ibeacon_msg:
            if not ranging_beacon.ibeacon_uuid:
                ranging_beacon.ibeacon_uuid = ibeacon_msg.parsed.content.uuid
            if not ranging_beacon.ibeacon_major:
                ranging_beacon.ibeacon_major = ibeacon_msg.parsed.content.major
            if not ranging_beacon.ibeacon_uuid:
                ranging_beacon.ibeacon_minor = ibeacon_msg.parsed.content.minor

        node_msg = node.last_messages[MeshMessageType.CONFIG_NODE]
        if node_msg:
            if not ranging_beacon.node_number:
                ranging_beacon.node_number = node_msg.parsed.content.number
            ranging_beacon.node_name = node_msg.parsed.content.name

    return NodesAndBeacons(
        beacons=beacons,
        nodes=nodes,
        nodes_for_beacons=nodes_for_beacons
    )
