
from collections import Iterable

from vigenere import VigenereCipher
from utilities import *


# look up table
mapTbl = [
[0x7, 0x5, 0x0, 0x4, 0x2, 0x3, 0xb, 0x6, 0xa, 0x8, 0x9, 0xd, 0xc, 0xf, 0xe, 0x1], 
[0x3, 0x8, 0xd, 0xa, 0xc, 0xe, 0xf, 0xb, 0x7, 0x6, 0x4, 0x5, 0x1, 0x2, 0x0, 0x9], 
[0x4, 0x0, 0x3, 0x1, 0xb, 0xa, 0x8, 0x5, 0x9, 0xd, 0xc, 0xe, 0xf, 0x6, 0x7, 0x2], 
[0x9, 0xe, 0x7, 0xc, 0x6, 0x4, 0x5, 0xd, 0x1, 0x0, 0x2, 0x3, 0xb, 0x8, 0xa, 0xf], 
[0x1, 0x3, 0xa, 0x2, 0x8, 0x9, 0xd, 0x0, 0xc, 0xe, 0xf, 0x7, 0x6, 0x5, 0x4, 0xb], 
[0xe, 0x6, 0x5, 0x7, 0x1, 0x0, 0x2, 0xf, 0x3, 0xb, 0xa, 0x8, 0x9, 0xc, 0xd, 0x4], 
[0x2, 0xa, 0x9, 0xb, 0xd, 0xc, 0xe, 0x3, 0xf, 0x7, 0x6, 0x4, 0x5, 0x0, 0x1, 0x8], 
[0xd, 0xf, 0x6, 0xe, 0x4, 0x5, 0x1, 0xc, 0x0, 0x2, 0x3, 0xb, 0xa, 0x9, 0x8, 0x7], 
[0xb, 0x9, 0xc, 0x8, 0xe, 0xf, 0x7, 0xa, 0x6, 0x4, 0x5, 0x1, 0x0, 0x3, 0x2, 0xd], 
[0x0, 0xb, 0x8, 0x3, 0x9, 0xd, 0xc, 0x2, 0xe, 0xf, 0x7, 0x6, 0x4, 0x1, 0x5, 0xa], 
[0x8, 0xc, 0xf, 0xd, 0x7, 0x6, 0x4, 0x9, 0x5, 0x1, 0x0, 0x2, 0x3, 0xa, 0xb, 0xe], 
[0x5, 0x2, 0xb, 0x0, 0xa, 0x8, 0x9, 0x1, 0xd, 0xc, 0xe, 0xf, 0x7, 0x4, 0x6, 0x3], 
[0x6, 0x1, 0x2, 0x5, 0x3, 0xb, 0xa, 0x4, 0x8, 0x9, 0xd, 0xc, 0xe, 0x7, 0xf, 0x0], 
[0xc, 0x7, 0x4, 0xf, 0x5, 0x1, 0x0, 0xe, 0x2, 0x3, 0xb, 0xa, 0x8, 0xd, 0x9, 0x6], 
[0xa, 0xd, 0xe, 0x9, 0xf, 0x7, 0x6, 0x8, 0x4, 0x5, 0x1, 0x0, 0x2, 0xb, 0x3, 0xc], 
[0xf, 0x4, 0x1, 0x6, 0x0, 0x2, 0x3, 0x7, 0xb, 0xa, 0x8, 0x9, 0xd, 0xe, 0xc, 0x5]];


class Vigenere333Cipher(VigenereCipher):

    # ////////////////////////
    # PUBLIC
    # ////////////////////////

    def encrypt(self, message, key):
        self.message = message
        self.key = key

        self.translateSymbol = encryptSymbol

        if type(message) == str:
            self.getHex = getHexString
        else:
            self.getHex = getPaddedHexString
            
        return self.translate()

    def decrypt(self, message, key):
        self.message = message if isinstance(message, Iterable) else [message]
        self.key = key if isinstance(key, Iterable) else [key]

        self.translateSymbol = decryptSymbol
        self.getHex = getPaddedHexString
        return ''.join([getChar(h) for h in self.translate()])

    # ////////////////////////
    # OVERRIDEN
    # ////////////////////////

    def getTranslatedSymbol(self, symbol):
        hexChar = self.getHex(symbol)        
        hexKeyChar = getPaddedHexString(self.key[self.key_index])
                
        ph = getHigherHex(hexChar)
        pl = getLowerHex(hexChar)
        
        kh = getHigherHex(hexKeyChar)
        kl = getLowerHex(hexKeyChar)

        return self.translateSymbol(ph, pl, kh, kl)

# ////////////////////////
# HELPERS
# ////////////////////////

def encryptSymbol(ph, pl, kh, kl):
    a = mapTbl[ph][kl]
    b = mapTbl[pl][kh]
    
    ch = mapTbl[b][kl]
    cl = mapTbl[a^b][kh]
    
    return convertToHex(ch, cl)

def decryptSymbol(ch, cl, kh, kl):
    b = findRow(ch, kl)
    c = findRow(cl, kh)
    a = c ^ b

    pl = findRow(b, kh)
    ph = findRow(a, kl)

    return convertToHex(ph, pl)

def findRow(value, key):
    for index, row in enumerate(mapTbl):
        if row[key] == value:
            return int(convertToHex(0, index), 16) 


