"""Modul pro praci s Jakobsenovym algoritmem."""

from itertools import combinations
import pickle
from ocesavac import ocesat
from spolecne import MABECEDA

def nova_tabulka(zprava, abc=MABECEDA):
    """Vraci tabulku cetnosti bigramu ve zprave nad danou abecedou."""
    tabulka = dict()
    for i in abc:
        tabulka[i] = dict()
        for j in abc:
            tabulka[i][j] = 0
    for i in range(len(zprava) - 1):
        tabulka[zprava[i]][zprava[i+1]] += 1
    celkem = sum(sum(tab2.values()) for tab2 in tabulka.values())
    for i in abc:
        for j in abc:
            tabulka[i][j] /= float(celkem)
    return tabulka

def vzdalenost(tab1, tab2, abc=MABECEDA):
    """Vraci soucet strednich kvadratickych odchylek pro dve tabulky cetnosti
    bigramu nad danou abecedou."""
    rozdil = 0
    for i in abc:
        for j in abc:
            rozdil += abs(tab1[i][j] - tab2[i][j])
    return rozdil

def substituce(zprava, slovnik, abc=MABECEDA):
    """Vrati zpravu, ve ktere jsou znaky dane abecedy nahrazenypodle daneho
    slovniku."""
    pole = []
    for char in zprava:
        if char in abc:
            pole.append(slovnik[char])
        else:
            pole.append(char)
    return pole

def poradi_dle_frekvence(zprava, abc=MABECEDA):
    """Vrati znaky dane abecedy v poradi podle frekvence v dane zprave."""
    freq = dict()
    for char in abc:
        freq[char] = 0
    for char in zprava:
        if char in abc:
            freq[char] += 1
    return sorted(freq.keys(), key=freq.get, reverse=True)

class reference:
    pass

def prohod(sl_a, sl_b, tabulka):
    """Vrati tabulku s prohozenou dvojici sloupcu a radku."""
    tabulka[sl_a], tabulka[sl_b] = tabulka[sl_b], tabulka[sl_a]
    for c in tabulka.keys():
        tabulka[c][sl_a], tabulka[c][sl_b] = tabulka[c][sl_b], tabulka[c][sl_a]
    return

def jakobsen(zprava, ref):
    """Pro danou sifrovanou zpravu vrati substitucni slovnik odvozeny s pomoci
    Jakobsenova algoritmu s danou referencni tabulkou."""
    slovnik = dict(zip(ref.abeceda, ref.abeceda))
    tabulka = nova_tabulka(zprava, ref.abeceda)
    vzdal = vzdalenost(tabulka, ref.tabulka, ref.abeceda)

    vzdal_old = vzdal + 1
    while vzdal_old > vzdal:
        vzdal_old = vzdal
        for (x, y) in combinations(ref.abeceda, 2):
            prohod(x, y, tabulka)
            nova_vzdalenost = vzdalenost(tabulka, ref.tabulka, ref.abeceda)
            if  nova_vzdalenost < vzdal:
                vzdal = nova_vzdalenost
                slovnik[x], slovnik[y] = slovnik[y], slovnik[x]
            else:
                prohod(x, y, tabulka)
    return slovnik
    
def desifruj(zprava, mezery=True):
    """Vrati vysledek pokusu dekodovat zpravu Jakobsenovym algoritmem."""
    if mezery:
        soubor = open('ref')
    else:
        soubor = open('bref')
    ref = pickle.load(soubor)
    soubor.close()
    zprava = substituce(zprava, dict(zip(poradi_dle_frekvence(zprava, ref.abeceda), ref.poradi)), ref.abeceda)
    slovnik = jakobsen(ocesat(zprava, mezery), ref)
    return '\n'.join([''.join(substituce(zprava, slovnik, ref.abeceda)),
                      ' '.join(ref.abeceda),
                      ' '.join([slovnik[c] for c in ref.abeceda])])

def __test():
    print desifruj('Mxbhfxn cbfbhinpr, wnx fv f gvzuyr cbenqv.'.upper())

def __test2():
    print desifruj('Sel pes do lesa a potkal dlazebni kostku. Chtelo by to jeste o neco delsi test, to tedy jo.'.upper())
