# Libraries
from os import path, remove, listdir
from PIL import Image   # pip install Pillow

# constants
PATH_WA = "../../apps/"                                                     # path before editors
PATH_HELP = "/main/resources/help/images/"                                  # path after editor directory
NAME_CSS = "sprite.css"
NAME_SMB_CSS = "symbols.css"
pathEditors = ["documenteditor", "presentationeditor", "spreadsheeteditor","common"]     # names of editor directories

# sprites names
cssClassNames = ["big","icon"]
cssClassSmbName = "smb"
# directories names
spriteDirNames = ["big", "icons"]
spriteSmbDirName ="symbols"

# Class for image
class ImgClass:
    _x=0
    _y=0  
    
    def __init__(self, adr):
        self._imgFile = Image.open(adr)
        self._imgName = path.splitext(path.basename(adr))[0]
        
    def get_width(self):
        return self._imgFile.size[0]

    def get_height(self):
        return self._imgFile.size[1]

    def get_nameImage(self):
        return self._imgName
    
    def get_image(self):
        return self._imgFile

    #operators definition
    # operator '<'
    def __lt__(self, other):
        if(self.get_height() == other.get_height()):
            return self.get_width() < other.get_width()
        else:
            return self.get_height() < other.get_height()

    # operator '>'
    def __gt__(self, other):
        if(self.get_height() == other.get_height()):
            return self.get_width() > other.get_width()
        else:
            return self.get_height() > other.get_height()

    # operator '!='
    def __ne__(self, other):
        return (self.get_height() != other.get_height() or self.get_width() != other.get_width())

    # operator '=='
    def __eq__(self, other):
        return (self.get_height() == other.get_height() and self.get_width() == other.get_width())
   
# class fit
# _down :Fit is bottom cell
# _right :Fit is right cell
class Fit:
    def __init__(self, x, y, w: int, h: int):
        # true if the rectangle has a image
        self.used = False

        # Rectangle for cell for image
        self.x = x
        self.y = y
        self.width = w
        self.height = h

    def set_down(self, fdown):
        self._down = fdown
    
    def get_down(self):
        return self._down

    def set_right(self, fright):
        self._right = fright

    def get_right(self):
        return self._right
    
    def __str__(self):
        strFit = "\tused:" + str(self.used) +"\n"
        strFit += "\tx:" + str(self.x) +"\n"
        strFit += "\ty:" + str(self.y) +"\n"
        strFit += "\twidth:" + str(self.width) +"\n"
        strFit += "\theight:" + str(self.height) +"\n"
        return strFit
    
#Image with fit
class Block(ImgClass):

    def __init__(self, adr):
        super().__init__(adr)
    
    def get_x(self):
        return self._x

    def get_y(self):
        return self._y

    def set_x(self, val):
        self._x = val

    def set_y(self, val):
        self._y = val
    
    def set_fit(self, fit):
        self.fit = fit
        self.set_x(fit.x)
        self.set_y(fit.y)

#binary-tree
class GrowingPacker:
    def __init__(self, imgAdrArr):
        self.blocks =[]
        for adr in imgAdrArr:
            self.blocks.append(Block(adr))
        self.blocks.sort(reverse=True)

    # create tree
    def fit(self):
        self.ln = len(self.blocks)
        if(self.ln > 0):
            w, h = self.blocks[0].get_width(), self.blocks[0].get_height() 
        else:
            w, h = (0, 0)

        # start rectangle for general cell
        self.root = Fit(0, 0, w, h)
        
        for n in range(self.ln):
            node = self.findNode(self.root, self.blocks[n].get_width(), self.blocks[n].get_height())
            if (node != None):
                self.blocks[n].set_fit(self.splitNode(node, self.blocks[n].get_width(), self.blocks[n].get_height()))
            else:
                self.blocks[n].set_fit(self.growNode(self.blocks[n].get_width(), self.blocks[n].get_height()))
    
    def findNode(self, rootN, w, h):
        if (rootN.used):
            node = self.findNode(rootN.get_right(), w, h)
            if (node != None):
                return   node 
            else:
                return self.findNode(rootN.get_down(), w, h)
        elif (w <= rootN.width and h <= rootN.height):
            return rootN
        else:
            return None
    
    def splitNode(self, node, w, h):
        node.used = True
        node.set_down(Fit(node.x, node.y + h, node.width, node.height - h))
        node.set_right(Fit(node.x + w, node.y, node.width - w, h))
        return node
    
    def growNode(self, w, h):
        canGrowDown = (w <= self.root.width)
        canGrowRight = (h <= self.root.height)
        
        shouldGrowRight = (canGrowRight and self.root.height >= self.root.width + w)
        shouldGrowDown = (canGrowDown and self.root.width >= self.root.height + h)
              
        
        if (shouldGrowRight):
            return self.growRight(w, h)
        elif (shouldGrowDown):
            return self.growDown(w, h)
        elif (canGrowRight):
            return self.growRight(w, h)
        elif (canGrowDown):
            return self.growDown(w, h)
        else:
            return None
        
    def growRight (self, w, h):
        rootN =self.root
        
        self.root =  Fit(0, 0, rootN.width + w, rootN.height)
        self.root.used = True
        self.root.set_down(rootN)
        self.root.set_right (Fit(rootN.width, 0,  w, rootN.height))        
        node = self.findNode(self.root, w, h)        
        if (node != None ):
            return self.splitNode(node, w, h)
        else:
            return None

    def growDown (self, w, h):
        rootN = self.root
        
        self.root = Fit(0, 0, rootN.width, rootN.height + h)
        self.root.used = True
        self.root.set_down( Fit(0, rootN.height, rootN.width, h))
        self.root.set_right (rootN)

        node = self.findNode(self.root, w, h)
        if (node != None):
            return self.splitNode(node, w, h)
        else:
            return None
        
# create sprite
def createSprite (dirName, adrSprite, cssClassName, urlDirImage):
    imgAdrArr = listdir(adrDir+"src/"+dirName)
    for i in range(len(imgAdrArr)):
        imgAdrArr[i] = adrDir +"src/"+dirName +"/" + imgAdrArr[i]
    gp = GrowingPacker(imgAdrArr)
    gp.fit()
    spriteFile = open(adrSprite,'a')
    img = Image.new("RGBA", (gp.root.width, gp.root.height), (0,0,0,0))
    
    strCSS = "." + cssClassName +"{\n\tbackground-image: url(" + urlDirImage + dirName + ".png);\n"
    strCSS += "\tbackground-repeat: no-repeat;\n\tdisplay: inline-block;\n}\n\n"
    
    spriteFile.write(strCSS)  
    for i in range(len(gp.blocks)):
        img.paste(gp.blocks[i].get_image(), (gp.blocks[i].get_x(), gp.blocks[i].get_y()))
        strCSS = "." + cssClassName +"-"+ gp.blocks[i].get_nameImage() + " {\n"
        strCSS += "\tbackground-position: "+ str(-gp.blocks[i].get_x())+"px "+ str(-gp.blocks[i].get_y())+"px;\n"
        strCSS += "\twidth: "+ str(gp.blocks[i].get_width())+"px;\n\theight: "+ str(gp.blocks[i].get_height())+"px;\n}\n\n"
        spriteFile.write(strCSS)
    spriteFile.close()
    
    imgPath = adrDir + dirName +".png";
    img.save(imgPath,'PNG')
    
    print('created sprite', imgPath)
    print('created styles', adrSprite)

#crreate all sprites
for index_editor in range(len(pathEditors)):
    adrDir = PATH_WA + pathEditors[index_editor] + PATH_HELP
    print('start for', adrDir)

    spriteName = adrDir + NAME_CSS
    isCommon = (pathEditors[index_editor]=="common")

    if(isCommon):
        spriteName = adrDir + NAME_SMB_CSS

    if(path.exists(spriteName)):
        remove(spriteName)

    if(isCommon):
        createSprite(spriteSmbDirName, spriteName, cssClassSmbName, "../../../../../common/main/resources/help/images/")
    else:
        spriteFile = open(spriteName,'a')
        spriteFile.write('@import "../../../../../common/main/resources/help/images/symbols.css";\n\n')
        spriteFile.close()
        for index_spr in range(len(spriteDirNames)):  
            createSprite(spriteDirNames[index_spr], spriteName, cssClassNames[index_spr],"../images/")
            
    print("done!\n")