#!/usr/bin/env python3

from hinawa_utils.misc.cli_kit import CliKit
from hinawa_utils.bebob.bebob_unit import BebobUnit
from hinawa_utils.bebob.extensions import BcoPlugInfo

def handle_dump_connections(unit, args):
    unit_plug_list = unit.get_unit_plug_list()
    subunit_plug_list = unit.get_subunit_plug_list()

    conns = unit.get_avail_connections(unit_plug_list, subunit_plug_list)

    if len(conns) == 0:
        print('nothing avail.')
        return True

    for dst_seqid, avails in conns.items():
        if dst_seqid in unit_plug_list:
            info = unit_plug_list[dst_seqid]
        elif dst_seqid in subunit_plug_list:
            info = subunit_plug_list[dst_seqid]
        else:
            return False

        dst_spec = unit.get_plug_spec(info)
        print('{0} ({1})'.format(dst_spec['name'], dst_seqid))

        for avail in avails:
            src_seqid, used = avail
            if src_seqid in unit_plug_list:
                info = unit_plug_list[src_seqid]
            elif src_seqid in subunit_plug_list:
                info = subunit_plug_list[src_seqid]
            else:
                return False

            src_spec = unit.get_plug_spec(info)
            if used:
                print('   < {0} ({1})'.format(src_spec['name'], src_seqid))
            else:
                print('   * {0} ({1})'.format(src_spec['name'], src_seqid))

    return True

def handle_graph_connections(unit, args):
    unit_plug_list = unit.get_unit_plug_list()
    subunit_plug_list = unit.get_subunit_plug_list()

    specs = {}
    for plug_list in (unit_plug_list, subunit_plug_list):
        for seqid, info in plug_list.items():
            spec = unit.get_plug_spec(info)
            specs[seqid] = spec

    unit_plugs = {}
    subunit_plugs = {}

    for seqid, info in unit_plug_list.items():
        dir = info['dir']
        mode = info['mode']
        data = info['data']
        unit_type = data['unit-type']
        if unit_type not in unit_plugs:
            unit_plugs[unit_type] = {}
        if dir not in unit_plugs[unit_type]:
            unit_plugs[unit_type][dir] = {}
        unit_plugs[unit_type][dir][seqid] = info

    for seqid, info in subunit_plug_list.items():
        dir = info['dir']
        mode = info['mode']
        data = info['data']
        subunit_type = (data['subunit-type'], data['subunit-id'])
        if subunit_type not in subunit_plugs:
            subunit_plugs[subunit_type] = {}
        if dir not in subunit_plugs[subunit_type]:
            subunit_plugs[subunit_type][dir] = {}
        subunit_plugs[subunit_type][dir][seqid] = info

    print('digraph {')
    print('  rankdir = "LR"')

    for unit_type, entries in unit_plugs.items():
        if len(entries) == 0:
            continue

        for dir, plugs in entries.items():
            print('  subgraph cluster_unit_{0}_{1} {{'.format(unit_type, dir))
            print('    label = "{0} {1}"'.format(unit_type, dir))
            for seqid, info in plugs.items():
                spec = specs[seqid]
                label = '{0} ({1})'.format(spec['name'], spec['type'])
                shape = 'box' if spec['type'] == 'Sync' else 'ellipse'
                print('    {0} [label = "{1}", shape = "{2}"]'.format(
                    seqid, label, shape))
            print('  }')

    for subunit_type, entries in subunit_plugs.items():
        if len(entries) == 0:
            continue

        print('  subgraph cluster_subunit_{0[0]}_{0[1]} {{'.format(subunit_type))
        print('    label = "{0[0]} subunit {0[1]}"'.format(subunit_type))

        for dir, plugs in entries.items():
            print('    subgraph cluster_subunit_{0[0]}_{0[1]}_{1} {{'.format(
                subunit_type, dir))
            print('      label = "{0}"'.format(dir))
            for seqid, info in plugs.items():
                spec = specs[seqid]
                label = '{0} ({1})'.format(spec['name'], spec['type'])
                shape = 'box' if spec['type'] == 'Sync' else 'ellipse'
                print('    {0} [label = "{1}", shape = "{2}"]'.format(
                    seqid, label, shape))
            print('    }')
        print('  }')

    conns = unit.get_avail_connections(unit_plug_list, subunit_plug_list)
    for dst_seqid, avails in conns.items():
        dst_spec = specs[dst_seqid]
        for avail in avails:
            src_seqid, used = avail
            src_spec = specs[src_seqid]

            decorations = []
            if not used:
                decorations.append('style=dashed')
            if dst_spec['type'] == 'Sync' or src_spec['type'] == 'Sync':
                decorations.append('constraint=false')
            if len(decorations) > 0:
                decoration = '[{0}]'.format(' '.join(decorations))
            else:
                decoration = ''

            print('  {0} -> {1} {2}'.format(src_seqid, dst_seqid, decoration))

    print('}')

    return True

cmds = {
    'dump-connections':     handle_dump_connections,
    'graph-connections':    handle_graph_connections,
}

fullpath = CliKit.seek_snd_unit_path()
if fullpath:
    with BebobUnit(fullpath) as unit:
        CliKit.dispatch_command(unit, cmds)
