import csv
import ctypes
import os
import re
import sqlite3
from ctypes import CDLL

from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QFileDialog, QProgressBar
from qgis._core import QgsVectorFileWriter, QgsCoordinateTransformContext, QgsWkbTypes, QgsRenderContext, QgsFillSymbol, \
    QgsLineSymbolLayer, QgsEmbeddedSymbolRenderer, QgsGraduatedSymbolRenderer, QgsSingleSymbolRenderer, \
    QgsCategorizedSymbolRenderer

from qgis.PyQt import QtWidgets, uic, QtCore
from qgis.PyQt.QtCore import pyqtSignal

FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'cyanlove_export_tab_kml_base.ui'))


class cyanlove_export_tab_kml(QtWidgets.QDockWidget, FORM_CLASS):
    closingPlugin = pyqtSignal()

    def __init__(self, parent=None):
        super(cyanlove_export_tab_kml, self).__init__(parent)
        self.export_thread = None
        self.setupUi(self)
        self.setFloating(True)
        self.setWindowFlags(QtCore.Qt.Dialog)  # 使对话框始终在前
        # self.setWindowModality(QtCore.Qt.ApplicationModal)  # 或者使用 Qt.WindowModal
        self.btn_export_tab.clicked.connect(self.export_tab_kml)
        self.btn_export_shanqutu.clicked.connect(self.export_tab_shanqutu)
        self.btn_export_kml.clicked.connect(self.export_tab_kml)
        self.btn_export_custom_thematic_map.clicked.connect(self.export_custom_thematic_map)

    def update_label(self, value):
        self.label_show.setText(value)  # 更新值

    def update_progress(self, value):
        self.progressBar.setValue(value)  # 更新进度条的值

    def on_thread_finished(self):
        self.label_show.setText("导出完成！")
        self.progressBar.setValue(100)

    def export_tab_kml(self):
        button = self.sender()
        button_name = ""
        if button:
            # 获取按钮的文本
            button_name = button.objectName()
            print(button_name)
        options = QFileDialog.Options()  # 创建文件对话框选项
        options |= QFileDialog.ReadOnly  # 可以设置一些选项，例如只读
        filterfile = "MapInfo TAB Files (*.tab)"
        if button_name == "btn_export_kml":
            filterfile = "KML File (*.kml)"
        file_name, _ = QFileDialog.getSaveFileName(self, "选择文件", "", filterfile, options=options)

        # 检查用户是否选择了文件
        if file_name:
            selected_layer = self.mMapLayerComboBox.currentLayer()
            if selected_layer is None:
                print("未选择有效图层!")
            else:
                print(f"选择的图层: {selected_layer.name()}")
                csv_file_path = file_name
                print(f"保存的文件：{csv_file_path}")

                # 启动导出线程
                self.export_thread = ExportThread(selected_layer, csv_file_path, button_name)
                self.export_thread.updatelabel.connect(self.update_label)
                self.export_thread.finished.connect(self.on_thread_finished)  # 导出完成后连接到槽
                self.export_thread.start()  # 启动线程

        else:
            print("用户取消了文件选择")  # 处理取消选择的情况

    def export_tab_shanqutu(self):
        options = QFileDialog.Options()  # 创建文件对话框选项
        options |= QFileDialog.ReadOnly  # 可以设置一些选项，例如只读
        filterfile = "MapInfo TAB Files (*.tab)"
        file_name, _ = QFileDialog.getSaveFileName(self, "选择文件", "", filterfile, options=options)

        # 检查用户是否选择了文件
        if file_name:
            selected_layer = self.mMapLayerComboBox.currentLayer()
            if selected_layer is None:
                self.label_show.setText("未选择有效图层!")
            else:
                print(f"选择的图层: {selected_layer.name()}")
                csv_file_path = file_name
                print(f"保存的文件：{csv_file_path}")
                geom_type = selected_layer.wkbType()

                # 如果是点图层，则调用点的缓冲
                if geom_type in [QgsWkbTypes.Polygon, QgsWkbTypes.MultiPolygon]:
                    self.progressBar.setRange(0, 100)  # 设置进度条范围
                    self.progressBar.setValue(0)  # 初始化进度条值
                    self.progressBar.show()  # 显示进度条 (如果之前是隐藏状态)
                    # 启动导出线程
                    self.export_thread = ExportThread2(selected_layer, csv_file_path)
                    self.export_thread.updatelabel.connect(self.update_label)
                    self.export_thread.progress.connect(self.update_progress)  # 连接信号到槽
                    self.export_thread.start()  # 启动线程
                else:
                    self.label_show.setText("不是扇区图Polygon格式！")
        else:
            self.label_show.setText("用户取消了文件选择")  # 处理取消选择的情况

    def export_custom_thematic_map(self):
        options = QFileDialog.Options()  # 创建文件对话框选项
        options |= QFileDialog.ReadOnly  # 可以设置一些选项，例如只读
        filterfile = "MapInfo TAB Files (*.tab)"
        file_name, _ = QFileDialog.getSaveFileName(self, "选择文件", "", filterfile, options=options)

        # 检查用户是否选择了文件
        if file_name:
            selected_layer = self.mMapLayerComboBox.currentLayer()
            if selected_layer is None:
                self.label_show.setText("未选择有效图层!")
            else:
                print(f"选择的图层: {selected_layer.name()}")
                csv_file_path = file_name
                print(f"保存的文件：{csv_file_path}")
                geom_type = selected_layer.wkbType()

                # 如果是点图层，则调用点的缓冲
                if geom_type in [QgsWkbTypes.MultiPoint, QgsWkbTypes.Polygon, QgsWkbTypes.MultiPolygon]:
                    self.progressBar.setRange(0, 100)  # 设置进度条范围
                    self.progressBar.setValue(0)  # 初始化进度条值
                    self.progressBar.show()  # 显示进度条 (如果之前是隐藏状态)
                    # 启动导出线程
                    self.export_thread = ExportThread3(selected_layer, csv_file_path)
                    self.export_thread.updatelabel.connect(self.update_label)
                    self.export_thread.progress.connect(self.update_progress)  # 连接信号到槽
                    self.export_thread.start()  # 启动线程
                else:
                    self.label_show.setText("不是Polygon格式！")
        else:
            self.label_show.setText("用户取消了文件选择")  # 处理取消选择的情况


class ExportThread(QThread):
    updatelabel = pyqtSignal(str)  # 声明一个更新label新增信号
    progress = pyqtSignal(int)  # 声明一个信号

    def __init__(self, selected_layer, savpath, button_name):
        super().__init__()  # 初始化父类
        self.savpath = savpath  # 存储
        self.button_name = button_name  # 按钮名称
        self.selected_layer = selected_layer  # QGIS 图层

    def run(self):
        self.updatelabel.emit('正在导出中...')
        field_names = [f"{field.name()}" for field in self.selected_layer.fields()]
        # 导出TAB格式
        if self.button_name == "btn_export_tab":
            # 设置导出参数
            options = QgsVectorFileWriter.SaveVectorOptions()

            options.driverName = 'MapInfo File'  # 指定输出格式为MapInfo TAB
            options.fileEncoding = 'cp936'  # 设置文件编码为cp936
            options.supportsFeatureStyles = True
            transform_context = QgsCoordinateTransformContext()  # 创建坐标转换上下文
            # 执行导出
            error = QgsVectorFileWriter.writeAsVectorFormatV3(self.selected_layer, self.savpath, transform_context,
                                                              options)

            if error[0] == QgsVectorFileWriter.NoError:
                print("导出成功！")
                # 检查是否能成功调用写入函数
                try:
                    self.writertabfilecolumn_to_gbk(self.savpath, field_names)
                    self.updatelabel.emit('导出成功!')
                except Exception as e:
                    print("写入表格时出现错误:", e)
                    self.updatelabel.emit(f'写入表格失败: {e}')
            else:
                print("导出失败：", error[1])
                self.updatelabel.emit(f'导出失败{error[1]}')

        if self.button_name == "btn_export_kml":
            # 设置导出参数
            options = QgsVectorFileWriter.SaveVectorOptions()
            options.driverName = 'KML'  # 指定输出格式为MapInfo TAB
            options.fileEncoding = 'utf-8'  # 设置文件编码为cp936
            transform_context = QgsCoordinateTransformContext()  # 创建坐标转换上下文
            # 执行导出
            error = QgsVectorFileWriter.writeAsVectorFormatV3(self.selected_layer, self.savpath, transform_context,
                                                              options)

            if error[0] == QgsVectorFileWriter.NoError:
                print("导出成功！")
                self.updatelabel.emit('导出成功!')
            else:
                print("导出失败：", error[1])
                self.updatelabel.emit(f'导出失败{error[1]}')

    def writertabfilecolumn_to_gbk(self, file_path, field_names):
        print("修改表头")
        # 读取原文件内容
        fields_list = []
        try:
            with open(file_path, 'r', encoding='utf-8', errors='replace') as file:
                content = file.readlines()
        except Exception as e:
            print(f"发生错误: {e}")
            self.updatelabel.emit(f'导出失败{e}')
            content = []

        if content:
            for i, line in enumerate(content):
                if line.strip().startswith("Fields"):
                    # 提取字段类型，将其写入数组
                    fields_list.extend(self.rx_to_str(line) for line in content[i + 1:] if line.strip())
                    break  # 找到后可结束循环

        # 字段和字段类型合并
        result = list(map(lambda x: f"\"{x[0]}\" {x[1]}", zip(field_names, fields_list)))
        # 合并格式，替换
        header = ("!table\n"
                  "!version 450\n"
                  "!charset WindowsSimpChinese\n\n"
                  "Definition Table\n"
                  "  Type NATIVE Charset \"WindowsSimpChinese\"\n"
                  "  Fields {}\n").format(len(field_names))

        fields_string = ""
        for field in result:
            fields_string += "    {}\n".format(field)
        table_definition = header + fields_string
        print(table_definition)
        try:
            with open(file_path, 'w', encoding='gbk', errors='replace') as file:
                file.write(table_definition)  # 写入字符串
        except Exception as e:
            print(f"发生错误: {e}")
            self.updatelabel.emit(f'导出失败{e}')

    @staticmethod
    def rx_to_str(textstr):
        data_types = [
            "Char",
            "Integer",
            "Smallint",
            "Float",
            "Decimal",
            "Date",
            "Time",
            "DateTime",
            "Logical"
        ]
        # 将数据类型转换为小写并创建正则表达式模式
        lower_data_types = [dtype.lower() for dtype in data_types]
        pattern = r'(' + '|'.join(re.escape(dtype) for dtype in lower_data_types) + r')\s*(\([^)]*\))?\s*;'

        # 将输入字符串转为小写
        lower_textstr = textstr.lower()

        # 从右向左查找匹配
        matches = re.findall(pattern, lower_textstr)

        # 如果找到匹配，则返回匹配项
        if matches:
            last_match = matches[-1]  # 获取最后一个匹配
            return f"{last_match[0]} {last_match[1] if last_match[1] else ''} ;".strip()  # 返回数据类型和可能的长度
        return None


# 导出扇区图(包含颜色）
class ExportThread2(QThread):
    updatelabel = pyqtSignal(str)  # 声明一个更新label新增信号
    progress = pyqtSignal(int)  # 声明一个信号

    def __init__(self, selected_layer, savpath):
        super().__init__()  # 初始化父类
        self.savpath = savpath  # 存储
        self.selected_layer = selected_layer  # QGIS 图层

    def run(self):
        self.updatelabel.emit('正在导出中...')
        field_names = [f"{field.name()}" for field in self.selected_layer.fields()]
        field_info = [(field.name(), field.typeName()) for field in self.selected_layer.fields()]
        # 导出TAB格式
        self.write_polygon_file(self.savpath, "tab", field_names, field_info)

    def write_polygon_file(self, outpath, out_mif_or_tab_type, field_names, field_info):
        # 加载 mitab64.dll
        current_dir = os.path.dirname(__file__)
        relative_path = os.path.join(current_dir, 'scripts', 'mitab64.dll')
        lib = CDLL(relative_path)
        if not lib:
            print("Failed to load the DLL.")
            return
        self.setargtypes(lib)
        try:
            # 创建数据集
            dataset = lib.mitab_c_create(
                outpath.encode('gbk'),  # windows系统路径默认gbk
                out_mif_or_tab_type.encode('utf-8'),
                "CoordSys Earth Projection 1, 104".encode('utf-8'),
                90, -90, 180, -180
            )

            if dataset is None:
                print("Failed to create the file:", out_mif_or_tab_type)
                print("Error:", lib.mitab_c_getlasterrormsg().decode('utf-8'))
                return

            # 改进的字段类型映射
            for name, typefilename in field_info:
                encoded_name = name.encode('utf-8')
                typefilename_lower = typefilename.lower()

                if typefilename_lower in ["string", "text", "char"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 1, 254, 0, 0, 0)
                elif typefilename_lower in ["real", "double", "float"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 5, 254, 6, 0, 0)
                elif typefilename_lower in ["integer", "int", "long"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 2, 254, 0, 0, 0)
                elif typefilename_lower in ["date", "datetime", "time"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 6, 254, 0, 0, 0)
                elif typefilename_lower in ["boolean", "bool"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 7, 254, 0, 0, 0)  # Logical
                else:
                    # 默认处理为字符串类型
                    lib.mitab_c_add_field(dataset, encoded_name, 1, 254, 0, 0, 0)

            # 获取要素总数以计算进度
            total_features = self.selected_layer.featureCount()
            renderer = self.selected_layer.renderer()

            for index, feature in enumerate(self.selected_layer.getFeatures()):
                # 创建一个多边形 (区域类型)
                boundary_wkt = feature.geometry().asWkt()
                new_feature = lib.mitab_c_create_feature(dataset, 7)  # 7 = TABFC_Region
                # 设置多边形的点坐标 (WGS84 坐标系下的多边形顶点)
                x, y = self.wkt_to_coordinates(boundary_wkt)
                # 设置多边形的点
                lib.mitab_c_set_points(new_feature, 0, len(x), x, y)

                # 设置style - 改进的样式处理
                self.set_feature_style(lib, new_feature, renderer, feature)

                # 设置字段数据
                k = 0
                for field_name, typefilename in field_info:
                    value = feature[field_name]  # 获取每个字段的值
                    if value is None:
                        value = ""

                    typefilename_lower = typefilename.lower()
                    if typefilename_lower in ["string", "text", "char"]:
                        lib.mitab_c_set_field(new_feature, k, str(value).encode('gbk'))
                    elif typefilename_lower in ["real", "double", "float"]:
                        # 对于浮点数，确保是有效的数值
                        try:
                            float_value = float(value) if value != "" else 0.0
                            lib.mitab_c_set_field(new_feature, k, str(float_value).encode('gbk'))
                        except (ValueError, TypeError):
                            lib.mitab_c_set_field(new_feature, k, "0.0".encode('gbk'))
                    elif typefilename_lower in ["integer", "int", "long"]:
                        # 对于整数，确保是有效的数值
                        try:
                            int_value = int(value) if value != "" else 0
                            lib.mitab_c_set_field(new_feature, k, str(int_value).encode('gbk'))
                        except (ValueError, TypeError):
                            lib.mitab_c_set_field(new_feature, k, "0".encode('gbk'))
                    elif typefilename_lower in ["date", "datetime", "time"]:
                        lib.mitab_c_set_field(new_feature, k, str(value).encode('gbk'))
                    elif typefilename_lower in ["boolean", "bool"]:
                        bool_value = "T" if value else "F"
                        lib.mitab_c_set_field(new_feature, k, bool_value.encode('gbk'))
                    else:
                        # 默认处理为字符串
                        lib.mitab_c_set_field(new_feature, k, str(value).encode('gbk'))
                    k += 1

                # 写入多边形要素
                lib.mitab_c_write_feature(dataset, new_feature)
                # 销毁特征对象
                lib.mitab_c_destroy_feature(new_feature)
                # 发送进度更新
                progress_value = int((index + 1) / total_features * 100)
                self.progress.emit(progress_value)

            # 关闭数据集
            lib.mitab_c_close(dataset)
            self.writertabfilecolumn_to_gbk(self.savpath, field_names)
            self.updatelabel.emit(f'导出TAB含格式颜色完毕！')

        except Exception as e:
            self.updatelabel.emit(f"导出失败: {str(e)}")
            print(f"出错B:{str(e)}")

    def set_feature_style(self, lib, new_feature, renderer, feature):
        """设置要素样式"""
        try:
            if isinstance(renderer, (QgsEmbeddedSymbolRenderer, QgsSingleSymbolRenderer)):
                render_context = QgsRenderContext()
                symbol = renderer.symbolForFeature(feature, context=render_context)
                if symbol and symbol.symbolLayers():
                    symbol_layer = symbol.symbolLayers()[0]
                    properties = symbol_layer.properties()

                    # 获取填充颜色
                    fill_color = symbol.color()
                    fill_color_hex = fill_color.name().lstrip('#')
                    rgb_decimal_fill = int(fill_color_hex, 16) if fill_color_hex else 0
                    alpha = fill_color.alpha()

                    # 获取边框颜色和宽度
                    outline_color = properties.get('outline_color', '0,0,0,255')
                    outline_width = properties.get('outline_width', '0.5')

                    # 解析边框颜色
                    try:
                        r, g, b, a = map(int, outline_color.split(','))
                        rgb_decimal_line = (r << 16) + (g << 8) + b
                    except:
                        rgb_decimal_line = 0  # 黑色

                    # 设置填充色
                    lib.mitab_c_set_brush(new_feature, rgb_decimal_fill, rgb_decimal_fill, 2, alpha)
                    # 设置边框
                    lib.mitab_c_set_pen(new_feature, max(1, round(float(outline_width))), 2, rgb_decimal_line)

        except Exception as e:
            print(f"设置样式时出错: {e}")
            # 设置默认样式
            lib.mitab_c_set_brush(new_feature, 0xFFFFFF, 0xFFFFFF, 2, 255)  # 白色填充
            lib.mitab_c_set_pen(new_feature, 1, 2, 0)  # 黑色边框

    @staticmethod
    def setargtypes(lib):
        # 设置 mitab_c_create 函数的参数类型和返回类型
        lib.mitab_c_create.argtypes = [
            ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p,
            ctypes.c_double, ctypes.c_double, ctypes.c_double, ctypes.c_double
        ]
        lib.mitab_c_create.restype = ctypes.POINTER(ctypes.c_void_p)

        # 设置 mitab_c_add_field 函数的参数类型和返回类型
        lib.mitab_c_add_field.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_char_p, ctypes.c_int, ctypes.c_int,
            ctypes.c_int, ctypes.c_int, ctypes.c_int
        ]
        lib.mitab_c_add_field.restype = ctypes.c_long

        # 设置 mitab_c_create_feature 函数的参数类型和返回类型
        lib.mitab_c_create_feature.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_int
        ]
        lib.mitab_c_create_feature.restype = ctypes.POINTER(ctypes.c_void_p)

        # 设置 mitab_c_set_points 函数的参数类型和返回类型
        lib.mitab_c_set_points.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_double),
            ctypes.POINTER(ctypes.c_double)
        ]
        lib.mitab_c_set_points.restype = None

        # 设置 mitab_c_set_brush 函数的参数类型和返回类型
        lib.mitab_c_set_brush.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int
        ]
        lib.mitab_c_set_brush.restype = None

        # 设置 mitab_c_set_pen 函数的参数类型和返回类型
        lib.mitab_c_set_pen.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_int, ctypes.c_int
        ]
        lib.mitab_c_set_pen.restype = None

        # 设置 mitab_c_set_field 函数的参数类型
        lib.mitab_c_set_field.argtypes = [ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_char_p]
        lib.mitab_c_set_field.restype = None

        # 设置 mitab_c_write_feature 函数的参数类型和返回类型
        lib.mitab_c_write_feature.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_void_p)
        ]
        lib.mitab_c_write_feature.restype = None

        # 设置 mitab_c_destroy_feature 函数的参数类型
        lib.mitab_c_destroy_feature.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
        lib.mitab_c_destroy_feature.restype = None

        # 设置 mitab_c_close 函数的参数类型
        lib.mitab_c_close.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
        lib.mitab_c_close.restype = None

        # 设置 mitab_c_getlasterrormsg 函数的参数类型和返回类型
        lib.mitab_c_getlasterrormsg.argtypes = []
        lib.mitab_c_getlasterrormsg.restype = ctypes.c_char_p

    def writertabfilecolumn_to_gbk(self, file_path, field_names):
        print("修改表头")
        # 读取原文件内容
        fields_list = []
        try:
            with open(file_path, 'r', encoding='utf-8', errors='replace') as file:
                content = file.readlines()
        except Exception as e:
            print(f"发生错误: {e}")
            self.updatelabel.emit(f'导出失败{e}')
            content = []

        if content:
            for i, line in enumerate(content):
                if line.strip().startswith("Fields"):
                    # 提取字段类型，将其写入数组
                    fields_list.extend(self.rx_to_str(line) for line in content[i + 1:] if line.strip())
                    break  # 找到后可结束循环

        # 字段和字段类型合并
        result = list(map(lambda x: f"\"{x[0]}\" {x[1]}", zip(field_names, fields_list)))
        # 合并格式，替换
        header = ("!table\n"
                  "!version 450\n"
                  "!charset WindowsSimpChinese\n\n"
                  "Definition Table\n"
                  "  Type NATIVE Charset \"WindowsSimpChinese\"\n"
                  "  Fields {}\n").format(len(field_names))

        fields_string = ""
        for field in result:
            fields_string += "    {}\n".format(field)
        table_definition = header + fields_string
        print(table_definition)
        try:
            with open(file_path, 'w', encoding='gbk', errors='replace') as file:
                file.write(table_definition)  # 写入字符串
        except Exception as e:
            print(f"发生错误: {e}")
            self.updatelabel.emit(f'导出失败{e}')

    @staticmethod
    def rx_to_str(textstr):
        data_types = [
            "Char",
            "Integer",
            "Smallint",
            "Float",
            "Decimal",
            "Date",
            "Time",
            "DateTime",
            "Logical"
        ]
        # 将数据类型转换为小写并创建正则表达式模式
        lower_data_types = [dtype.lower() for dtype in data_types]
        pattern = r'(' + '|'.join(re.escape(dtype) for dtype in lower_data_types) + r')\s*(\([^)]*\))?\s*;'

        # 将输入字符串转为小写
        lower_textstr = textstr.lower()

        # 从右向左查找匹配
        matches = re.findall(pattern, lower_textstr)

        # 如果找到匹配，则返回匹配项
        if matches:
            last_match = matches[-1]  # 获取最后一个匹配
            return f"{last_match[0]} {last_match[1] if last_match[1] else ''} ;".strip()  # 返回数据类型和可能的长度
        return None

    @staticmethod
    def wkt_to_coordinates(wkt):
        wkt = wkt.lower()
        # 正则表达式解析 WKT 多边形
        pattern = r'polygon \(\((.*?)\)\)'
        match = re.search(pattern, wkt)

        if not match:
            raise ValueError("Invalid WKT format")

        # 提取坐标字符串并分割
        coord_str = match.group(1).strip()
        points = coord_str.split(',')

        # 初始化坐标数组
        x_coords = []
        y_coords = []

        for point in points:
            # 去除空格并分割坐标
            coords = point.strip().split()
            if len(coords) == 2:
                x, y = float(coords[0]), float(coords[1])
                x_coords.append(x)
                y_coords.append(y)

        # 转换为 ctypes 类型
        x_array = (ctypes.c_double * len(x_coords))(*x_coords)
        y_array = (ctypes.c_double * len(y_coords))(*y_coords)

        return x_array, y_array


# 导出专题地图
class ExportThread3(QThread):
    updatelabel = pyqtSignal(str)  # 声明一个更新label新增信号
    progress = pyqtSignal(int)  # 声明一个信号

    def __init__(self, selected_layer, savpath):
        super().__init__()  # 初始化父类
        self.savpath = savpath  # 存储
        self.selected_layer = selected_layer  # QGIS 图层

    def run(self):
        try:
            self.updatelabel.emit('正在导出中...')
            field_names = [f"{field.name()}" for field in self.selected_layer.fields()]
            field_info = [(field.name(), field.typeName()) for field in self.selected_layer.fields()]

            # 加载 mitab64.dll
            current_dir = os.path.dirname(__file__)
            relative_path = os.path.join(current_dir, 'scripts', 'mitab64.dll')
            lib = CDLL(relative_path)
            if not lib:
                raise Exception("无法加载 mitab64.dll")

            self.setargtypes(lib)
            self.write_polygon_file(lib, self.savpath, "tab", field_names, field_info)

        except Exception as e:
            self.updatelabel.emit(f"导出失败: {str(e)}")
            print(f"导出失败: {str(e)}")

    def write_polygon_file(self, lib, outpath, out_mif_or_tab_type, field_names, field_info):
        try:
            # 创建数据集
            dataset = lib.mitab_c_create(
                outpath.encode('gbk'),  # 使用gbk编码路径
                out_mif_or_tab_type.encode('utf-8'),
                "CoordSys Earth Projection 1, 104".encode('utf-8'),
                90.0, -90.0, 180.0, -180.0
            )

            if dataset is None:
                error_msg = lib.mitab_c_getlasterrormsg()
                raise Exception(f"创建文件失败: {error_msg.decode('utf-8') if error_msg else 'Unknown error'}")

            # 添加字段 - 改进的字段类型映射
            for name, typefilename in field_info:
                encoded_name = name.encode('utf-8')
                typefilename_lower = typefilename.lower()

                if typefilename_lower in ["string", "text", "char"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 1, 254, 0, 0, 0)
                elif typefilename_lower in ["real", "double", "float", "decimal"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 5, 254, 6, 0, 0)
                elif typefilename_lower in ["integer", "int", "long", "smallint"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 2, 254, 0, 0, 0)
                elif typefilename_lower in ["date", "datetime", "time"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 6, 254, 0, 0, 0)
                elif typefilename_lower in ["boolean", "bool", "logical"]:
                    lib.mitab_c_add_field(dataset, encoded_name, 7, 254, 0, 0, 0)
                else:
                    # 默认处理为字符串类型
                    lib.mitab_c_add_field(dataset, encoded_name, 1, 254, 0, 0, 0)

            # 获取要素总数以计算进度
            total_features = self.selected_layer.featureCount()
            renderer = self.selected_layer.renderer()

            # 支持多种渲染器类型
            for index, feature in enumerate(self.selected_layer.getFeatures()):
                # 创建一个多边形 (区域类型)
                boundary_wkt = feature.geometry().asWkt()
                new_feature = lib.mitab_c_create_feature(dataset, 7)  # 7 = TABFC_Region

                # 设置多边形的点坐标
                x, y = self.wkt_to_coordinates(boundary_wkt)
                lib.mitab_c_set_points(new_feature, 0, len(x), x, y)

                # 设置样式 - 修复专题地图颜色问题
                self.set_thematic_style(lib, new_feature, renderer, feature)

                # 设置字段数据
                k = 0
                for field_name, typefilename in field_info:
                    value = feature[field_name]
                    if value is None:
                        value = ""

                    typefilename_lower = typefilename.lower()
                    if typefilename_lower in ["string", "text", "char"]:
                        lib.mitab_c_set_field(new_feature, k, str(value).encode('gbk'))
                    elif typefilename_lower in ["real", "double", "float", "decimal"]:
                        try:
                            float_value = float(value) if value != "" else 0.0
                            lib.mitab_c_set_field(new_feature, k, str(float_value).encode('gbk'))
                        except (ValueError, TypeError):
                            lib.mitab_c_set_field(new_feature, k, "0.0".encode('gbk'))
                    elif typefilename_lower in ["integer", "int", "long", "smallint"]:
                        try:
                            int_value = int(value) if value != "" else 0
                            lib.mitab_c_set_field(new_feature, k, str(int_value).encode('gbk'))
                        except (ValueError, TypeError):
                            lib.mitab_c_set_field(new_feature, k, "0".encode('gbk'))
                    elif typefilename_lower in ["date", "datetime", "time"]:
                        lib.mitab_c_set_field(new_feature, k, str(value).encode('gbk'))
                    elif typefilename_lower in ["boolean", "bool", "logical"]:
                        bool_value = "T" if value else "F"
                        lib.mitab_c_set_field(new_feature, k, bool_value.encode('gbk'))
                    else:
                        lib.mitab_c_set_field(new_feature, k, str(value).encode('gbk'))
                    k += 1

                # 写入多边形要素
                lib.mitab_c_write_feature(dataset, new_feature)
                lib.mitab_c_destroy_feature(new_feature)

                # 发送进度更新
                progress_value = int((index + 1) / total_features * 100)
                self.progress.emit(progress_value)

            # 关闭数据集
            lib.mitab_c_close(dataset)
            self.writertabfilecolumn_to_gbk(self.savpath, field_names)
            self.updatelabel.emit('导出专题地图完毕！')

        except Exception as e:
            self.updatelabel.emit(f"导出专题地图失败: {str(e)}")
            print(f"导出专题地图失败: {str(e)}")

    def set_thematic_style(self, lib, new_feature, renderer, feature):
        """设置专题地图样式 - 修复颜色问题"""
        try:
            symbol = None

            # 分级渲染器 - 修复颜色获取问题
            if isinstance(renderer, QgsGraduatedSymbolRenderer):
                # 获取渲染字段
                render_field = renderer.classAttribute()
                value = feature[render_field]

                # 使用正确的符号获取方法
                symbol = renderer.symbolForValue(value)
                if not symbol:
                    # 如果根据值获取失败，尝试使用默认符号
                    symbol = renderer.sourceSymbol()

            # 分类渲染器
            elif isinstance(renderer, QgsCategorizedSymbolRenderer):
                category = renderer.categoryForFeature(feature)
                if category:
                    symbol = category.symbol()
                else:
                    # 如果没有匹配的分类，使用默认符号
                    symbol = renderer.sourceSymbol()

            # 单符号渲染器
            elif isinstance(renderer, QgsSingleSymbolRenderer):
                symbol = renderer.symbol()

            # 嵌入式符号渲染器
            elif isinstance(renderer, QgsEmbeddedSymbolRenderer):
                render_context = QgsRenderContext()
                symbol = renderer.symbolForFeature(feature, context=render_context)

            # 设置样式 - 确保符号存在
            if symbol:

                self.set_symbol_style(lib, new_feature, symbol)
            else:
                print("未找到符号，使用默认样式")
                # 设置默认样式
                self.set_default_style(lib, new_feature)

        except Exception as e:
            print(f"设置专题样式时出错: {e}")
            # 设置默认样式
            self.set_default_style(lib, new_feature)

    def set_symbol_style(self, lib, new_feature, symbol):
        """设置符号样式 - 修复颜色设置"""
        try:
            # 获取填充颜色
            fill_color = symbol.color()


            # 将颜色转换为 RGB 十进制值
            fill_color_hex = fill_color.name().lstrip('#')
            rgb_decimal_fill = int(fill_color_hex, 16) if fill_color_hex else 0xFFFFFF
            alpha = fill_color.alpha()

            # 获取边框样式
            outline_width = 0.3  # 默认边框宽度
            outline_color_rgb = 0  # 默认黑色边框

            if symbol.symbolLayers():
                symbol_layer = symbol.symbolLayers()[0]
                properties = symbol_layer.properties()

                # 获取边框颜色
                outline_color = properties.get('outline_color', '0,0,0,255')
                outline_width = properties.get('outline_width', '0.3')

                # 解析边框颜色
                try:
                    color_parts = outline_color.split(',')
                    if len(color_parts) >= 3:
                        r, g, b = map(int, color_parts[:3])
                        outline_color_rgb = (r << 16) + (g << 8) + b
                    else:
                        outline_color_rgb = 0  # 黑色
                except Exception as e:
                    print(f"解析边框颜色失败: {e}")
                    outline_color_rgb = 0

            # 设置填充色 - 修复颜色设置
            # mitab_c_set_brush 参数: 前景色, 背景色, 图案, 透明度
            lib.mitab_c_set_brush(new_feature, rgb_decimal_fill, rgb_decimal_fill, 2, alpha)

            # 设置边框 - 修复边框设置
            # mitab_c_set_pen 参数: 宽度, 图案, 颜色
            pen_width = max(1, round(float(outline_width)))
            lib.mitab_c_set_pen(new_feature, pen_width, 2, outline_color_rgb)



        except Exception as e:
            print(f"设置符号样式时出错: {e}")
            # 设置默认样式
            self.set_default_style(lib, new_feature)

    def set_default_style(self, lib, new_feature):
        """设置默认样式"""
        try:
            # 白色填充，黑色边框
            lib.mitab_c_set_brush(new_feature, 0xFFFFFF, 0xFFFFFF, 2, 255)
            lib.mitab_c_set_pen(new_feature, 1, 2, 0)

        except Exception as e:
            print(f"设置默认样式时出错: {e}")

    @staticmethod
    def setargtypes(lib):
        # 设置 mitab_c_create 函数的参数类型和返回类型
        lib.mitab_c_create.argtypes = [
            ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p,
            ctypes.c_double, ctypes.c_double, ctypes.c_double, ctypes.c_double
        ]
        lib.mitab_c_create.restype = ctypes.POINTER(ctypes.c_void_p)

        # 设置 mitab_c_add_field 函数的参数类型和返回类型
        lib.mitab_c_add_field.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_char_p, ctypes.c_int, ctypes.c_int,
            ctypes.c_int, ctypes.c_int, ctypes.c_int
        ]
        lib.mitab_c_add_field.restype = ctypes.c_long

        # 设置 mitab_c_create_feature 函数的参数类型和返回类型
        lib.mitab_c_create_feature.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_int
        ]
        lib.mitab_c_create_feature.restype = ctypes.POINTER(ctypes.c_void_p)

        # 设置 mitab_c_set_points 函数的参数类型和返回类型
        lib.mitab_c_set_points.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_double),
            ctypes.POINTER(ctypes.c_double)
        ]
        lib.mitab_c_set_points.restype = None

        # 设置 mitab_c_set_brush 函数的参数类型和返回类型
        lib.mitab_c_set_brush.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int
        ]
        lib.mitab_c_set_brush.restype = None

        # 设置 mitab_c_set_pen 函数的参数类型和返回类型
        lib.mitab_c_set_pen.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_int, ctypes.c_int
        ]
        lib.mitab_c_set_pen.restype = None

        # 设置 mitab_c_set_field 函数的参数类型
        lib.mitab_c_set_field.argtypes = [ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_char_p]
        lib.mitab_c_set_field.restype = None

        # 设置 mitab_c_write_feature 函数的参数类型和返回类型
        lib.mitab_c_write_feature.argtypes = [
            ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_void_p)
        ]
        lib.mitab_c_write_feature.restype = None

        # 设置 mitab_c_destroy_feature 函数的参数类型
        lib.mitab_c_destroy_feature.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
        lib.mitab_c_destroy_feature.restype = None

        # 设置 mitab_c_close 函数的参数类型
        lib.mitab_c_close.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
        lib.mitab_c_close.restype = None

        # 设置 mitab_c_getlasterrormsg 函数的参数类型和返回类型
        lib.mitab_c_getlasterrormsg.argtypes = []
        lib.mitab_c_getlasterrormsg.restype = ctypes.c_char_p

    def writertabfilecolumn_to_gbk(self, file_path, field_names):
        print("修改表头")
        # 读取原文件内容
        fields_list = []
        try:
            with open(file_path, 'r', encoding='utf-8', errors='replace') as file:
                content = file.readlines()
        except Exception as e:
            print(f"发生错误: {e}")
            self.updatelabel.emit(f'导出失败{e}')
            content = []

        if content:
            for i, line in enumerate(content):
                if line.strip().startswith("Fields"):
                    # 提取字段类型，将其写入数组
                    fields_list.extend(self.rx_to_str(line) for line in content[i + 1:] if line.strip())
                    break  # 找到后可结束循环

        # 字段和字段类型合并
        result = list(map(lambda x: f"\"{x[0]}\" {x[1]}", zip(field_names, fields_list)))
        # 合并格式，替换
        header = ("!table\n"
                  "!version 450\n"
                  "!charset WindowsSimpChinese\n\n"
                  "Definition Table\n"
                  "  Type NATIVE Charset \"WindowsSimpChinese\"\n"
                  "  Fields {}\n").format(len(field_names))

        fields_string = ""
        for field in result:
            fields_string += "    {}\n".format(field)
        table_definition = header + fields_string
        print(table_definition)
        try:
            with open(file_path, 'w', encoding='gbk', errors='replace') as file:
                file.write(table_definition)  # 写入字符串
        except Exception as e:
            print(f"发生错误: {e}")
            self.updatelabel.emit(f'导出失败{e}')

    @staticmethod
    def rx_to_str(textstr):
        data_types = [
            "Char",
            "Integer",
            "Smallint",
            "Float",
            "Decimal",
            "Date",
            "Time",
            "DateTime",
            "Logical"
        ]
        # 将数据类型转换为小写并创建正则表达式模式
        lower_data_types = [dtype.lower() for dtype in data_types]
        pattern = r'(' + '|'.join(re.escape(dtype) for dtype in lower_data_types) + r')\s*(\([^)]*\))?\s*;'

        # 将输入字符串转为小写
        lower_textstr = textstr.lower()

        # 从右向左查找匹配
        matches = re.findall(pattern, lower_textstr)

        # 如果找到匹配，则返回匹配项
        if matches:
            last_match = matches[-1]  # 获取最后一个匹配
            return f"{last_match[0]} {last_match[1] if last_match[1] else ''} ;".strip()  # 返回数据类型和可能的长度
        return None

    @staticmethod
    def wkt_to_coordinates(wkt):
        wkt = wkt.lower()
        # 正则表达式解析 WKT 多边形
        pattern = r'polygon \(\((.*?)\)\)'
        match = re.search(pattern, wkt)

        if not match:
            raise ValueError("Invalid WKT format")

        # 提取坐标字符串并分割
        coord_str = match.group(1).strip()
        points = coord_str.split(',')

        # 初始化坐标数组
        x_coords = []
        y_coords = []

        for point in points:
            # 去除空格并分割坐标
            coords = point.strip().split()
            if len(coords) == 2:
                x, y = float(coords[0]), float(coords[1])
                x_coords.append(x)
                y_coords.append(y)

        # 转换为 ctypes 类型
        x_array = (ctypes.c_double * len(x_coords))(*x_coords)
        y_array = (ctypes.c_double * len(y_coords))(*y_coords)

        return x_array, y_array