import inspect
import shutil
import sys
import os
from typing import Optional

from PyQt5.QtCore import Qt, QUrl,QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QTreeWidget, QTreeWidgetItem, QMenu, QFileDialog, \
    QMessageBox, QInputDialog, QLineEdit,QPushButton,QHBoxLayout
from PyQt5.QtWebEngineWidgets import *
import ftplib
from FtpUitl import FtpOper
import FtpConfig


class FtpManage(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        # 连接到FTP服务器
        self.ftpOper = FtpOper()
        self.ftpOper.ftp.connect(FtpConfig.ftpHost, FtpConfig.ftpPort)
        self.ftpOper.ftp.login(FtpConfig.ftpUsername, FtpConfig.ftpPassword)
        self.fileItem = None
        self.ftpItem = None
        self.setupUi()
        #self.setContextMenuPolicy(Qt.CustomContextMenu)  # 右键菜单
        #self.customContextMenuRequested.connect(self.rightClickMenu)
        self.createTempDir()

    def setupUi(self):
        current_directory = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) #获取当前路径
        # 构建布局
        self.mainLayout = QVBoxLayout()
        self.setLayout(self.mainLayout)
        self.layoutH = QHBoxLayout()  # 按钮布局
        self.layoutV = QVBoxLayout()  # 树结构的布局
        self.mainLayout.addLayout(self.layoutH)
        self.mainLayout.addLayout(self.layoutV)  # 为窗体添加布局
        self.resize(600, 500)
        self.setWindowTitle("文件服务器")
        self.setWindowIcon(QIcon(os.path.join(current_directory + "\\icon", "logo.png")))
        # 功能按钮区域
        self.uploadFileButton = QPushButton(self)
        self.uploadFileButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件上传.png")))
        self.uploadFileButton.setIconSize(QSize(32, 32))
        self.uploadFileButton.setFixedSize(36,36)
        self.uploadFileButton.setToolTip("上传文件")
        self.uploadFileButton.clicked.connect(self.actionUploadHandler)
        self.layoutH.addWidget(self.uploadFileButton)
        self.uploadFolderButton = QPushButton( self)
        self.uploadFolderButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件夹上传.png")))
        self.uploadFolderButton.setIconSize(QSize(32, 32))
        self.uploadFolderButton.setFixedSize(36, 36)
        self.uploadFolderButton.setToolTip("上传文件夹")
        self.uploadFolderButton.clicked.connect(self.actionUploadDirHandler)
        self.layoutH.addWidget(self.uploadFolderButton)
        self.downloadButton = QPushButton(self)
        self.downloadButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件下载.png")))
        self.downloadButton.setIconSize(QSize(32, 32))
        self.downloadButton.setFixedSize(36, 36)
        self.downloadButton.setToolTip("下载")
        self.downloadButton.clicked.connect(self.actionDownloadHandler)
        self.layoutH.addWidget(self.downloadButton)
        self.createDirButton = QPushButton(self)
        self.createDirButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "新建文件夹.png")))
        self.createDirButton.setIconSize(QSize(32, 32))
        self.createDirButton.setFixedSize(36, 36)
        self.createDirButton.setToolTip("新建目录")
        self.createDirButton.clicked.connect(self.actionCreateHandler)
        self.layoutH.addWidget(self.createDirButton)
        self.renameButton = QPushButton(self)
        self.renameButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件重命名.png")))
        self.renameButton.setIconSize(QSize(32, 32))
        self.renameButton.setFixedSize(36, 36)
        self.renameButton.setToolTip("重命名")
        self.renameButton.clicked.connect(self.actionRenameHandler)
        self.layoutH.addWidget(self.renameButton)
        self.deleteButton = QPushButton(self)
        self.deleteButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件删除.png")))
        self.deleteButton.setIconSize(QSize(32, 32))
        self.deleteButton.setFixedSize(36, 36)
        self.deleteButton.setToolTip("删除")
        self.deleteButton.clicked.connect(self.actionDeleteHandler)
        self.layoutH.addWidget(self.deleteButton)
        self.modelViewButton = QPushButton(self)
        self.modelViewButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "模型查看.png")))
        self.modelViewButton.setIconSize(QSize(32, 32))
        self.modelViewButton.setFixedSize(36, 36)
        self.modelViewButton.setToolTip("查看模型")
        self.modelViewButton.clicked.connect(self.action3DHandler)
        self.layoutH.addWidget(self.modelViewButton)
        self.openFileButton = QPushButton(self)
        self.openFileButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件查看.png")))
        self.openFileButton.setIconSize(QSize(32, 32))
        self.openFileButton.setFixedSize(36, 36)
        self.openFileButton.setToolTip("查看文件")
        self.openFileButton.clicked.connect(self.actionFileOpenHandler)
        self.layoutH.addWidget(self.openFileButton)
        # 目录树结构区
        self.treeWidget = QTreeWidget()  # QTreeWidget组件定义
        self.treeWidget.headerItem().setText(0, "列表")  # FTP服务器节点树
        self.treeWidget.headerItem().setText(1, "类型")  # 节点对应的类型：文件，文件夹
        self.treeWidget.headerItem().setText(2, "路径")  # 当前节点在FTP服务器中的路径，方便文件操作时获取服务器中路径，为隐藏列
        self.treeWidget.setColumnWidth(0, 375)  # 给第1列设置列宽
        self.rootItem = QTreeWidgetItem()
        self.rootItem.setText(0, str(self.ftpOper.ftp.host))  # 给根节点增加文本
        self.treeWidget.addTopLevelItem(self.rootItem)  # 增加根节点
        self.layoutV.addWidget(self.treeWidget)  # 将组件添加到布局中
        self.treeWidget.setColumnHidden(2, True)  # 隐藏路径列
        #self.treeWidget.setStyleSheet("QTreeView::item {border:0.5px solid gray; border-left-color: transparent; border-right-color: transparent;}")

    # 右键菜单
    # def rightClickMenu(self, pos):
    #     try:
    #         self.currentItem = self.treeWidget.currentItem()
    #         self.contexMenu = QMenu()
    #         if (self.currentItem.text(1) == '文件' or self.currentItem.text(1) == '文件夹'):
    #             self.actionFileUpload = self.contexMenu.addAction('上传文件')
    #             self.actionFileUpload.triggered.connect(self.actionUploadHandler)
    #             self.actionDirUpload = self.contexMenu.addAction('上传文件夹')
    #             self.actionDirUpload.triggered.connect(self.actionUploadDirHandler)
    #             self.actionDownload = self.contexMenu.addAction('下载')
    #             self.actionDownload.triggered.connect(self.actionDownloadHandler)
    #             self.actionDirCreate = self.contexMenu.addAction('新建目录')
    #             self.actionDirCreate.triggered.connect(self.actionCreateHandler)
    #             self.actionRename = self.contexMenu.addAction('重命名')
    #             self.actionRename.triggered.connect(self.actionRenameHandler)
    #             self.actionDelete = self.contexMenu.addAction('删除')
    #             self.actionDelete.triggered.connect(self.actionDeleteHandler)
    #         else:
    #             self.actionDirCreate = self.contexMenu.addAction('新建目录')
    #             self.actionDirCreate.triggered.connect(self.actionCreateHandler)
    #         if (self.currentItem.text(1) == '文件'):
    #             self.action3D = self.contexMenu.addAction('查看模型')
    #             self.action3D.triggered.connect(self.action3DHandler)
    #             self.actionFileOpen = self.contexMenu.addAction('查看文件')
    #             self.actionFileOpen.triggered.connect(self.actionFileOpenHandler)
    #         self.contexMenu.exec_(self.mapToGlobal(pos))
    #         self.contexMenu.show()
    #
    #     except Exception as e:
    #         print(e)

    # 文件上传操作
    def actionUploadHandler(self):
        if self.treeWidget.currentItem():
            self.currentItem = self.treeWidget.currentItem()
            if self.currentItem.text(1) == '文件':  # 如果选中文件节点则获取此节点的父节点
                self.ftp_path = self.currentItem.parent().text(2)
                self.ftpItem = self.currentItem.parent()
            elif self.currentItem.text(1) == '文件夹':
                self.ftp_path = self.currentItem.text(2)
                self.ftpItem = self.currentItem
            file_paths, _ = QFileDialog.getOpenFileNames(None, '选择文件', '', 'All Files (*)')
            if len(file_paths) > 0:
                for file_path in file_paths:
                    self.file_name = os.path.basename(file_path)
                    self.ftpOper.uploadfile(file_path, self.ftp_path)
                    self.fileItem = QTreeWidgetItem()
                    self.fileItem.setText(0, self.file_name)
                    self.fileItem.setText(1, '文件')
                    self.fileItem.setText(2, self.ftp_path + '/' + self.file_name)
                    # 已有同名节点不再增加此名称的节点
                    havesame = self.findTreeSameNode(self.ftpItem, self.file_name)
                    if not havesame:
                        self.ftpItem.addChild(self.fileItem)
                QMessageBox.information(self, "提示信息", "文件上传完成")
        else:
             QMessageBox.information(self, "提示信息", "请选择树节点")

    # 文件上传时查找目录树此节点的子节点中是否已有此名称
    def findTreeSameNode(self, thisitem, filename):
        for i in range(thisitem.childCount()):
            child = thisitem.child(i)
            if child.text(0) == filename:
                return True
            else:
                return False

    # 文件夹上传操作
    def actionUploadDirHandler(self):
        if self.treeWidget.currentItem():
            self.currentItem = self.treeWidget.currentItem()
            if self.currentItem.text(1) == '文件':  # 如果选中文件节点则获取此节点的父节点
                self.ftp_path = self.currentItem.parent().text(2)
                self.ftpItem = self.currentItem.parent()
            else:
                self.ftp_path = self.currentItem.text(2)
                self.ftpItem = self.currentItem
            dir_path = QFileDialog.getExistingDirectory(None, "选择文件夹", "")
            if dir_path:
                self.ftpOper.uploaddir(dir_path, self.ftp_path)
                dirname = os.path.split(dir_path)[-1]  # 文件夹名称
                rootchildItem = QTreeWidgetItem()
                rootchildItem.setText(0, dirname)
                rootchildItem.setText(1, '文件夹')
                rootchildItem.setText(2, self.ftp_path + '/' + dirname)
                self.ftpItem.addChild(rootchildItem)
                self.traverseLocalDirectory(dir_path, rootchildItem, self.ftp_path + '/' + dirname)
                QMessageBox.information(self, "提示信息", "文件夹上传完成")
        else:
             QMessageBox.information(self, "提示信息", "请选择树节点")

    # 遍历本地目录，构建可视化树结构
    def traverseLocalDirectory(self, dirpath, item, ftppath):
        for file in os.listdir(dirpath):
            src = os.path.join(dirpath, file)
            childItem = QTreeWidgetItem()
            childItem.setText(0, file)
            if os.path.isfile(src):
                childItem.setText(1, '文件')
                childItem.setText(2, ftppath + '/' + file)
                item.addChild(childItem)
            elif os.path.isdir(src):
                childItem.setText(1, '文件夹')
                childItem.setText(2, ftppath + '/' + file)
                item.addChild(childItem)
                # 递归调用自身
                self.traverseLocalDirectory(src, childItem, ftppath + '/' + file)

    # 文件下载操作
    def actionDownloadHandler(self):
        if self.treeWidget.currentItem():
            self.currentItem = self.treeWidget.currentItem()
            dir_path = QFileDialog.getExistingDirectory(None, "选择文件夹", "")
            if dir_path:
                if self.currentItem.text(1) == '文件':
                    localfilepath = dir_path + '/' + self.currentItem.text(0)
                    reomtefilepath = self.currentItem.text(2)
                    self.ftpOper.downloadfile(localfilepath, reomtefilepath)
                else:
                    localdirpath = dir_path + '/' + self.currentItem.text(0)
                    remotedirpath = self.currentItem.text(2)
                    self.ftpOper.downloaddir(localdirpath, remotedirpath)
                QMessageBox.information(self, "提示信息", "下载完成")
        else:
             QMessageBox.information(self, "提示信息", "请选择树节点")

    # 文件删除操作
    def actionDeleteHandler(self):
        if self.treeWidget.currentItem():
            self.currentItem = self.treeWidget.currentItem()
            questiontext = '是否要删除所选中的:【' + self.currentItem.text(0) + '】？'
            question = QMessageBox.question(None, '删除确认', questiontext)
            if question == QMessageBox.Yes:
                reomtefilepath = self.currentItem.text(2)
                if self.currentItem.text(1) == '文件':
                    remotepath, remotefile_name = os.path.split(reomtefilepath)
                    self.ftpOper.deletfile(remotefile_name, remotepath)  # 删除FTP服务端文件
                    self.currentItem.parent().removeChild(self.currentItem)  # 删除目录树对应的节点
                else:
                    self.ftpOper.deletedir(reomtefilepath)
                    self.currentItem.parent().removeChild(self.currentItem)  # 删除目录树对应的节点
        else:
             QMessageBox.information(self, "提示信息", "请选择树节点")

    # 创建文件夹操作
    def actionCreateHandler(self):
        if self.treeWidget.currentItem():
            self.currentItem = self.treeWidget.currentItem()
            dirname, ok = QInputDialog.getText(QWidget(), '新建文件夹', '请输入文件夹名称：')
            if ok:
                if self.currentItem.text(1):
                    if self.currentItem.text(1) == '文件':  # 如果选中文件节点则获取此节点的父节点
                        self.dirpath = self.currentItem.parent().text(2)
                        self.ftpItem = self.currentItem.parent()
                    else:
                        self.dirpath = self.currentItem.text(2)
                        self.ftpItem = self.currentItem
                else:
                    self.dirpath = ''
                    self.ftpItem = self.currentItem
                self.newdirpath = self.dirpath + '/' + dirname
                self.rusulttext = self.ftpOper.makedir(dirname, self.dirpath, self.newdirpath)
                if self.rusulttext == '文件夹创建成功':
                    self.fileItem = QTreeWidgetItem()
                    self.fileItem.setText(0, dirname)
                    self.fileItem.setText(1, '文件夹')
                    self.fileItem.setText(2, self.newdirpath)
                    self.ftpItem.addChild(self.fileItem)
        else:
             QMessageBox.information(self, "提示信息", "请选择树节点")

    # 重命名操作
    def actionRenameHandler(self):
        if self.treeWidget.currentItem():
            self.currentItem = self.treeWidget.currentItem()
            extension = None
            oldname = None
            oldpath = None
            if self.currentItem.text(1) == '文件':
                oldname, oldextension = os.path.splitext(self.currentItem.text(0))
            elif self.currentItem.text(1) == '文件夹':
                oldname = self.currentItem.text(0)
            newname, ok = QInputDialog.getText(QWidget(), '重命名', '请输入新名称', QLineEdit.Normal, oldname)
            if ok:
                if self.currentItem.text(1) == '文件':
                    oldpath = self.currentItem.text(2)
                    pathparent, name1 = os.path.split(oldpath)
                    name2, extension = os.path.splitext(name1)
                    newpath = pathparent + '/' + newname + extension
                else:
                    oldpath = self.currentItem.text(2)
                    if not (self.currentItem.parent().text(1)):  # 根目录
                        newpath = '/' + newname
                    else:
                        pathparent, name1 = os.path.split(oldpath)
                        newpath = pathparent + '/' + newname
                result = self.ftpOper.rename(oldpath, newpath)
                if result:
                    if extension:
                        self.currentItem.setText(0, newname + extension)
                    else:
                        self.currentItem.setText(0, newname)
                    self.currentItem.setText(2, newpath)
        else:
             QMessageBox.information(self, "提示信息", "请选择树节点")


    # 查看三维模型操作
    def action3DHandler(self):
        if self.treeWidget.currentItem():
            self.currentItem = self.treeWidget.currentItem()
            if (self.currentItem.text(1) == '文件'):
                modelUrl = FtpConfig.modelViewerUri + FtpConfig.modelBaseUri + self.currentItem.text(2)
                self.modelWebView = ModelWebView(weburi=modelUrl, title='数管系统三维模型浏览')
                self.modelWebView.show()
            else:
                QMessageBox.information(self, "提示信息", "请选择文件类型树节点")
        else:
             QMessageBox.information(self, "提示信息", "请选择树节点")

    # 打开文件操作
    def actionFileOpenHandler(self):
        if self.treeWidget.currentItem():
            self.currentItem = self.treeWidget.currentItem()
            if (self.currentItem.text(1) == '文件'):
                localdir = os.path.abspath(os.curdir)
                localtempdir = localdir + '/temp'
                if os.path.exists(localtempdir):
                    if self.currentItem.text(1) == '文件':
                        localfilepath = localtempdir + '/' + self.currentItem.text(0)
                        reomtefilepath = self.currentItem.text(2)
                        if os.path.exists(localfilepath):
                            try:
                                os.startfile(localfilepath)  # 打开本地文件
                            except Exception as e:
                                print(e)
                        else:
                            self.ftpOper.downloadfile(localfilepath, reomtefilepath)  # 下载到本地临时文件夹
                            try:
                                os.startfile(localfilepath)  # 打开本地文件
                            except Exception as e:
                                print(e)
            else:
                QMessageBox.information(self, "提示信息", "请选择文件类型树节点")
        else:
             QMessageBox.information(self, "提示信息", "请选择树节点")

    # 创建本地文件临时文件夹
    def createTempDir(self):
        localdir = os.path.abspath(os.curdir)
        localtempdir = localdir + '/temp'
        if not (os.path.exists(localtempdir)):  # 如不存在，创建本地临时文件夹
            os.makedirs(localtempdir)
        else:  # 如存在，删除后重新创建本地临时文件夹
            shutil.rmtree(localtempdir)
            os.mkdir(localtempdir)

    # 遍历FTP服务端目录，构建可视化树结构
    def traverseFTPDirectory(self, ftp, path, item):
        ftp.cwd(path)
        file_list = ftp.nlst()
        for file in file_list:
            try:
                ftp.cwd(path + '/' + file)  # 尝试切换到子目录
                # 添加子节点
                childItem = QTreeWidgetItem()
                childItem.setText(0, file)
                childItem.setText(1, '文件夹')
                childItem.setText(2, (path + '/' + file).replace('//', '/'))
                item.addChild(childItem)
                # 递归调用自身
                self.traverseFTPDirectory(ftp, (path + '/' + file).replace('//', '/'), childItem)
                ftp.cwd('..')  # 返回到上一级目录
            except ftplib.error_perm:
                # 添加子节点
                childItem = QTreeWidgetItem()
                childItem.setText(0, file)
                childItem.setText(1, '文件')
                childItem.setText(2, (path + '/' + file).replace('//', '/'))
                item.addChild(childItem)


# 三维模型浏览
class ModelWebView(QWebEngineView):
    def __init__(
            self,
            weburi: str,
            title: Optional[str] = "自定义窗口",
            windowW: Optional[int] = 1200,
            windowH: Optional[int] = 800,
    ):
        super().__init__()
        self.uri = weburi
        self.resize(windowW, windowH)
        self.setWindowTitle(title)
        self.page = QWebEnginePage()
        self.page.load(QUrl(self.uri))
        self.setPage(self.page)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ftpMain = FtpManage()
    ftpMain.show()
    ftpMain.traverseFTPDirectory(ftpMain.ftpOper.ftp, '/', ftpMain.rootItem)  # 遍历构建树
    sys.exit(app.exec_())
