class ProjectTreeView(QTreeView):

    def __init__(self, parent):
        super(ProjectTreeView, self).__init__(parent)
        self.mSettingsSection = 'browser'
        self.mExpandPaths = []
        self._projectModel = None  # ProjectModel

    def setProjectModel(self, model):
        self._projectModel = model

    def setModel(self, model):
        super(ProjectTreeView, self).setModel(model)
        self.restoreState()

    def showEvent(self, event):
        if self.model() is not None:
            self.restoreState()
        super(ProjectTreeView, self).showEvent(event)

    def hideEvent(self, event):
        if self.model() is not None:
            self.saveState()
        super(ProjectTreeView, self).hideEvent(event)

    def saveState(self):
        expandedPaths = self.expandedPathsList(QModelIndex())
        QSettings().setValue(self.expandedPathsKey(), expandedPaths)

    def restoreState(self):
        self.mExpandPaths = QSettings().value(self.expandedPathsKey())

        if self.mExpandPaths:
            expandIndexSet = {}  # QSet<QModelIndex>
            for path in self.mExpandPaths:
                expandIndex = ProjectModel.findPath(self.model(), path, Qt.MatchStartsWith)
                if expandIndex.isValid():
                    modelIndex = self.browserModel().findPath(path, Qt.MatchExactly)
                    if modelIndex.isValid():
                        ptr = self.browserModel().dataItem(modelIndex)
                        if ptr is not None and (ptr.capabilities2() & QgsDataItem.Capability.Collapse):
                            parentIndex = self.model().parent(expandIndex)
                            if parentIndex.isValid():
                                expandIndexSet.insert(parentIndex)
                        else:
                            expandIndexSet.insert(expandIndex)

                for expandIndex in expandIndexSet:
                    self.expandTree(expandIndex)
        else:
            index = ProjectModel.findPath(self.model(), "favourites:")
            self.expand(index)

    def expandTree(self, index):
        if self.model() is None:
            return
        self.expand(index)
        parentIndex = self.model().parent(index)
        if parentIndex.isValid():
            self.expandTree(parentIndex)

    def treeExpanded(self, index):
        if self.model() is None:
            return False
        if not self.isExpanded(index):
            return False
        parentIndex = self.model().parent(index)
        if parentIndex.isValid():
            return self.treeExpanded(parentIndex)
        return True

    def hasExpandedDescendant(self, index):
        if self.model() is None:
            return False
        for i in range(0, self.model().rowCount()):
            childIndex = self.model().index(i, 0, index)
            if self.isExpanded(childIndex):
                return True
            if self.hasExpandedDescendant(childIndex):
                return True
        return False

    def rowsInserted(self, parentIndex, start, end):
        super(ProjectTreeView, self).rowsInserted(parentIndex, start, end)
        if self.model() is None:
            return
        if not self.mExpandPaths:
            return
        parentPath = self.model().data(parentIndex, ProjectModel.PathRole)
        self.mExpandPaths.remove(parentPath)
        if not self.treeExpanded(parentIndex):
            for path in self.mExpandPaths:
                if path.startswith(parentPath + '/'):
                    self.mExpandPaths.remove(path)
            return
        for i in range(start, end):
            childIndex = self.model().index(i, 0, parentIndex)
            childPath = self.model().data(childIndex, ProjectModel.PathRole)
            escapedChildPath = childPath
            escapedChildPath.replace('|', "\\|")
            if self.mExpandPaths.contains(childPath) or self.mExpandPaths.indexOf(QRegExp("^" + escapedChildPath + "/.*")) != -1:
                self.expand(childIndex)

    def expandedPathsKey(self):
        return '/' + self.mSettingsSection + "/expandedPaths"

    def expandedPathsList(self, index):
        paths = []
        if self.model() is None:
            return paths
        for i in range(0, self.model().rowCount(index)):
            childIndex = self.model().index(i, 0, index)
            if self.isExpanded(childIndex):
                childrenPaths = self.expandedPathsList(childIndex)
                if childrenPaths:
                    paths.append(childrenPaths)
                else:
                    paths.append(self.model().data(childIndex, ProjectModel.PathRole))
        return paths