'''
Created on 07-Feb-2021

@author: bthakur
'''
from ._arithmetichelper import ArithmeticHelper
from bs4 import BeautifulSoup
import math


class SumInMemory():
    def __init__(self):
        self._soup = None
        self._defaultImageName = None
        self.tot = 0
        self.nums = []
        self.processedNums = []
        pass
    pass

    def _add(self, *args):
        imageName = [x for x in args if isinstance(x, str)]
        if imageName:
            imageName = imageName[0]
        else:
            imageName = None
        self._setDefaultImageName(imageName)
        
        self.nums = [x for x in args if isinstance(x, int)]
        self._initSoup()
        return self._addBig()

    def _addBig(self):
        self._solve()
        return self._printSolution()
        pass

    def _printSolution(self):
        addTag = self._soup.new_tag(ArithmeticHelper._DEFAULT_SECTION_TAG)

        
        for processedNum in self.processedNums:
            stepTags = self._printStep(processedNum)
            addTag.extend(stepTags)
            pass
        

        self._soup.find('body').append(addTag)
        
        htmlDisplay = ArithmeticHelper._getHtmlDisplay()
        if htmlDisplay is None:
            return self._soup.prettify()
         
        #print(self._soup.prettify())
        return htmlDisplay(self._soup.prettify())
        
        pass
    
    def _printStep(self, processedNum):
        stepTags = []
        prevTot = processedNum.get('prevTot')
        subTot = processedNum.get('subTot')
        
        expandedNums = processedNum.get('expandedNums', None)
        if expandedNums:
            if prevTot != 0:
                expandedNums.insert(0, prevTot)
            stepTags.append(self._getRowTag(expandedNums))
        
        aggregatedNums = processedNum.get('aggregatedNums')
        if subTot != 0:
            aggregatedNums.insert(0, subTot)
        stepTags.append(self._getRowTag(aggregatedNums))

        return stepTags

        print(f"""
        prevTot = {prevTot}
        {expandedNums}
        subTot = {subTot}
        {aggregatedNums}
        """)
        
        pass
    
    def _getRowTag(self, nums):
        rowTag = self._soup.new_tag(ArithmeticHelper._DEFAULT_ROW_TAG)

        for idx, n in enumerate(nums):
            if not (isinstance(n, list)):
                rowTag.append(self._getOperatorTag(n))
                rowTag.append(self._getNumTag(abs(n)))
            else:
                for i, x in enumerate(n):
                    opTag = self._getOperatorTag(x)
                    numTag = self._getNumTag(abs(x))
                    if i == 0:
                        opTag['style'] = "color: red;"
                        numTag['style'] = "color: red;"
                    else:
                        opTag['style'] = "color: blue;"
                        numTag['style'] = "color: blue;"
                        pass
                    rowTag.append(opTag)
                    rowTag.append(numTag)
                    pass
            pass
        
        return rowTag
        pass

    def _getNumTag(self, n):
        numTag = self._soup.new_tag(ArithmeticHelper._DEFAULT_NUM_TAG)
        numTag.string = str(n)
        return numTag
        pass
    
    def _solve(self):
        processedNum = (
                {'nums': self.nums,
                'expandedNums': None,
                'prevTot': 0,
                'subTot': 0,
                'aggregatedNums': self.nums
                }
            )
        self.processedNums.append(processedNum)
        #print(processedNum)

        idx = 0
        while(len(processedNum['aggregatedNums']) > 0):
            processedNum = self._processNumSeries(processedNum)
            self.processedNums.append(processedNum)
            
            #print(f"""processedNum = {processedNum}""")
            
            idx = idx + 1
            if idx > 99:
                break
            pass
        

    def _processNumSeries(self, prevNum):
        nums = prevNum['aggregatedNums']
        prevTot = prevNum['subTot']
        subTot = prevNum['subTot']
        
        highestDecimalExp = self._getHighestDecimalExp(nums)
        #print(f"highestDecimalExp = {highestDecimalExp}")
        
        expandedNums = []
        if highestDecimalExp == 0:
            aggregatedNums= []
            subTot = subTot + sum(nums)
        else:
            for n in nums:
                expandedNums.append(self._expandNumber(n, highestDecimalExp))
            aggregatedNums, subTot = self._aggregatedNumbers(expandedNums, subTot, highestDecimalExp)
            
        processedNum = (
                {'nums': nums,
                'expandedNums': expandedNums,
                'prevTot': prevTot,
                'subTot': subTot,
                'aggregatedNums': aggregatedNums
                }
            )
        return processedNum
        pass

    def _aggregatedNumbers(self, expandedNums, subTot, highestDecimalExp):
        aggregatedNums = []
        if highestDecimalExp == 0:
            #subTot = subTot + sum(expandedNums)
            #return aggregatedNums, subTot
            pass
        
        for n in expandedNums:
            #print(f"n = {n}")
            if not (isinstance(n, list) or isinstance(n, tuple)):
                aggregatedNums.append(n)
                continue
            
            subTot = subTot + n[0]
            if len(n) > 1:
                aggregatedNums.extend(n[1:])
                pass
            #print(f"aggregatedNums = {aggregatedNums}")
            pass
        
        return aggregatedNums, subTot
        pass

    def _setDefaultImageName(self, imageName = None):
        self._defaultImageName = ArithmeticHelper._getImageOrDefault(imageName)
        pass

    def _initSoup(self):
        self._soup = BeautifulSoup("""
        <html>
            <head></head>
            <body>
            </body>
        </html>
        """, 'html.parser')
        pass
    
    def _getHighestDecimalExp(self, nums):
        numLengths = [len(str(abs(n))) for n in nums]
        highestDecimalExp = max(numLengths) - 1
        return highestDecimalExp

    def _expandNumber(self, n, highestDecimalExp):
        expFactor = 10 ** highestDecimalExp
        aAbs = abs(n)
        if (aAbs // expFactor == 0):
            return n
        
        nBig = (aAbs // expFactor) * expFactor
        nBig = int(math.copysign(nBig, n))

        nSmall = aAbs % expFactor
        if nSmall == 0:
            return [nBig]
        nSmall = int(math.copysign(nSmall, n))
        return [nBig, nSmall]
        pass

    def _getOperatorTag(self, n):
        operatorTag = self._soup.new_tag(ArithmeticHelper._DEFAULT_NUM_TAG)
        if n < 0:
            operatorTag.string = " - "
        else:
            operatorTag.string = " + "
            
        return operatorTag
        pass

