# -*- coding: utf-8 -*-
"""
严格模仿 PostgreSQL 函数 ssj_get_convex_hull_from_polygon_v2
递归凸包简化，仅当 WKT 长度 > 32767 时处理，直到 ≤ 32767 或达到最大深度。
输出 CSV，WKT 精度 6 位小数。
"""

import os
import traceback
import csv
import math
from qgis.core import (
    QgsGeometry, QgsVectorLayer, QgsProject, QgsFeature,
    QgsWkbTypes, QgsMapLayerProxyModel, QgsPointXY
)
from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import (
    QDockWidget, QApplication, QFileDialog, QProgressBar
)

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


class cyanlove_polygonwkt_toexcel_geometry(QDockWidget, FORM_CLASS):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.init_ui()
        self.btn_calc_area.clicked.connect(self.export_to_csv)
        self.progressBar.setVisible(False)

    def init_ui(self):
        self.setWindowTitle("简化导出CSV")
        self.mMapLayerComboBox.setFilters(QgsMapLayerProxyModel.PolygonLayer)
        self.btn_calc_area.setText("简化边界集并导出CSV")
        self.label_show.setText(
            "说明：若多边形WKT长度 ≤ 32767，直接输出原始几何,若超过限制，递归计算凸包（使用叉积筛选凸点）直到长度符合要求"
        )

    def update_status_message(self, message, is_error=False):
        style_error = "color:#d9534f; background-color:#f2dede; padding:6px; border-radius:4px; border-left:3px solid #d9534f;"
        style_normal = "color:#5a6c7d; background-color:#f0f4f8; padding:6px; border-radius:4px; border-left:3px solid #4a6fa5;"
        self.label_show.setStyleSheet(style_error if is_error else style_normal)
        self.label_show.setText(message)
        QApplication.processEvents()

    def export_to_csv(self):
        layer = self.mMapLayerComboBox.currentLayer()
        if not layer:
            self.update_status_message("❌ 请先选择一个多边形图层！", is_error=True)
            return
        if layer.geometryType() != QgsWkbTypes.PolygonGeometry:
            self.update_status_message("❌ 请选择多边形图层！", is_error=True)
            return

        file_path, _ = QFileDialog.getSaveFileName(self, "保存CSV文件", "", "CSV文件 (*.csv)")
        if not file_path:
            return

        self.progressBar.setVisible(True)
        self.progressBar.setValue(0)
        self.btn_calc_area.setEnabled(False)
        self.update_status_message(f"⏳ 正在处理图层：{layer.name()} ...")

        QApplication.processEvents()

        try:
            data_rows, headers = self.process_layer(layer)
            self.save_to_csv(data_rows, headers, file_path)
            self.update_status_message(f"✅ 导出成功！共处理 {len(data_rows)} 个要素")
        except Exception as e:
            self.update_status_message(f"❌ 处理出错：{str(e)}", is_error=True)
            print(traceback.format_exc())
        finally:
            self.progressBar.setVisible(False)
            self.btn_calc_area.setEnabled(True)

    def process_layer(self, layer):
        total = layer.featureCount()
        original_fields = [f.name() for f in layer.fields()]
        headers = original_fields + ["simplified_wkt", "simplify_depth"]
        data_rows = []

        for idx, feat in enumerate(layer.getFeatures()):
            progress = int((idx + 1) / total * 100)
            self.progressBar.setValue(progress)
            if progress % 20 == 0:
                self.update_status_message(f"⏳ 处理中：{idx+1}/{total} ({progress}%)")
            QApplication.processEvents()

            geom = feat.geometry()
            if geom is None or geom.isEmpty():
                row = self.build_row(feat, original_fields, "", -1)
                data_rows.append(row)
                continue

            # 先检查原始WKT长度（6位精度）
            wkt6 = geom.asWkt(6)
            if len(wkt6) <= 32767:
                out_wkt = wkt6
                depth = 0
            else:
                simplified_geom, depth = self.recursive_convex_hull(geom, max_depth=10)
                if simplified_geom is None:
                    simplified_geom = geom
                    depth = -1
                out_wkt = simplified_geom.asWkt(6)

            row = self.build_row(feat, original_fields, out_wkt, depth)
            data_rows.append(row)

        return data_rows, headers

    def build_row(self, feature, original_fields, wkt, depth):
        row = []
        for fname in original_fields:
            try:
                val = feature[fname]
            except:
                val = None
            if isinstance(val, (float, int, str)):
                row.append(val)
            else:
                row.append(str(val) if val is not None else "")
        row.append(wkt)
        row.append(depth)
        return row

    def recursive_convex_hull(self, geometry, max_depth=10, depth=0):
        """
        递归凸包：每次计算当前几何的凸包（SQL 风格凸点筛选），检查 WKT 长度。
        若长度 ≤ 32767 或达到最大深度，停止并返回当前凸包及深度。
        否则继续递归。
        """
        if len(geometry.asWkt(6)) <= 32767:
            return geometry, depth
        if depth >= max_depth:
            return geometry, depth

        convex_geom = self.compute_convex_hull_sql_style(geometry)
        if convex_geom is None or convex_geom.isEmpty():
            return geometry, depth

        if len(convex_geom.asWkt(6)) <= 32767:
            return convex_geom, depth + 1
        else:
            return self.recursive_convex_hull(convex_geom, max_depth, depth + 1)

    @staticmethod
    def compute_convex_hull_sql_style(geometry):
        """
        输入: QgsGeometry (Polygon 或 MultiPolygon)
        输出: QgsGeometry (Polygon) 凸包，若失败返回 None
        """
        # 提取外环点（取第一个多边形，实际可根据需求处理多个）
        if geometry.isMultipart():
            polygons = geometry.asMultiPolygon()
            if not polygons:
                return None
            poly = polygons[0]  # 取第一个多边形
        else:
            poly = geometry.asPolygon()
        if not poly or not poly[0]:
            return None

        points = poly[0][:-1]  # 去除最后一个重复闭合点
        n = len(points)
        if n < 3:
            return None

        # 计算有符号面积，判断方向（逆时针为正）
        area = 0.0
        for i in range(n):
            x1, y1 = points[i].x(), points[i].y()
            x2, y2 = points[(i+1)%n].x(), points[(i+1)%n].y()
            area += x1 * y2 - x2 * y1
        orientation = 'CCW' if area > 0 else 'CW'

        # 筛选凸点：计算每个点的前后向量叉积
        convex_indices = []
        for i in range(n):
            prev = points[(i-1)%n]
            curr = points[i]
            nxt = points[(i+1)%n]
            cross = (curr.x() - prev.x()) * (nxt.y() - prev.y()) - (curr.y() - prev.y()) * (nxt.x() - prev.x())
            if (orientation == 'CCW' and cross > 0) or (orientation == 'CW' and cross < 0):
                convex_indices.append(i)

        if len(convex_indices) < 3:
            return None

        # 按原始顺序取出凸点
        convex_points = [points[i] for i in convex_indices]
        # 闭合
        convex_points.append(convex_points[0])
        # 构造 QgsGeometry
        convex_geom = QgsGeometry.fromPolygonXY([convex_points])
        return convex_geom

    def save_to_csv(self, data_rows, headers, file_path):
        with open(file_path, 'w', newline='', encoding='gbk') as f:
            writer = csv.writer(f)
            writer.writerow(headers)
            writer.writerows(data_rows)

    def closeEvent(self, event):
        event.accept()