from operator import xor


def detect_secstruct_clash(i, j, secstruct):
    """
    Detect if an EC pair (i, j) is geometrically
    impossible given a predicted secondary structure

    Based on direct port of the logic implemented in
    choose_CNS_constraint_set.m from original pipeline,
    lines 351-407.

    Use secstruct_clashes() to annotate an entire
    table of ECs.

    Parameters
    ----------
    i : int
        Index of first position
    j : int
        Index of second position
    secstruct : dict
        Mapping from position (int) to secondary
        structure ("H", "E", "C")

    Returns
    -------
    clashes : bool
        True if (i, j) clashes with secondary
        structure
    """

    # extract a secondary structure substring
    # start and end are inclusive
    def _get_range(start, end):
        return "".join(
            [secstruct[pos] for pos in range(start, end + 1)]
        )

    def _all_equal(string, char):
        return string == len(string) * char

    # get bigger and smaller of the two positions
    b = max(i, j)
    s = min(i, j)

    # if pair too distant in primary sequence, do
    # not consider for clash
    if b - s >= 15:
        return False

    # get secondary structure in range between pairs
    secstruct_string = _get_range(s, b)

    # part 1: check for clashes based on alpha helices
    # first check for helix between them, or both in a helix
    # (or either one directly next to helix)
    if _all_equal(_get_range(s + 1, b - 1), "H"):
        return True
    # of if just one of them is in a helix
    elif xor(secstruct[s] == "H", secstruct[b] == "H"):
        h2 = "H" * (b - s - 1)
        h3 = "H" * (b - s - 2)
        if h2 in secstruct_string:
            if b - s > 6:
                return True
        elif h3 in secstruct_string:
            if b - s > 11:
                return True

    # part 2: check for clashes based on beta strands
    if _all_equal(_get_range(s + 1, b - 1), "E"):
        return True
    elif _all_equal(_get_range(s + 2, b - 2), "E"):
        if b - s > 8:
            return True

    if xor(secstruct[s] == "E", secstruct[b] == "E"):
        e2 = "E" * (b - s - 1)
        e3 = "E" * (b - s - 2)
        e4 = "E" * (b - s - 3)

        if e2 in secstruct_string:
            return True
        elif e3 in secstruct_string:
            return True
        elif e4 in secstruct_string:
            if b - s > 8:
                return True

    return False