# coding=cp1251
'''
Created on 01.08.2011

@author: Scondo
'''
import datetime
import re
import tb_callback
from tb69.caller import caller
#from tb69.core import Match
tb69 = caller(None)

kosgu_pair = (("3", "4"), ("5", "6"), ("7", "8"))


def FormatNumber(Number, Accuracy):
    res = format(Number, ",." + str(Accuracy) + "f").replace(",", "'")
    return res


class Transaction(object):
    """Проводка ТБ6.9
    стандартные атрибуты:
        deb
        cre
        sum
        sumval
        val
        ana
        kolana
        kol
        """
    def __init__(self, jur=None):
        self.jur = jur
        self.comment = ""
        self.ana = {}

    def Invert(self):
        """Меняет знак всех численных значений проводки"""
        if hasattr(self, "sum"):
            self.sum = -1 * self.sum
        if hasattr(self, "kol"):
            self.kol = -1 * self.kol
        if hasattr(self, "sumval"):
            self.sumval = -1 * self.sumval

    def CompParam(self, other):
        cmp_ = cmp(self.deb, other.deb)
        if cmp_:
            return cmp_
        cmp_ = cmp(self.cre, other.cre)
        if cmp_:
            return cmp_
        if hasattr(self, "sumval") and not hasattr(other, "sumval"):
            return 1
        if hasattr(other, "sumval") and not hasattr(self, "sumval"):
            return -1

        if hasattr(self, "kolana"):
            if hasattr(other, "kolana"):
                cmp_ = cmp(self.kolana.keys()[0], other.kolana.keys()[0])
                if cmp_:
                    return cmp_
                cmp_ = cmp(self.kolana.values()[0], other.kolana.values()[0])
                if cmp_:
                    return cmp_
            else:
                return -1
        vocs = self.ana.keys()
        vocs.sort()
        vocs1 = other.ana.keys()
        vocs1.sort()
        cmp_ = cmp(vocs, vocs1)
        if cmp_:
            return cmp_
        cmp_ = 0
        for voc in vocs:
            cmp_ = cmp(self.ana[voc], other.ana[voc])
            if cmp_:
                break
        return cmp_

    def TransLike(self, tranList):
#        if len(tranList) < 1000:
            for tran in tranList:
                if not self.CompParam(tran):
                    return tran

    def AddValues(self, other):
        if hasattr(self, "sum"):
            self.sum += other.sum
        if hasattr(self, "sumval"):
            self.sumval += other.sumval
        if hasattr(self, "kol"):
            self.kol += other.kol

    def FlatAna(self, order=None, nokol=None):
        res = u""
        vocs = self.ana.keys()
        if hasattr(self, 'kolana'):
            vocs.append(self.kolana.keys()[0])
        vocs.sort()
        if order is not None:
            for voc in order:
                #Удаляем элементы, для которых определен порядок
                if voc in vocs:
                    vocs.remove(voc)
            #Для которых не определен порядок - добавляем в конец
            order.extend(vocs)
            vocs = order
        for voc in vocs:
            if hasattr(self, 'kolana') and self.kolana.keys()[0] == voc:
                res = res + u" " + self.kolana.values()[0]
                if not nokol:
                    res = res + u":" + FormatNumber(self.kol, 0)
            else:
                res = res + u" " + self.ana[voc]
        res = res[1:]
        return res

    #@property
    def Key(self):
        if hasattr(self, '_key'):
            return self._key
        key = self.deb + self.cre
        key = key + (self.val if hasattr(self, 'val') else '')
        key = key + self.FlatAna(None, True)
        self._key = key
        return key

    def ParseSum(self, param):
        if "^" in param:
            self.sumval = float(param.split("^")[0].replace("'", ""))
            self.val = param.split("^")[1]
            if not self.val in self.jur.accuracies:
                if tb69 != None:
                    self.jur.accuracies[self.val] = tb69.execute("Accuracy", (self.val, ))
                else:
                    self.jur.accuracies[self.val] = 4
        else:
            self.sum = float(param.replace("'", ""))

    def ParseString(self, line, checksig = False):
        #Parse transaction string
        #0 1              2 3         4(2)                              5(3)                               6(4)
        #: 1'000.0000^usd : 10'000.00 074.0000.0000000.000.2.101.11.310 074.0000.0000000.000.2.101.11.310 {}

        # если есть валюта - число параметров больше на 2.
        valmod = 2 if (":" in line[1:].split("{")[0]) else 0
        myparams = line[1:].split(None, 3 + valmod)
        self.deb = myparams[1 + valmod]
        self.cre = myparams[2 + valmod]
        if len(myparams) > (3 + valmod):
            ana = myparams[3 + valmod].strip().strip("{").strip("}")
        else:
            ana = ""
        self.ParseSum(myparams[0])
        if valmod == 2:
            self.ParseSum(myparams[2])
        #Парсим аналитику
        signs = ana.split()
        if checksig:
            sigmask = tb69.execute("Раб.Бюджет2005.СписокСправ",(self.deb+"|"+self.cre,0,""),'str')
        for sign in signs:
            if not checksig or MatchMulti(sign, sigmask):
                if ":" in sign:
                    kolsig = sign.split(":")
                    self.kolana = {}
                    kolana = kolsig[0].split(".", 1)
                    self.kolana[kolana[0]] = kolsig[0]
                    self.kol = float(kolsig[1].replace("'", ""))
                else:
                    ana_ = sign.split(".", 1)
                    self.ana[ana_[0]] = sign


def kosgu_switch(Acc):
    res = Acc
    for pair in kosgu_pair:
        if re.match(pair[0] + "..", Acc[-3:]):
            res = Acc[:-3] + pair[1] + Acc[-2:]
            continue
        if re.match(pair[1] + "..", Acc[-3:]):
            res = Acc[:-3] + pair[0] + Acc[-2:]
    return res

from tb69.krok import MatchMulti


class TBJur(object):
    def __init__(self):
        self.accuracies = {}

    def ParseJur(self, filename, checksig = False):
        """Парсер текстового журнала
        Возвращает список кортежей (дата, [проводки])
        """
        f = open(filename)
        #if tb69 != None:
        #    tb69.execute("Trace", ('OPEN', ))
        res = [(None, [])]
        it = f.readlines()
        it = enumerate ( it )
        for cnt, line in it:
            #if tb69 != None and (cnt % 1000) == 0:
            #    tb69.execute("Trace", (str(cnt), ))

            if line[0] == ":":
                line=line.decode('cp1251')
                trans = Transaction(self)
                trans.ParseString(line, checksig)
                #Добавляем в последнюю запись новую проводку
                #print res
                res[-1][1].append(trans)
            else:
                try:
                    date = datetime.datetime.strptime(line.strip(), "%d.%m.%Y").date()
                    res.append((date, []))
                except ValueError:
                    pass
        #if tb69 != None:
        #    tb69.execute("Trace", ('ALL READ', ))
        f.close()
        self.Jur = res
        if type(tb69.cb) == tb_callback.callback:
            tb69.execute("Break")
        return None

    def StripAna(self, AccMask, AnaDel_):
        #AnaDel_=ConvMaskA(AnaDel)
        #AnaDel_=(AnaDel)
        for (date, JurD) in self.Jur:
            for trans in JurD:
                if AccMask == '*' or AccMask == '' or MatchMulti(trans.deb, AccMask) or MatchMulti(trans.cre, AccMask):
                    for ana in trans.ana.keys():
                        if ana in AnaDel_:
                            del(trans.ana[ana])
                    if hasattr(trans, "kolana"):
                        if trans.kolana.keys()[0] in AnaDel_:
                            del(trans.kolana)
                            del(trans.kol)

    def StripAnaAll(self, AccMask, AnaKeep_):
        #AnaKeep_=ConvMaskA(AnaKeep)
        for (date, JurD) in self.Jur:
            for trans in JurD:
                if AccMask == '*' or AccMask == '' or MatchMulti(trans.deb, AccMask) or MatchMulti(trans.cre, AccMask):
                    for ana in trans.ana.keys():
                        if not ana in AnaKeep_:
                            del(trans.ana[ana])
                    if hasattr(trans, "kolana"):
                        if not trans.kolana.keys()[0] in AnaKeep_:
                            del(trans.kolana)
                            del(trans.kol)

    def StripNonFull(self, AccMask, AnaList):
        #AnaKeep_=ConvMaskA(AnaKeep)
        for (date, JurD) in self.Jur:
            for trans in JurD:
                if AccMask == '*' or AccMask == '' or MatchMulti(trans.deb, AccMask) or MatchMulti(trans.cre, AccMask):
                    for ana in AnaList:
                        if not ana in trans.ana and trans.kolana.keys()[0] != ana:
                            trans.ana = ''
                            del(trans.kolana)
                            del(trans.kol)

    def PartAcc(self, AccTyp):
        if AccTyp == '':
            if type(tb69.cb) == tb_callback.callback:
                tb69.execute("Break")
            return
        if AccTyp.find('+') == 0:
            for (date, JurD) in self.Jur:
                for trans in JurD:
                    trans.deb = tb69.execute(u'Раб.Бюджет2005.ВзятьКодСчета', (trans.deb, '', AccTyp, False), 'str')
        else:
            for (date, JurD) in self.Jur:
                for trans in JurD:
                    accparts = [tb69.execute(u'Раб.Бюджет2005.ВзятьКодСчета', (trans.deb, '', acctyp, False), 'str') for acctyp in AccTyp.split('+')]
                    trans.deb = '.'.join(accparts)
        if type(tb69.cb) == tb_callback.callback:
            tb69.execute("Break")

    def DropAcc(self):
        for (date, JurD) in self.Jur:
            for trans in JurD:
                trans.deb = '00'
                trans.cre = '00'

    def NormAcc(self):
        for (date, JurD) in self.Jur:
            for trans in JurD:
                if trans.deb == "00" or trans.deb == "00Z":
                    deb = kosgu_switch(trans.cre)
                    (trans.deb, trans.cre) = (deb, trans.deb)
                    trans.Invert()

    def deNormAcc(self):
        for(date, JurD) in self.Jur:
            for trans in JurD:
                do = False
                if hasattr(trans, "sum") and trans.sum != 0:
                    do = trans.sum < 0
                if hasattr(trans, "sumval") and trans.sumval != 0:
                    do = trans.sumval < 0
                if do:
                    deb = kosgu_switch(trans.cre)
                    cre = kosgu_switch(trans.deb)
                    (trans.deb, trans.cre) = (deb, cre)
                    trans.Invert()

    def FoldJur(self, keep0=False):
        Jur_new = []
        for (date, JurD) in self.Jur:
            if JurD:
                JurD_new = {}
                #if tb69 != None:
                #tb69.execute("Trace", (str(len(JurD))+"__"+str(len(JurD_new)), ) )
                while JurD:
                    trans = JurD.pop(0)
                    #if tb69 != None and (len(JurD) % 100) == 0:
                    #tb69.execute("Trace", (str(len(JurD))+"__"+str(len(JurD_new)), ) )
                    trans2 = JurD_new.get(trans.Key())
                    if trans2:
                        trans2.AddValues(trans)
                        if not keep0 and \
                            round(trans2.sum, 4) == 0 and \
                            (not hasattr(trans2, 'sumval') or trans2.sumval == 0) and \
                            (not hasattr(trans2, 'kol') or trans2.kol == 0):
                            del JurD_new[trans2.Key()]
                    else:
                        JurD_new[trans.Key()] = trans
                Jur_new.append((date, JurD_new.values()))
        self.Jur = Jur_new
        if type(tb69.cb) == tb_callback.callback:
            tb69.execute("Break")

    def WriteJur(self, FileName):
        """Сохранение журнала в файл"""
        File = open(FileName, "w")
        for (date, JurD) in self.Jur:
            if not JurD:
                continue
            if date == None:
                File.write("1.1.100")
            else:
                File.write(date.strftime("%d.%m.%Y"))
            File.write("\n")
            JurD.sort(key = Transaction.Key)
            for trans in JurD:
                if hasattr(trans, "sumval"):
                    #Сумма в валюте
                    File.write(": " + FormatNumber(trans.sumval, self.accuracies[trans.sumval]).rjust(17) + "^" + trans.val + " ")
                #Сумма в рублях
                File.write(": " + (FormatNumber(trans.sum, 2).rjust(17)))
                try:
                    File.write("   " + trans.deb.encode("cp1251")  + "   " + trans.cre.encode("cp1251") )
                    File.write(" {" + trans.FlatAna().encode("cp1251") + "}")
                    File.write(" --" + trans.comment)
                    File.write("\n")
                except UnicodeDecodeError, UnicodeEncodeError:
                    print trans.FlatAna(), type(trans.FlatAna())
                    print trans.comment, type(trans.comment)
                    raise
        File.close()
        if type(tb69.cb) == tb_callback.callback:
            tb69.execute("Break")


    def DayTrans(self, day):
        allj = [JurD for date, JurD in self.Jur if date == day]
        if allj:
            return allj[0]
        else:
            return allj
