"""
"""

from copy import deepcopy
from sage.all_cmdline import SymmetricGroup, floor
import warnings
from . import structures
from .enums import *

def css(cuts_set):
    string = ''.join(sorted([str(cut[0]) for cut in cuts_set]))
    if len(string) != len(cuts_set): print("something went very wrong here...")
    return string

def cuts(framework, sigma):
    sigma = deepcopy(framework.one_row(sigma))
    n = framework.n
    pmN = structures.set_plus_minus(n)
    G = SymmetricGroup(pmN)
    c_string = f'({",".join(str(i) for i in list(range(1, n+1)))})({",".join(str(i) for i in list(range(-n, 0)))})'
    c = G(c_string)
    sigma = list(sigma)
    return set([ 
        tuple(([i+1,c(i+1)])) for i in range(len(sigma)) 
        if (sigma[c(i + 1)-1] != c(sigma[i]))
    ])

def __all_canonical_inversions(framework, num_regions=None):
    if not framework.oriented:
        raise NotImplementedError(f"not yet implemented for {str(framework)}")
    if framework.symmetry not in {SYMMETRY.circular, SYMMETRY.linear}:
        raise NotImplementedError(f"not implemented for framework with {str(framework.symmetry)} symmetry")
    n = framework.n
    G = framework.genome_group()
    if num_regions == 1:
        return {G(f'({i},-{i})') for i in range(1, n+1)} if framework.oriented else {}
    elif num_regions == 2:
        if framework.oriented:
            perms = {G(f'({i},{-1*(i+1)})({-1*i},{i+1})') for i in range(1,n)}
            if framework.symmetry is not SYMMETRY.linear:
                perms.union({G(f'({n},-1)(-{n},1)')})
        else:
            perms = {G(f'({i},{i+1})') for i in range(1,n)} 
            if framework.symmetry is not SYMMETRY.linear:
                perms.union({G(f'(1,{n})')})
        return perms
    elif num_regions == None: # Return all inversions up to length floor(n/2)
        up_to_length = floor(framework.n/2)
        if framework.oriented: 
            perms = set()
            for permutation in G:
                cycle_type = list(permutation.cycle_type())
                if set(cycle_type)=={1,2} and list(cycle_type).count(2)<=up_to_length and len(cuts(framework, permutation))==2:
                    perms.add(permutation)
            if framework.symmetry is SYMMETRY.linear:
                perms = {perm for perm in perms if not ('1' in str(perm) and str(n) in str(perm))}
            return perms
        else:
            raise NotImplementedError(f"not yet implemented for {framework}")
    else:
        raise NotImplementedError(f"inversions of length {num_regions} not yet implemented")
    
def __two_region_adjacent_transposition_reps(framework):
    if not framework.oriented or framework.symmetry != SYMMETRY.circular:
        raise NotImplementedError(f"not yet implemented for {str(framework)}")
    G = framework.genome_group()
    return { G('(-2,-1)(1,2)'), G('(-2,1,2,-1)'), G('(-2,-1,2,1)'), G('(-2,2)(-1,1)') }

def double_coset(framework, perm):
    Z = framework.symmetry_group()
    return { d1 * perm * d2 for d1 in Z for d2 in Z }

def conjugacy_class(framework, perm):
    Z = framework.symmetry_group()
    return { d.inverse() * perm * d for d in Z }

def single_coset(framework, perm):
    Z = framework.symmetry_group()
    return { perm * d for d in Z }

def __representatives(framework, set_of_permutations, classes=CLASSES.double_cosets):
    if not framework.oriented:
        raise NotImplementedError(f"not yet implemented for {str(framework)}")
    Z = framework.symmetry_group()
    perms = {x for x in set_of_permutations}
    cosets = []
    while len(perms):
        perm = perms.pop()
        if classes is CLASSES.double_cosets:
            coset = double_coset(framework, perm)
        elif classes is CLASSES.conjugacy_classes:
            coset = conjugacy_class(framework, perm)
        elif classes is CLASSES.cosets:
            coset = single_coset(framework, perm)
        for element in coset:
            perms.discard(element)
        cosets.append(coset)
    reps = set()
    for coset in cosets:
        reps.add(sorted(list(coset), key=lambda x: (sum(set(x.cycle_type())), list(x.cycle_type()).count(2), str(x)) )[0])
    return reps

def all_inversions_representatives(framework, num_regions=None):
    return __representatives(framework, __all_canonical_inversions(framework, num_regions=num_regions), classes=CLASSES.double_cosets)

def all_adjacent_transpositions_representatives(framework, num_regions=None):
    if num_regions == 2:
        return __two_region_adjacent_transposition_reps(framework)
    else:
        raise NotImplementedError(f"model not yet implemented")
