class SocketTool(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(SocketTool, self).__init__(parent=parent)
        ui_file = os.path.join(__file__, "..", "skeletal_socket_tool.ui")
        load_ui(ui_file, self)

        self.model = MTableModel()
        header_list = [u"骨骼", u"插槽"]
        self.header_list = [
            {
                "label": data,
                "key": data,
                "editable": True,
                "selectable": True,
                "width": 400,
            }
            for data in header_list
        ]
        self.model.set_header_list(self.header_list)

        self.model_sort = MSortFilterModel()
        self.model_sort.setSourceModel(self.model)

        self.Table_View.setShowGrid(True)
        self.Table_View.setModel(self.model_sort)
        header = self.Table_View.horizontalHeader()
        header.setStretchLastSection(True)

        self.popMenu = QtWidgets.QMenu(self)
        action = QtWidgets.QAction(u"删除选择", self)
        action.triggered.connect(self.remove_line)
        self.popMenu.addAction(action)
        action = QtWidgets.QAction(u"添加一行", self)
        action.triggered.connect(self.add_line)
        self.popMenu.addAction(action)

        self.Table_View.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.Table_View.customContextMenuRequested.connect(self.on_context_menu)

        self.Close_Action.triggered.connect(self.close)
        self.Export_CSV_Action.triggered.connect(self.export_csv)
        self.Import_CSV_Action.triggered.connect(self.import_csv)
        self.Help_Action.triggered.connect(
            lambda: webbrowser.open_new_tab(
                "http://wiki.l0v0.com/unreal/PyToolkit/#/anim/2_skeletal_socket_tool"
            )
        )
        self.Socket_BTN.clicked.connect(self.add_socket)
        self.Clear_BTN.clicked.connect(self.clear_socket)

        # NOTE 读取 csv
        self.settings = QtCore.QSettings(
            "%s.ini" % self.__class__.__name__, QtCore.QSettings.IniFormat
        )
        csv_file = self.settings.value("csv_file")
        # csv_file = os.path.join(__file__,"..","test.csv")
        if csv_file and os.path.exists(csv_file):
            self.handle_csv(csv_file)

    def get_data_list(self):
        row_count = self.model.rowCount()
        column_count = self.model.columnCount()
        return [
            {
                self.header_list[j]["key"]: self.model.itemData(self.model.index(i, j))[
                    0
                ]
                for j in range(column_count)
            }
            for i in range(row_count)
        ]

    def remove_line(self):

        data_list = self.get_data_list()
        indexes = self.Table_View.selectionModel().selectedRows()
        for index in sorted(indexes, reverse=True):
            data_list.pop(index.row())
        self.model.set_data_list(data_list)

    def add_line(self):
        data_list = self.get_data_list()
        column_count = self.model.columnCount()
        data_list.append({self.header_list[j]["key"]: "" for j in range(column_count)})
        self.model.set_data_list(data_list)

    def on_context_menu(self, point):
        x = point.x()
        y = point.y()
        self.popMenu.exec_(self.Table_View.mapToGlobal(QtCore.QPoint(x, y + 35)))

    def import_csv(self, path=None):
        if not path:
            path, _ = QFileDialog.getOpenFileName(
                self, caption=u"获取设置", filter=u"csv (*.csv)"
            )
        # NOTE 如果文件不存在则返回空
        if not path or not os.path.exists(path):
            return

        self.handle_csv(path)
        self.settings.setValue("csv_file", path)

    def export_csv(self, path=None):
        if not path:
            path, _ = QFileDialog.getSaveFileName(
                self, caption=u"输出设置", filter=u"csv (*.csv)"
            )
            if not path:
                return

        csv = ""
        row_count = self.model.rowCount()
        column_count = self.model.columnCount()
        for i in range(row_count):
            col_list = [
                self.model.itemData(self.model.index(i, j))[0]
                for j in range(column_count)
            ]
            csv += ",".join(col_list) + "\n"

        with open(path, "w", encoding="gbk") as f:
            f.write(csv)

        # NOTE 打开输出的路径
        subprocess.Popen(
            ["start", "", os.path.dirname(path)], creationflags=0x08000000, shell=True
        )

    def handle_csv(self, csv_path):
        data_list = []
        with open(csv_path, "r") as f:
            reader = csv.reader(f)
            for i, row in enumerate(reader):
                data_list.append(
                    {
                        self.header_list[j]["key"]: r.decode("gbk")
                        for j, r in enumerate(row)
                    }
                )
        self.model.set_data_list(data_list)
        self.Table_View.resizeColumnToContents(1)

    def get_selectal_mesh(self):
        skel_mesh_list = [
            a
            for a in unreal.EditorUtilityLibrary.get_selected_assets()
            if isinstance(a, unreal.SkeletalMesh)
        ]
        if not skel_mesh_list:
            msg = u"请选择一个 Skeletal Mesh 物体"
            alert(msg)
            return []
        return skel_mesh_list

    def clear_socket(self):
        for skel_mesh in self.get_selectal_mesh():
            skeleton = skel_mesh.get_editor_property("skeleton")
            # NOTE 删除所有的 socket
            socket_list = [
                skel_mesh.get_socket_by_index(i) for i in range(skel_mesh.num_sockets())
            ]
            py_lib.delete_skeletal_mesh_socket(skeleton, socket_list)

    def add_socket(self):

        # NOTE 读取 Table View
        row_count = self.model.rowCount()
        itemData = self.model.itemData
        index = self.model.index
        data_dict = {
            itemData(index(i, 0))[0]: itemData(index(i, 1))[0] for i in range(row_count)
        }

        if not data_dict:
            msg = u"当前配置数据为空请导入一个 CSV"
            alert(msg)
            return

        fail_list = {}
        add_socket = py_lib.add_skeletal_mesh_socket
        bone_num = py_lib.get_skeleton_bone_num
        bone_name = py_lib.get_skeleton_bone_name
        for skel_mesh in self.get_selectal_mesh():

            skeleton = skel_mesh.get_editor_property("skeleton")
            bone_list = [bone_name(skeleton, i) for i in range(bone_num(skeleton))]

            for bone, socket in data_dict.items():
                # NOTE 检查骨骼名称是否存在
                if not socket or skel_mesh.find_socket(socket):
                    continue
                elif bone not in bone_list:
                    skel_name = skel_mesh.get_name()
                    if skel_name not in fail_list:
                        fail_list[skel_name] = []
                    fail_list[skel_name].append(bone)
                    continue

                _socket = add_socket(skeleton, bone).socket_name
                if _socket != socket:
                    skel_mesh.rename_socket(_socket, socket)

        if fail_list:
            msg = u"以下骨骼名称没有在选中的角色找到↓↓↓\n\n"
            for skel_name, bone_list in fail_list.items():
                msg += u"%s:\n%s" % (skel_name, "\n".join(bone_list))

            alert(msg)
class PropertyTransferTool(QtWidgets.QWidget, MaterialTransfer, AssetList):

    state = PropertyTransferBinder()

    def update_icon(self):
        data = {
            "Del": {
                "icon": QtWidgets.QStyle.SP_BrowserStop,
                "callback": self.remove_assets,
                "tooltip": u"删除文件",
            },
            "Locate": {
                "icon": QtWidgets.QStyle.SP_FileDialogContentsView,
                "callback": self.locate_asset_location,
                "tooltip": u"定位文件",
            },
            "Drive": {
                "icon": QtWidgets.QStyle.SP_DriveHDIcon,
                "callback": self.locate_file_location,
                "tooltip": u"打开系统目录路径",
            },
            "Expand": {
                "icon": QtWidgets.QStyle.SP_ToolBarVerticalExtensionButton,
                "callback": self.Property_Tree.expandAll,
                "tooltip": u"扩展全部",
            },
            "Collapse": {
                "icon": QtWidgets.QStyle.SP_ToolBarHorizontalExtensionButton,
                "callback": self.Property_Tree.collapseAll,
                "tooltip": u"收缩全部",
            },
            "Source_Locate": {
                "icon":
                QtWidgets.QStyle.SP_FileDialogContentsView,
                "callback":
                lambda: asset_lib.sync_browser_to_objects(
                    [self.source.get_path_name()]),
                "tooltip":
                u"定位复制源",
            },
        }

        widget_dict = {
            "BTN": "clicked",
            "Action": "triggered",
        }

        style = QtWidgets.QApplication.style()
        icon = style.standardIcon(QtWidgets.QStyle.SP_BrowserReload)
        self.setWindowIcon(icon)
        for typ, info in data.items():
            icon = style.standardIcon(info.get("icon"))
            tooltip = info.get("tooltip", "")
            tooltip = '<span style="font-weight:600;">%s</span>' % tooltip
            callback = info.get("callback", lambda *args: None)

            for widget, signal in widget_dict.items():
                widget = "%s_%s" % (typ, widget)
                if hasattr(self, widget):
                    widget = getattr(self, widget)
                    widget.setIcon(icon)
                    getattr(widget, signal).connect(callback)
                    widget.setToolTip(tooltip)
                    widget.setEnabled(lambda: self.state.source_eanble)
                    widget.setEnabled(self.state.source_eanble)
                    # print(widget.isEnabled())

        # QtCore.QTimer.singleShot(0, lambda: self.state.source_eanble >> Set(False))

    def __init__(self, parent=None):
        super(PropertyTransferTool, self).__init__(parent)
        DIR, file_name = os.path.split(__file__)
        file_name = os.path.splitext(file_name)[0]
        load_ui(os.path.join(DIR, "%s.ui" % file_name), self)

        self.source = None

        help_link = "http://redarttoolkit.pages.oa.com/docs/posts/85c3f876.html"
        self.Help_Action.triggered.connect(
            lambda: webbrowser.open_new_tab(help_link))

        # NOTE 设置按钮图标
        QtCore.QTimer.singleShot(0, self.update_icon)

        menu_callback = lambda: self.Asset_Menu.popup(QtGui.QCursor.pos())
        self.Asset_List.customContextMenuRequested.connect(menu_callback)
        menu_callback = lambda: self.Property_Menu.popup(QtGui.QCursor.pos())
        self.Property_Tree.customContextMenuRequested.connect(menu_callback)

        self.Asset_Label.setVisible(lambda: self.state.lable_visible)
        self.Asset_Label.setText(lambda: self.state.lable_text)

        self.Src_BTN.clicked.connect(self.get_source)
        self.Dst_BTN.clicked.connect(self.get_destination)
        self.Transfer_BTN.clicked.connect(self.transfer_property)

        # NOTE 配置 splitter
        name = "%s.ini" % self.__class__.__name__
        self.settings = QtCore.QSettings(name, QtCore.QSettings.IniFormat)
        self.Splitter.splitterMoved.connect(lambda: self.settings.setValue(
            "splitter_size", self.Splitter.sizes()))
        splitter_size = self.settings.value("splitter_size")
        size = [int(i) for i in splitter_size] if splitter_size else [700, 200]
        self.Splitter.setSizes(size)

        # NOTE 配置搜索栏
        self.Dst_Filter_LE.search()
        self.Dst_Filter_LE.setPlaceholderText("")
        self.Prop_Filter_LE.search()
        self.Prop_Filter_LE.setPlaceholderText("")

        # NOTE 配置 Property_Tree
        self.property_model = MTableModel()
        columns = {u"property": "属性名", u"value": "数值"}
        self.property_header_list = [{
            "label":
            label,
            "key":
            key,
            "bg_color":
            lambda x, y: y.get("bg_color", QtGui.QColor("transparent")),
            "checkable":
            i == 0,
            "searchable":
            True,
            "width":
            300,
            "font":
            lambda x, y: {
                "bold": True
            },
        } for i, (key, label) in enumerate(columns.items())]
        self.property_model.set_header_list(self.property_header_list)
        self.property_model_sort = MSortFilterModel()
        self.property_model_sort.set_header_list(self.property_header_list)
        self.property_model_sort.setSourceModel(self.property_model)
        self.Prop_Filter_LE.textChanged.connect(
            self.property_model_sort.set_search_pattern)

        self.Property_Tree.setModel(self.property_model_sort)
        header = self.Property_Tree.header()
        header.setStretchLastSection(True)

        # NOTE 配置 Asset_List
        self.asset_model = MTableModel()
        self.asset_header_list = [{
            "label":
            "destination",
            "key":
            "destination",
            "bg_color":
            lambda x, y: y.get("bg_color", QtGui.QColor("transparent")),
            "tooltip":
            lambda x, y: y.get("asset").get_path_name(),
            "checkable":
            True,
            "searchable":
            True,
            "width":
            300,
            "font":
            lambda x, y: {
                "bold": True
            },
        }]
        self.asset_model.set_header_list(self.asset_header_list)
        self.asset_model_sort = MSortFilterModel()
        self.asset_model_sort.search_reg.setPatternSyntax(
            QtCore.QRegExp.RegExp)
        self.asset_model_sort.set_header_list(self.asset_header_list)
        self.asset_model_sort.setSourceModel(self.asset_model)
        self.Dst_Filter_LE.textChanged.connect(
            self.asset_model_sort.set_search_pattern)

        self.Asset_List.setModel(self.asset_model_sort)
        self.Asset_List.selectionModel().selectionChanged.connect(
            self.asset_selection_change)
        self.property_model.dataChanged.connect(self.asset_selection_change)

    def get_source(self):
        assets = util_lib.get_selected_assets()
        if len(assets) < 1:
            toast(u"请选择一个资产")
            return

        asset = assets[0]
        self.set_property_tree(asset)
        self.asset_model.set_data_list([])
        self.property_model_sort.sort(0, QtCore.Qt.AscendingOrder)

    def get_destination(self):
        if not self.source:
            toast(u"请拾取复制源")
            return

        data_list = self.asset_model.get_data_list()
        tooltip_list = [
            data.get("asset").get_path_name() for data in data_list
        ]

        assets = [
            asset for asset in util_lib.get_selected_assets()
            if isinstance(asset, type(self.source))
            and asset.get_path_name() not in tooltip_list
        ]

        if not assets:
            toast(u"请选择匹配的资产")
            return

        data_list.extend([
            {
                "bg_color": QtGui.QColor("transparent"),
                "asset": asset,
                "destination": asset.get_name(),
                # NOTE 默认勾选
                "destination_checked": QtCore.Qt.Checked,
            } for asset in assets if not asset is self.source
        ])

        self.asset_model.set_data_list(data_list)

    def asset_selection_change(self, *args):
        model = self.Asset_List.selectionModel()
        indexes = model.selectedRows()
        if not indexes:
            return
        index = indexes[0]
        data_list = self.asset_model.get_data_list()
        data = data_list[index.row()]
        asset = data.get("asset")
        if isinstance(asset, unreal.MaterialInterface):
            self.update_material_property(asset)

    def set_property_tree(self, asset):
        if isinstance(asset, unreal.MaterialInterface):
            data = self.get_material_property(asset)
        elif isinstance(asset, unreal.Blueprint):
            pass
        else:
            toast(u"不支持资产")
            return

        data_list = [{
            "property":
            group,
            "value":
            "",
            "children": [{
                "bg_color": QtGui.QColor("transparent"),
                "property": prop,
                "property_checked": QtCore.Qt.Unchecked,
                "value": value,
            } for prop, value in props.items()],
        } for group, props in data.items()]
        self.property_model.set_data_list(data_list)

        self.source = asset
        self.state.source_eanble = True
        self.state.lable_visible = True
        self.state.lable_text = asset.get_name()

    def transfer_property(self):

        for i, asset in enumerate(self.asset_model.get_data_list()):
            if not asset.get("destination_checked"):
                continue

            # NOTE 选择 Asset_List 的资产 更新颜色
            index = self.asset_model.index(i, 0)
            model = self.Asset_List.selectionModel()
            model.setCurrentIndex(index, model.SelectCurrent)

            property_list = [
                prop.get("property")
                for grp in self.property_model.get_data_list()
                for prop in grp.get("children")
                # NOTE 过滤没有勾选的资源
                if prop.get("bg_color") == QtGui.QColor("green")
                and prop.get("property_checked")
            ]

            # NOTE 传递属性
            asset = asset.get("asset")
            if isinstance(asset, type(self.source)):
                self.transfer_material_property(asset, property_list)
            else:
                raise RuntimeError(u"%s 和复制源类型不匹配" % asset.get_name())

        toast("传递完成", 'success')