'''
Created on 07-Feb-2021

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

class Count():
    def __init__(self):
        self._soup = None
        self._defaultImageName = None
        pass
    pass

    def _count(self, *args):
        self._initSoup()
        
        n1, n2, imageName, decimalExp = self._getCountArgs(*args)
        if not (isinstance(n1, int) and isinstance(n2, int)):
            raise Exception("Only integers allowed")
            pass

        if n2 == n1:
            raise Exception("Invalid range to count")
            pass

        
        shapeImage = ArithmeticHelper._getImageOrDefault(imageName)
        
        self._countHtmlOutput(n1, n2, shapeImage, decimalExp = decimalExp)

        htmlDisplay = ArithmeticHelper._getHtmlDisplay()   
        if htmlDisplay is None:
            return self._soup.prettify()
         
        return htmlDisplay(self._soup.prettify())

    def _getCountArgs(self, *args):
        n1 = 1
        n2 = 10
        imageName = ArithmeticHelper._DEFAULT_IMAGE_NAME
        argLength = len(args)
        
        if argLength == 1:
            if isinstance(args[0], int):
                n2 = args[0]
                if n2 < 0:
                    n1 = -1
            else:
                imageName = args[0]
            
        if argLength == 2:
            if (isinstance(args[0], int) and isinstance(args[1], int)):
                n1 = args[0]
                n2 = args[1]
            else:
                n2 = args[0]
                imageName = args[1]
            
        if argLength == 3:
            n1 = args[0]
            n2 = args[1]
            imageName = args[2]

        #get max exp-10
        n1, n2, decimalExp = self._getDecimalExp(n1, n2)
        return n1, n2, imageName, decimalExp

    def _getDecimalExp(self, n1, n2):
        decimalExp = 0
        while True:
            expFactor = decimalExp + 1
            if not (n1 / (10 ** expFactor) == n1 // (10 ** expFactor) and 
                n2 / (10 ** expFactor) == n2 // (10 ** expFactor)):
                break
            decimalExp = decimalExp + 1
        
        n1 = n1 // (10 ** decimalExp)
        n2 = n2 // (10 ** decimalExp)
        return n1, n2, decimalExp
        pass

    
    def _countHtmlOutput(self, n1, n2, shapeImage, decimalExp = 0):
        nIncr = 1
        if n2 > n1:
            nRange = self._getLowerAndUpperDisplayBands(n1, n2)
        else:
            nIncr = -1
            nRange = self._getLowerAndUpperDisplayBands(n2-1, n1-1)
            nRange = list(reversed(nRange))
            
        countTag = self._soup.new_tag(ArithmeticHelper._DEFAULT_SECTION_TAG)
        
        for idx in range(nRange[0], nRange[1], nIncr):
            leftTag = self._soup.new_tag(ArithmeticHelper._DEFAULT_ROW_TAG)
            
            shapesTag = self._getShapeSeries(idx, shapeImage)
            leftTag.append(shapesTag)
            
            idxTag = self._soup.new_tag("span")
            idxTag.string = f" {idx * (10 ** decimalExp)}"
            leftTag.append(idxTag)

            countTag.append(leftTag)
        
        if nRange[1] != nRange[2]:
            blankTag = self._soup.new_tag(ArithmeticHelper._DEFAULT_ROW_TAG)
            blankTag.string = '...'
            countTag.append(blankTag)
        
        for idx in range(nRange[2], nRange[3], nIncr):
            rightTag = self._soup.new_tag(ArithmeticHelper._DEFAULT_ROW_TAG)
            
            shapesTag = self._getShapeSeries(idx, shapeImage)
            rightTag.append(shapesTag)
            
            idxTag = self._soup.new_tag("span")
            idxTag.string = f" {idx * (10 ** decimalExp)}"
            rightTag.append(idxTag)

            countTag.append(rightTag)
        
        self._soup.find('body').append(countTag)
        pass
    
    def _getShapeSeries(self, idx, shapeImage):
        firstBatchSize = 10

        shapesTag = self._soup.new_tag("span")
        
        if idx <= 10:
            strPrint =  f"{shapeImage * idx}"
            shapesTag.string = strPrint
            return shapesTag

        remainder = idx % firstBatchSize 
        if remainder == 0:
            remainder = firstBatchSize

        strPrint = f"{shapeImage * firstBatchSize} ... {shapeImage *remainder}"
        shapesTag.string = strPrint
        return shapesTag
        pass

    def _getLowerAndUpperDisplayBands(self, n1, n2):
        firstBatchSize = 5
        n2 = n2 + 1
        if (n2-n1 <= 10 or
            ( abs(n1) <= 10 and abs(n2) <= 10)
            ):
            n1Upper = n2
            n2Lower = n2
            return (n1, n1Upper, n2Lower, n2)

        n1Upper = n1 + 5
        remainder = (n2-1) % 10  
        if remainder == 0:
            n2Lower = n2 - 3
        else:
            n2Lower = n2 - min(3, remainder)
        
        #print(n2, remainder, n2Lower)
        #print(n1, n1Upper, n2Lower, n2)
            
        return (n1, n1Upper, n2Lower, n2)
        pass

    def _initSoup(self):
        self._soup = BeautifulSoup("""
        <html>
            <head></head>
            <body>
            </body>
        </html>
        """, 'html.parser')
        pass
    
