import re
import tiles
import json
lc_input_re = re.compile(r'(J?[ABCDM]|CLK|LSR|CE)\d')
lc_output_re = re.compile(r'J?[FQ]\d')

def get_span(wire):
    if (wire.startswith("H") or wire.startswith("V")) and wire[1:3].isdigit():
        return "span" + wire[2] + wire[0].lower() + wire[3].lower()
    return None


def is_denorm(wire):
    if (wire.startswith("H06") or wire.startswith("V06")) and not wire.endswith("03"):
        return True
    if (wire.startswith("H02") or wire.startswith("V02")) and not wire.endswith("01"):
        return True
    return False


def get_distance(a, b):
    ra, ca = tiles.pos_from_name(a, (126, 95), 0)
    rb, cb = tiles.pos_from_name(b, (126, 95), 0)
    return abs(ra-rb) + abs(ca-cb)


def format_rel(a, b):
    ra, ca = tiles.pos_from_name(a, (126, 95), 0)
    rb, cb = tiles.pos_from_name(b, (126, 95), 0)
    rel = ""
    if rb < ra:
        rel += "n{}".format(ra-rb)
    elif rb > ra:
        rel += "s{}".format(rb-ra)

    if cb < ca:
        rel += "w{}".format(ca-cb)
    elif cb > ca:
        rel += "e{}".format(cb-ca)

    if rel != "":
        rel = "_" + rel
    return rel


def get_pip_class(source, sink):
    source_loc, source_base = source.split("_", 1)
    sink_loc, sink_base = sink.split("_", 1)
    if is_denorm(source_base) or is_denorm(sink_base):
        return None
    if source_base.endswith("_SLICE") or source_base.startswith("MUX") or sink_base.endswith("_SLICE"):
        return "slice_internal"
    if sink_base.endswith("_EBR") or source_base.endswith("_EBR"):
        return "ebr_internal"
    if "TEST" in sink_base or "TEST" in source_base:
        return None
    if "ALU" in sink_base or "ALU" in source_base or "MULT" in sink_base or "MULT" in source_base or "PRADD" in sink_base:
        return "dsp_internal"
    if lc_input_re.match(sink_base):
        if lc_output_re.match(source_base):
            return "{}_to_{}".format(source_base[:-1].lower(), sink_base[:-1].lower())
        elif get_span(source_base) is not None:
            return get_span(source_base)  + "_to_" + sink_base[:-1].lower() + format_rel(source_loc, sink_loc)
        elif "HPBX" in source_base:
            return "global_to_" + sink_base[:-1].lower()
        else:
            return None
    elif get_span(sink_base) is not None:
        if lc_output_re.match(source_base):
            return source_base[:-1].lower() + "_to_" + get_span(sink_base) + format_rel(source_loc, sink_loc)
        elif get_span(source_base) is not None:
            return  get_span(source_base) + "_to_" + get_span(sink_base) + format_rel(source_loc, sink_loc)
        elif "HPBX" in source_base:
            return "global_to_" + get_span(sink_base)
        elif "BOUNCE" in source_base:
            return None
        else:
            assert False, (source, sink)
    elif source_base.startswith("LSR") and sink_base.startswith("MUXLSR"):
        return "lsr_to_muxlsr"
    else:
        return None

# Force these pip classes to have a 0 fanout adder for a better solution
zero_fanout_classes = {
    "span0hr_to_ce",
    "span0vt_to_lsr",
    "span1he_to_m",
    "span1he_to_m_w1",
    "span1he_to_span2vn_n1",
    "span1he_to_span6he_e3",
    "span1hw_to_m",
    "span1hw_to_m_w1",
    "span1hw_to_span6hw_w4",
    "span1vn_to_span2vn_n1",
    "span1vn_to_span6he_e3",
    "span1vn_to_span6hw_w3",
    "span2he_to_lsr",
    "span2he_to_lsr_e1",
    "span2he_to_m",
    "span2he_to_span2vn_n1",
    "span2he_to_span2vn_n1e1",
    "span2he_to_span6he_e4",
    "span2hw_to_lsr",
    "span2hw_to_lsr_w1",
    "span2hw_to_m",
    "span2hw_to_m_w1",
    "span2hw_to_span2vn_n1",
    "span2hw_to_span2vn_n1w1",
    "span2hw_to_span6hw_w4",
    "span6vs_to_span1vs_s3",
    "span6vn_to_span6he_e3",
    "span6vn_to_span6he_n3e3",
    "span6vn_to_span6hw_w3",
    "span6vs_to_span2vn_s2",
    "span6vs_to_span6he_e3",
    "span6vs_to_span6hw_s3w3",
    "span1he_to_span1hw",
    "span1vn_to_m_s1",
    "span1vs_to_m",
    "span6he_to_span1he_e3",
    "span2vs_to_c",
    "span0hr_to_m",
}


def force_zero_fanout_pip(name):
    return name.startswith("jf") or name.startswith("jq") or "to_j" in name or name.startswith("q_to_") or name == "slice_internal" or name == "ebr_internal" or name in zero_fanout_classes

# Force these pip classes to have zero delay to avoid linear dependence and unstable results

zero_delay_classes = {
    "ebr_internal",
    "f_to_span1he_e1",
    "f_to_span1hw",
    "f_to_span1vs",
    "f_to_span2vn_n1",
    "f_to_span6hw_w3",
    "jf_to_span1he_e1",
    "jf_to_span1hw",
    "jf_to_span2vn_n1",
    "jq_to_span1hw",
    "jq_to_span6hw_w3",
    "lsr_to_muxlsr",
    "q_to_span1hw",
    "q_to_span1vs",
    "q_to_span2vn_n1",
    "q_to_span6he_e3",
    "q_to_span6hw_w3",
    "span0hl_to_c"
    "span0hl_to_ce",
    "span0hl_to_d",
    "span0hl_to_m",
    "span0vt_to_lsr",
    "span1he_to_span2vn_n1",
    "span1he_to_span6he_e3",
    "span1hw_to_span6hw_w4",
    "span1vn_to_span2vn_n1",
    "span1vn_to_span6he_e3",
    "span1vn_to_span6hw_w3",
    "span2he_to_lsr",
    "span2he_to_lsr_e1",
    "span2he_to_span2vn_n1",
    "span2he_to_span2vn_n1e1",
    "span2he_to_span6he_e4",
    "span2hw_to_lsr",
    "span2hw_to_lsr_w1",
    "span2hw_to_span2vn_n1",
    "span2hw_to_span2vn_n1w1",
    "span2hw_to_span6hw_w4",
    "span2vs_to_d",
    "span2vs_to_span0hr",
    "span2vs_to_span0vb",
    "span6vn_to_span1vs_n3",
    "span6vs_to_span1vs_s3",
    "span6vs_to_span2vn_s2",
    "span6vs_to_span6hw_w3",
    "span6vn_to_span6he_e3",
    "span6vn_to_span6he_n3e3",
    "span6vn_to_span6hw_w3",
    "span6vs_to_span2vn_s2",
    "span6vs_to_span6he_e3",
    "span6vs_to_span6hw_s3w3",
    "span1he_to_span1hw",
    "span6he_to_span1he_e3",

}

def force_zero_delay_pip(name):
    return name in zero_delay_classes