from collections import namedtuple

MT = namedtuple("MT", "inicial aceitação rejeição transição")

def imprime(situação):
    palavra_esq, estado, palavra_dir = situação
    L = len(palavra_esq) + len(palavra_dir)

    posição_cabeça = (2*len(palavra_esq))+6

    linha_cima = '─┬'*(L+5) + '─'
    prefixo = '…│❑│❑'

    posfixo = '│❑│❑│…'
    linha_baixo = '─┴'*(L+5) + '─'

    conteúdo = ''
    for s in (palavra_esq + palavra_dir):
        if s == 'B':
            s = '❑'
        conteúdo += f'│{s}'

    print(linha_cima)
    print(f'{prefixo}{conteúdo}{posfixo}')
    print(linha_baixo)
    print(f'{" "*posição_cabeça}⬑{estado}')


def passo(situação, transição):
    palavra_esq, estado, palavra_dir = situação
    # palavra_esq = palavra_esq.lstrip('B')
    # palavra_dir = palavra_dir.rstrip('B')
    if not palavra_dir:
        símbolo = 'B'
    else:
        símbolo = palavra_dir[0]
    novo_símbolo, novo_estado, direção = transição(símbolo, estado)
    esq = {'←', 'e'}
    dir = {'→', 'd'}
    if direção in esq:
        if palavra_esq:
            último_esq = palavra_esq[-1]
        else:
            último_esq = 'B'
        return (palavra_esq[:-1],
                novo_estado,
                (último_esq + novo_símbolo + palavra_dir[1:]))
    elif direção in dir:
        return palavra_esq + novo_símbolo, novo_estado, palavra_dir[1:]
    else:
        # cabeça fica parada
        return palavra_esq, novo_estado, novo_símbolo + palavra_dir[1:]


def executa(palavra_inicial, máquina, passo_a_passo=True):
    estado_inicial, terminais_aceitação, terminais_rejeição = máquina[:3]
    if isinstance(máquina[-1], dict):
        transição = transição_dic(máquina[-1])
    else:
        transição = máquina[-1]
    situação = ('', estado_inicial, palavra_inicial)
    passos = 0
    while True:
        passos += 1
        print(f'{passos}.')
        imprime(situação)
        if situação[1] in terminais_aceitação:
            print("Fim da execução: aceitação")
            return
        if situação[1] in terminais_rejeição:
            print("Fim da execução: rejeição")
            return
        if passo_a_passo:
            input("Pressione <enter> para continuar...")
        try:
            situação = passo(situação, transição)
        except Exception:
            print("TELA AZUL(não há transição válida)")
            return


def transição_dic(D):
    return lambda s, e: D[(s, e)]


# ============
# = EXEMPLOS =
# ============


dic_ancn = {
    ('0', 'procura_fronteira')    : ('0', 'procura_fronteira', '→'),
    ('1', 'procura_fronteira')    : ('1', 'procura_0_não_marcado', '←'),

    ('X', 'procura_0_não_marcado'): ('X', 'procura_0_não_marcado', '←'),
    ('0', 'procura_0_não_marcado'): ('X', 'procura_1_não_marcado', '→'),
    ('B', 'procura_0_não_marcado'): ('B', 'tudo_X?', '→'),

    ('X', 'procura_1_não_marcado'): ('X', 'procura_1_não_marcado', '→'),
    ('1', 'procura_1_não_marcado'): ('X', 'procura_0_não_marcado', '←'),

    ('X', 'tudo_X?')              : ('X', 'tudo_X?', '→'),
    ('B', 'tudo_X?')              : ('B', 'sucesso', '→'),
}

MT_ancn = MT(inicial='procura_fronteira', aceitação={'sucesso'}, rejeição=set(), transição=dic_ancn)


def delta_ex1(x, q):
    if x == 'B' and q == 'q_I':
        return ('B', 'q_I', '→')


MT_ex1 = MT(inicial='q_I', aceitação=set(), rejeição=set(), transição=delta_ex1)


def delta_ex2(simbolo, estado):
    if simbolo == 'x' and estado == 'q_I':
        return ('x', 'q_I', '→')
    if simbolo == 'y' and estado == 'q_I':
        return ('y', 'q_F', '⟳')


MT_ex2 = MT(inicial='q_I', aceitação={'q_F'}, rejeição=set(), transição=delta_ex2)

MT_rejeita_se_tem_0 = MT(
    inicial   = 'inicial',
    aceitação = {'aceita'},
    rejeição  = {'rejeita'},
    transição = {
        ('0', 'inicial'): ('0', 'rejeita', '⟳'),
        ('1', 'inicial'): ('1', 'inicial', '→'),
        ('B', 'inicial'): ('B', 'aceita', '⟳'),
    }
)


MT_par = MT(
    inicial   = 'inicial',
    aceitação = {'aceita'},
    rejeição  = {'rejeita'},
    transição = {
        ('0', 'inicial')  : ('0', 'inicial', '→'),
        ('1', 'inicial')  : ('1', 'inicial', '→'),
        ('B', 'inicial')  : ('B', 'le_ultimo', '←'),
        ('0', 'le_ultimo'): ('0', 'aceita', '⟳'),
        ('1', 'le_ultimo'): ('1', 'rejeita', '⟳'),
        ('B', 'le_ultimo'): ('B', 'rejeita', '⟳'),
    }
)


MT_0n1n2n = MT(
    inicial   = 'procura0',
    aceitação = {'aceita'},
    rejeição  = {'rejeita'},
    transição = {
        ('B', 'procura0')     : ('B', 'aceita', '⟳'),
        
        ('0', 'procura0')     : ('Z', 'procura1', '→'),
        ('Z', 'procura0')     : ('Z', 'procura0', '→'),
        ('U', 'procura0')     : ('U', 'procura0', '→'),
        ('D', 'procura0')     : ('D', 'procura0', '→'),
        
        ('1', 'procura0')     : ('1', 'rejeita', '⟳'),
        ('2', 'procura0')     : ('2', 'rejeita', '⟳'),
        
        ('0', 'procura1')     : ('0', 'procura1', '→'),
        ('Z', 'procura1')     : ('Z', 'procura1', '→'),
        ('U', 'procura1')     : ('U', 'procura1', '→'),
        ('1', 'procura1')     : ('U', 'procura2', '→'),
        
        ('B', 'procura1')     : ('B', 'rejeita', '⟳'),
        ('2', 'procura1')     : ('2', 'rejeita', '⟳'),
        ('D', 'procura1')     : ('D', 'rejeita', '⟳'),

        ('1', 'procura2')     : ('1', 'procura2', '→'),
        ('U', 'procura2')     : ('U', 'procura2', '→'),
        ('D', 'procura2')     : ('D', 'procura2', '→'),
        
        ('2', 'procura2')     : ('D', 'volta_início', '←'),

        ('B', 'volta_início') : ('B', 'procura0', '→'),
        
        ('0', 'volta_início') : ('0', 'volta_início', '←'),
        ('1', 'volta_início') : ('1', 'volta_início', '←'),
        ('2', 'volta_início') : ('2', 'volta_início', '←'),
        ('Z', 'volta_início') : ('Z', 'volta_início', '←'),
        ('U', 'volta_início') : ('U', 'volta_início', '←'),
        ('D', 'volta_início') : ('D', 'volta_início', '←'),})
