"""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):
    """Vrati zpravu, ve ktere jsou znaky dane abecedy nahrazenypodle daneho
    slovniku."""
    pole = []
    for char in zprava:
        if char in slovnik:
            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 jakobsen(zprava, ref):
    """Pro danou sifrovanou zpravu vrati substitucni slovnik odvozeny s pomoci
    Jakobsenova algoritmu s danou referencni tabulkou."""
    slovnik = dict(zip(poradi_dle_frekvence(zprava, ref.abeceda), ref.poradi))
    tabulka = nova_tabulka(
                  substituce(zprava, slovnik),
                  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.poradi, 2):
            slovnik[x], slovnik[y] = slovnik[y], slovnik[x]
            nova_vzdalenost = vzdalenost(
                     nova_tabulka(
                         substituce(zprava, slovnik),
                         ref.abeceda),
                     ref.tabulka,
                     ref.abeceda)
            if  nova_vzdalenost < vzdal:
                vzdal = nova_vzdalenost
            else:
                slovnik[x], slovnik[y] = slovnik[y], slovnik[x]
    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()
    slovnik = jakobsen(ocesat(zprava, mezery), ref)
    return '\n'.join([''.join(substituce(zprava, slovnik)),
                      ' '.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())
