from PySide6.QtWidgets import *
from PySide6.QtCore import *
from PySide6.QtGui import *
import os

def getMainWindow():
    # for widget in QApplication.allWidgets():
    #     print(widget)
    for widget in QApplication.topLevelWidgets():
        if isinstance(widget, QMainWindow):
            return widget
    return None

class JumpButton(QPushButton):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.marginsAnim = QPropertyAnimation(self, b'geometry')
        self.marginsAnim.setDuration(300)
        self.marginsAnim.setEasingCurve(QEasingCurve.OutElastic)

    def enterEvent(self,event):
        self.marginsAnim.setDirection(self.marginsAnim.Forward)
        if self.marginsAnim.state() == self.marginsAnim.State.Stopped:
            rect = self.geometry()
            self.marginsAnim.setStartValue(rect)
            rect+=QMargins(0,10,0,0)
            self.marginsAnim.setEndValue(rect)
            if self.isEnabled():self.marginsAnim.start()
        QPushButton.enterEvent(self, event)

    def leaveEvent(self,event):
        self.marginsAnim.setDirection(self.marginsAnim.Backward)
        if self.marginsAnim.state() == self.marginsAnim.State.Stopped and self.isEnabled(): self.marginsAnim.start()
        QPushButton.leaveEvent(self, event)

class ToolButton(QToolButton):
    def __init__(self, *args, **kwargs):
        QToolButton.__init__(self, *args, **kwargs)
        self.tip = QLabel(getMainWindow())
        self.tip.setStyleSheet(" ".join(F"""
            color: rgb(219, 203, 189);
            background-color: rgb(135, 67, 29);
            padding: 10px;
            border: 2px solid #c87941;
            border-radius: 5px;
        """.split()))


        self.opacityFx = QGraphicsOpacityEffect(self.tip)
        self.opacityFx.setOpacity(0)
        self.tip.setGraphicsEffect(self.opacityFx)
        self.opacityAnim = QPropertyAnimation(self.opacityFx, b'opacity')
        self.opacityAnim.setDuration(250)
        self.opacityAnim.setStartValue(0)
        self.opacityAnim.setEndValue(1)

        self.posAnim = QPropertyAnimation(self.tip, b'pos')
        self.posAnim.setDuration(400)
        self.posAnim.setEasingCurve(QEasingCurve.OutElastic)

        self.animGroup = QParallelAnimationGroup(self.tip)
        self.animGroup.addAnimation(self.opacityAnim)
        self.animGroup.addAnimation(self.posAnim)

    def event(self,event):
        t=event.type()
        if t == QEvent.ToolTip:
            return True
        elif t == QEvent.Show or t == QEvent.Leave:
            self.tip.hide()
        elif t == QEvent.Enter:
            self.tip.setText(self.toolTip())
            self.tip.adjustSize()
            self.tip.show()
            gw=self.geometry().width()
            gh=self.geometry().height()
            x=self.pos().x()#+gw+15
            y=self.pos().y()#+gh*1.9

            if self.group().objectName() == 'mainPageGroup':
                x=(x+gw*.8)-self.tip.geometry().width()*.5
                y+=gh+7
                self.posAnim.setStartValue(QPoint(x,y*2))
            else:
                x+=gw+15
                y+=gh*1.9
                self.posAnim.setStartValue(QPoint(x*2,y))
            self.posAnim.setEndValue(QPoint(x,y))

            if self.isEnabled():self.animGroup.start()
        return QToolButton.event(self, event)

class DragDrop(QAbstractItemView):
    dropped = Signal(list)
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)

    def mousePressEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            mData = QMimeData()
            urls=[]
            for item in self.selectedItems():
                if hasattr(item,'file'):
                    urls.append(QUrl.fromLocalFile(item.file))
            if len(urls):
                mData.setUrls(urls)
                drag = QDrag(self)
                drag.setMimeData(mData)
                drag.setHotSpot(event.position().toPoint() - self.rect().topLeft())
                Qt.DropAction = drag.exec(Qt.CopyAction)
        return super().mousePressEvent(event)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls() and event.source() is None:
            event.setDropAction(Qt.CopyAction)
            event.accept()
            links = []
            for url in event.mimeData().urls():
                links.append(str(url.toLocalFile()))
            self.dropped.emit(links)
        else:
            event.ignore()

class ListView(QListWidget,DragDrop):
    def __init__(self, parent=None):
        super().__init__(parent)

    def getItems(self):
        return (self.item(i) for i in range(self.count()))

    def removeItem(self,file):
        for i, item in enumerate(self.getItems()):
            if item.file == file:
                self.takeItem(i)
                break

    def dragEnterEvent(self, event):DragDrop.dragEnterEvent(self, event)
    def dragMoveEvent(self, event):DragDrop.dragMoveEvent(self, event)
    def dropEvent(self, event):DragDrop.dropEvent(self, event)

class PathsList(ListView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.mainWindow = getMainWindow()
        self.menu = QMenu('Paths Menu',self)
        self.menu.setCursor(Qt.PointingHandCursor)
        self.menu.addAction('Add',self.mainWindow.addPluginPath)
        self.menu.addAction('Delete',self.mainWindow.removePluginPath)

    def removeCurrentItem(self):
        for i, item in enumerate(self.getItems()):
            if item == self.currentItem():
                self.takeItem(i)
                return item

    def contextMenuEvent(self,event):
        underMouse = self.itemAt(event.pos())
        if underMouse is None:# Empty area
            [action.setVisible(action.text() == 'Add') for action in self.menu.actions()]
        else:# Item
            [action.setVisible(action.text() == 'Delete') for action in self.menu.actions()]
        self.menu.popup(event.globalPos())
        event.accept()

class TreeView(QTreeWidget,DragDrop):
    droppedQrc = Signal(list)
    droppedImg = Signal(dict)
    droppedModule = Signal(list)
    def __init__(self, parent=None):
        super().__init__(parent)
        self.imgTypes = ['.png' ,'.ico' ,'.jpg' ,'.gif' ,'.svg']
        self.qrcParent = None
        self.prefixParent = None
        self.mainWindow = getMainWindow()

        # icons
        fileIcon = QIcon(":/icons/img/cil-file.png")
        folderIcon = QIcon(":/icons/img/cil-folder-open.png")
        pencilIcon = QIcon(":/icons/img/cil-pencil.png")
        refreshIcon = QIcon(":/icons/img/cil-loop-circular.png")
        imageIcon = QIcon(":/icons/img/cil-satelite.png")
        plusIcon = QIcon(":/icons/img/cil-plus.png")

        # menus
        self.popup = None
        self.tinted = False
        self.menu = QMenu('Menu',self)
        self.menu.setCursor(Qt.PointingHandCursor)

        self.menu.addAction(pencilIcon,'Open',self.open)
        self.menu.addAction(fileIcon,'New Module',self.mainWindow.newModule)
        self.menu.addAction(folderIcon,'Add Module',self.mainWindow.addModuleDialogue)
        self.menu.addAction(plusIcon,'Add Class',self.mainWindow.addClass)
        self.menu.addAction(folderIcon,'Add Images',self.mainWindow.addImagesDialogue)
        self.menu.addAction(folderIcon,'Add Prefix',self.mainWindow.addPrefix)
        self.menu.addAction(folderIcon,'Add Qrc',self.mainWindow.addQrc)
        self.menu.addAction(folderIcon,'Create Qrc',self.mainWindow.createQrc)
        self.menu.addAction(pencilIcon,'Tint',self.tint)
        self.menu.addAction(pencilIcon,'Multiply',self.multiply)
        self.menu.addAction(refreshIcon,'Restore',self.restore)
        self.menu.addAction(imageIcon,'Make ICO',self.makeIco)
        self.menu.addAction(imageIcon,'Set as project icon',self.setIcon)
        self.menu.addAction(fileIcon,'Rename',self.rename)
        self.menu.addAction(fileIcon,'Delete',self.delete)

    def mousePressEvent(self, event):
        QTreeWidget.mousePressEvent(self, event)
        si = self.selectedImages()
        sm = self.selectedModules()
        if si:selectedItems = si
        elif sm:selectedItems = sm
        else:return True
        if event.buttons() == Qt.LeftButton:
            mData = QMimeData()
            urls=[]
            for item in selectedItems:
                if hasattr(item,'file'):
                    urls.append(QUrl.fromLocalFile(item.file))
            if len(urls):
                mData.setUrls(urls)
                drag = QDrag(self)
                drag.setMimeData(mData)
                drag.setHotSpot(event.position().toPoint() - self.rect().topLeft())
                Qt.DropAction = drag.exec(Qt.CopyAction)

    def removeTopLevelItem(self,item,col=0):
        self.takeTopLevelItem(self.indexOfTopLevelItem(item))

    def makeIco(self):self.mainWindow.makeIco(self.selectedImages())
    def tint(self):self.mainWindow.tintImagesDlg(self.selectedImages(),0)
    def multiply(self):self.mainWindow.tintImagesDlg(self.selectedImages(),1)
    def restore(self):self.mainWindow.restoreImages(self.selectedImages())
    def setIcon(self):self.mainWindow.setIcon(self.currentItem())

    def open(self):
        if self.popup == 'module':self.mainWindow.openModule(self.currentItem())
        if self.popup == 'class':self.mainWindow.openClass(self.currentItem())

    def delete(self):
        if self.popup == 'qrc': self.mainWindow.deleteQrcs(self.selectedQrcs())
        if self.popup == 'prefix': self.mainWindow.deletePrefixes(self.selectedPrefixes())
        if self.popup == 'image': self.mainWindow.deleteImages(self.selectedImages())
        if self.popup == 'module': self.mainWindow.deleteModules()

    def rename(self):
        if self.popup == 'qrc': self.mainWindow.renameQrc(self.currentItem())# rename prefix
        if self.popup == 'prefix': self.mainWindow.addPrefix(add=False)# rename prefix
        if self.popup == 'image': self.mainWindow.renameImage(self.currentItem())# rename prefix
        if self.popup == 'module': self.mainWindow.renameModule(self.currentItem())# rename prefix

    def enableActions(self,actions=None,state=True):
        for action in self.menu.actions():
            if actions:
                if action.text() not in actions:
                    continue
            action.setEnabled(state)

    def visibleActions(self,actions=None,state=True):
        for action in self.menu.actions():
            if actions:
                if action.text() not in actions:
                    continue
            action.setVisible(state)

    def selectedQrcs(self):
        """returns selected qrc files"""
        if self.objectName() == 'qrcTree':
            return [i for i in self.selectedItems() if i.type()==0]

    def selectedPrefixes(self):
        """returns selected prefixes"""
        if self.objectName() == 'qrcTree':
            return [i for i in self.selectedItems() if i.type()==1]

    def selectedImages(self):
        """returns selected images"""
        if self.objectName() == 'qrcTree':
            return [si for si in self.selectedItems() if si.type()==2]

    def selectedModules(self):
        """returns selected modules"""
        if self.objectName() == 'moduleTree':
            return [i for i in self.selectedItems() if i.type()==0]

    def selectedClasses(self):
        """returns selected classes"""
        if self.objectName() == 'moduleTree':
            return [i for i in self.selectedItems() if i.type()==1]

    def contextMenuEvent(self,event):
        underMouse = self.itemAt(event.pos())
        self.visibleActions(state=0)
        if self.objectName() == 'qrcTree':
            if underMouse:
                if underMouse.type()==2:# image
                    self.popup = 'image'
                    self.visibleActions([
                        'Restore',
                        'Rename',
                        'Delete',
                    ])
                    # if any image is tinted enable undo tint
                    self.enableActions(['Restore'],0)
                    for img in self.selectedImages():
                        if '.ico' in img.file:
                            self.visibleActions(['Set as project icon'])
                        else:
                            self.visibleActions([
                                'Tint',
                                'Multiply',
                                'Make ICO'
                            ])
                        if img.tinted:
                            self.enableActions(['Restore'])
                elif underMouse.type()==1:# prefix
                    self.popup = 'prefix'
                    self.visibleActions([
                        'Add Images',
                        'Rename',
                        'Delete'
                    ])
                elif underMouse.type()==0:# qrc
                    self.popup = 'qrc'
                    self.visibleActions([
                        'Add Prefix',
                        'Rename',
                        'Delete'
                    ])
            else:# empty
                self.popup = None
                self.visibleActions(['Add Qrc','Create Qrc'])
        else:# moduleTree
            if underMouse:
                if underMouse.type()==1:# class
                    self.popup = 'class'
                    if self.mainWindow.hasSublime():
                        self.visibleActions(['Open'])
                    else:
                        event.ignore()
                        return
                elif underMouse.type()==0:# module
                    self.popup = 'module'
                    self.visibleActions([
                        'Add Class',
                        'Rename',
                        'Delete'
                    ])
                    if self.mainWindow.hasSublime():
                        self.visibleActions(['Open'])
            else:# empty
                self.popup = None
                self.visibleActions([
                    'New Module',
                    'Add Module'
                ])
        self.menu.popup(event.globalPos())
        event.accept()

    def getUrls(self,event):
        return [str(url.toLocalFile()) for url in event.mimeData().urls()]

    def urlType(self,event):
        if not event.mimeData().hasUrls():return None
        extList = []
        for url in self.getUrls(event):
            ext = os.path.splitext(url)[1]
            if ext not in extList and ext not in self.imgTypes and len(extList): return 'multiple'
            extList.append(ext)
        if all([e=='.py' for e in extList]):return 'module'
        if all([e=='.qrc' for e in extList]):return 'qrc'
        if all([e in self.imgTypes for e in extList]):return 'img'
        # if all([not len(e) for e in extList]):return 'folder'
        print(extList)
        return None

    def dragEnterEvent(self, event):DragDrop.dragEnterEvent(self, event)

    def dragMoveEvent(self, event):
        urlType = self.urlType(event)
        if not urlType or event.source():
            event.ignore()
            return
        elif (urlType=='img' or urlType=='qrc') and self.objectName() != 'qrcTree':
            event.ignore()
            return
        elif urlType=='module' and self.objectName() != 'moduleTree':
            event.ignore()
            return
        else:
            event.setDropAction(Qt.CopyAction)
            event.accept()

        hovered = self.itemAt(event.position().toPoint())
        # De-Select items
        for item in self.selectedItems():item.setSelected(0)
        shouldSelect = []

        if hovered:
            if self.objectName() == 'qrcTree':
                self.mainWindow.ui.statusbar.showMessage('')
                self.prefixParent=None
                if hovered.type()==2:# hovered image
                    self.prefixParent = hovered.parent()
                    self.qrcParent = hovered.parent().parent()
                elif hovered.type()==1:# hovered prefix
                    self.prefixParent=hovered
                    self.qrcParent=hovered.parent()
                else:# hovered qrc
                    self.qrcParent=hovered

                shouldSelect.append(self.qrcParent)
                if self.prefixParent:
                    shouldSelect.append(self.prefixParent)
                else:
                    self.mainWindow.ui.statusbar.showMessage('A new prefix will be created!')
        else:# hovering empty area
            if self.topLevelItem(0):# not empty
                if self.objectName() == 'moduleTree':
                    shouldSelect.append(self.topLevelItem(0))
                elif self.objectName() == 'qrcTree':
                    if urlType == 'img':
                        qrc_file = self.topLevelItem(0)
                        shouldSelect.append(qrc_file)
                        self.qrcParent=qrc_file

                        first_prefix = qrc_file.child(0)
                        if first_prefix:
                            shouldSelect.append(first_prefix)
                            self.prefixParent=first_prefix
                        else:
                            self.prefixParent=None
                            self.mainWindow.ui.statusbar.showMessage('A new prefix will be created!')

        [si.setSelected(1) for si in shouldSelect]
        # DragDrop.dragMoveEvent(self, event)

    def dropEvent(self, event):
        urlType = self.urlType(event)
        if urlType and event.source() is None:
            event.setDropAction(Qt.CopyAction)
            event.accept()
            if urlType=='img':
                self.droppedImg.emit({
                'qrc':self.qrcParent,
                'prefix':self.prefixParent,# if None we need to create a 'newPrefix'
                'imgs':self.getUrls(event)
                })
            if urlType=='qrc':self.droppedQrc.emit(self.getUrls(event))
            if urlType=='module':self.droppedModule.emit(self.getUrls(event))
        else:
            event.ignore()




