#!/usr/bin/env python
# -*- coding: utf8 -*-

from GenericFile import *
import BitVector
import re

class APNFile (GenericFile):

    name = "Algebraiczna Postać Normalna"
    extension = 'APN'

    def __init__(self, content=None):
        GenericFile.__init__(self, content)
#        self.args = 0
        
        if content is not None:
            self.init_from_content(content)

    def _fp_get_possible_elements(self, len):
        import tpi_dict
        elements = tpi_dict.tpi_alphabet[:len]
        return elements
    
    def _fp_build_string(self, elements, string="", start=-1):
        for x in xrange(len(elements)):
            if not elements[x] in string and (len(string)+1) < len(elements) and x > start:
                s = string + str(elements[x])
                self._fp_combinations.append([s,x])
        for e in self._fp_combinations:
            if len(e[0]) < len(elements) and len(e[0]) > len(string):
                self._fp_build_string(elements, e[0], e[1])
    
    def _fp_build_combinations(self):
        elements = self._fp_get_possible_elements(self.args)
        self._fp_build_string(elements)
        self._fp_combinations.append(["".join(elements),0])
        self._fp_combinations = [elem[0] for elem in self._fp_combinations]
        print self._fp_combinations
    
    def _fp_get_func_value(self, bits=None):   
        """oblicza wartość funkcji dla zapalonych bitów określonych w bits"""
        bt = BitVector.BitVector(size=self.args)
        if not bits == None:
            for b in bits:
                bt[int(b)-1]=1
        return self.fptab[str(bt.intValue())]
    
    def _fp_get_all_perms_with_components(self, components):
        comp = []
        for c in self._fp_combinations:
            if c in components:
                comp.append(c)
        return comp
    
    def _fp_components_val(self, components):
        comp = self._fp_get_all_perms_with_components(components)
        toret = self._fp_map['0'][1]
        for c in comp:
            if not self._fp_map.has_key(c):
                self._fp_map[c] = self._fp_get_func_value(c)
            toret = toret ^ self._fp_map[c][1]\
            
    def init_from_content(self, content):
        
        self.fptab = {}
        self.in_alphabet = 2;
        self.out_alphabet = 2;
        self._fp_combinations = []
        """
        size = BitVector.BitVector(intVal=len(content))
        for bit in xrange(len(size)-1, -1, -1):
            if size[bit] == 1:
                break
            self.args+=1
        """
        print "args", str(self.args)
        for x in xrange(len(content)):
            self.fptab[str(x)] = [str(BitVector.BitVector(intVal=x, size=self.args)), content[x]]
        self._fp_build_combinations()
        self._fp_map = {}
        self._fp_map['0'] = self._fp_get_func_value()
        for combination in self._fp_combinations:
            self._fp_components_val(combination)
        print "_fp_map", str(self._fp_map)
        self._apn = []
        for val in self._fp_map.keys():
            if self._fp_map[val][1] == 1:
                self._apn.append(val)
        self._function = self._apn

    def parse_body(self, content):
        self._function = []

        if not re.compile('^([\da-zA-Z],){' + str(self.args-1) \
                + '}[\da-zA-Z]').match(content[0]):
            #TODO: Exception
            pass

        for implicant in content[0].split(','):
            self._function.append( \
                    [self.char_to_value(char.strip()) \
                            for char in implicant.strip()])

    def prepare_body(self):
        result = ""
        
        # Fixed by modified GenericFile constructor.
        """
        for implicant in self._function:
            result += (self.value_to_char(value) \
                    for value in implicant) \
                    + ','

            return result[:-1]
        """
        return ", ".join(self._function)

    # args to jest lista bitów
    def calculate_function(self, args):
        result = 0

        # Sprawdzam, czy mamy a0, które nie odpowiada żadnej zmiennej,
        # tylko liczbie 1. Jak jest, to od razu dodaję do wyniku
        # i wywalam z listy, żeby reszta była już tylko indeksami.
        # self._function to po prostu zapis wartościami kolejnych
        # impikantów, czyli zamiast A jest 10, etc...
        if self._function[0] == 0:
            result = 1
            tmp_fun = self._function[1:]
        else:
            tmp_fun = self._function

        # Dla każdego implikanta w funkcji wykonuję mnożenie wszystkich
        # odpowiednich x-ów. Tak na prawdę starczy, żeby choć jeden był
        # zerem i cały iloczyn się zeruję, dlatego jako wart. pocz.
        # przyjmuję 1.
        for implicant in tmp_fun:
            implicant_value = 1
            for index in implicant:
                implicant_value *= args[index-1]

            # No i wreszcie do wyniku xoruję wartość implikanta.
            result ^= implicant_value

        return result

