"""
Core calculation logic for GeoCalc.

Routes each layer to the appropriate calculation based on its geometry type,
performs ellipsoidal measurements via QgsDistanceArea.
"""

import csv
import os

from qgis.PyQt import uic
from qgis.PyQt.QtCore import QThread, pyqtSignal
from qgis.PyQt.QtWidgets import QFileDialog
from qgis.core import (
    QgsWkbTypes, QgsDistanceArea, QgsProject, QgsMapLayer
)
from qgis.utils import iface
from PyQt5 import QtCore, QtWidgets


# ─────────────────────────────────────────────
# CALCULATOR CLASS
# ─────────────────────────────────────────────

class Calculator:
    """
    Calculate geometric attributes for vector layers.

    Parameters
    ----------
    ellipsoid : str, optional
        Ellipsoid name (e.g. 'WGS84', 'GRS80'). Default is 'WGS84'.
    log_callback : callable, optional
        Function(message: str, level: str) called for each layer processed.
        level is one of 'info', 'success', 'warning', 'critical'.
    """

    def __init__(self, ellipsoid='WGS84', log_callback=None):
        self.ellipsoid = ellipsoid
        self.log = log_callback or (lambda msg, level='info': None)

    # ─────────────────────────────────────
    # AREA CALCULATION
    # ─────────────────────────────────────

    def calculate_area(self, layer, progress_callback=None):
        """
        计算多边形图层的面积（平方米）

        Parameters
        ----------
        layer : QgsVectorLayer
            多边形矢量图层
        progress_callback : callable, optional
            进度回调函数，接收当前索引和总数

        Returns
        -------
        list of dict
            每个要素的计算结果，包含原始属性和面积
        """
        if layer.geometryType() != QgsWkbTypes.PolygonGeometry:
            raise ValueError("图层不是多边形类型")

        d = self._make_distance_area(layer)

        # 获取所有字段名
        field_names = [field.name() for field in layer.fields()]
        results = []

        total = layer.featureCount()
        for idx, feature in enumerate(layer.getFeatures()):
            row = {}
            # 复制原始属性
            for field_name in field_names:
                row[field_name] = feature[field_name]

            # 计算面积
            geom = feature.geometry()
            area_m2 = 0.0
            if geom and not geom.isEmpty():
                area_m2 = d.measureArea(geom)
            row['area_m2'] = area_m2

            results.append(row)

            # 更新进度
            if progress_callback:
                progress_callback(idx + 1, total)

        self.log(f"✓ {layer.name()}: 面积计算完成，共 {total} 个要素", 'success')
        return results

    # ─────────────────────────────────────
    # LENGTH CALCULATION
    # ─────────────────────────────────────

    def calculate_length(self, layer, progress_callback=None):
        """
        计算线图层的长度（米）

        Parameters
        ----------
        layer : QgsVectorLayer
            线矢量图层
        progress_callback : callable, optional
            进度回调函数，接收当前索引和总数

        Returns
        -------
        list of dict
            每个要素的计算结果，包含原始属性和长度
        """
        if layer.geometryType() != QgsWkbTypes.LineGeometry:
            raise ValueError("图层不是线类型")

        d = self._make_distance_area(layer)

        # 获取所有字段名
        field_names = [field.name() for field in layer.fields()]
        results = []

        total = layer.featureCount()
        for idx, feature in enumerate(layer.getFeatures()):
            row = {}
            # 复制原始属性
            for field_name in field_names:
                row[field_name] = feature[field_name]

            # 计算长度
            geom = feature.geometry()
            length_m = 0.0
            if geom and not geom.isEmpty():
                length_m = d.measureLength(geom)
            row['length_m'] = length_m

            results.append(row)

            # 更新进度
            if progress_callback:
                progress_callback(idx + 1, total)

        self.log(f"✓ {layer.name()}: 长度计算完成，共 {total} 个要素", 'success')
        return results

    # ─────────────────────────────────────
    # COORDINATES CALCULATION (POINTS)
    # ─────────────────────────────────────

    def calculate_coordinates(self, layer, progress_callback=None):
        """
        计算点图层的坐标（X, Y）

        Parameters
        ----------
        layer : QgsVectorLayer
            点矢量图层
        progress_callback : callable, optional
            进度回调函数，接收当前索引和总数

        Returns
        -------
        list of dict
            每个要素的计算结果，包含原始属性和坐标
        """
        if layer.geometryType() != QgsWkbTypes.PointGeometry:
            raise ValueError("图层不是点类型")

        # 获取所有字段名
        field_names = [field.name() for field in layer.fields()]
        results = []

        total = layer.featureCount()
        for idx, feature in enumerate(layer.getFeatures()):
            row = {}
            # 复制原始属性
            for field_name in field_names:
                row[field_name] = feature[field_name]

            # 计算坐标
            geom = feature.geometry()
            x = None
            y = None
            if geom and not geom.isEmpty():
                # 处理单点和多点
                if geom.isMultipart():
                    points = geom.asMultiPoint()
                    if points:
                        pt = points[0]
                        x = pt.x()
                        y = pt.y()
                else:
                    pt = geom.asPoint()
                    x = pt.x()
                    y = pt.y()

            row['x'] = x
            row['y'] = y

            results.append(row)

            # 更新进度
            if progress_callback:
                progress_callback(idx + 1, total)

        self.log(f"✓ {layer.name()}: 坐标计算完成，共 {total} 个要素", 'success')
        return results

    # ─────────────────────────────────────
    # AUTO DETECT AND CALCULATE
    # ─────────────────────────────────────

    def calculate(self, layer, progress_callback=None):
        """
        自动检测图层类型并计算相应属性

        Parameters
        ----------
        layer : QgsVectorLayer
            矢量图层
        progress_callback : callable, optional
            进度回调函数，接收当前索引和总数

        Returns
        -------
        tuple (geom_type, results)
            geom_type: 'polygon', 'line', 'point'
            results: list of dict 计算结果
        """
        geom_type = layer.geometryType()

        if geom_type == QgsWkbTypes.PolygonGeometry:
            return 'polygon', self.calculate_area(layer, progress_callback)
        elif geom_type == QgsWkbTypes.LineGeometry:
            return 'line', self.calculate_length(layer, progress_callback)
        elif geom_type == QgsWkbTypes.PointGeometry:
            return 'point', self.calculate_coordinates(layer, progress_callback)
        else:
            raise ValueError(f"不支持的几何类型: {geom_type}")

    # ─────────────────────────────────────
    # UTILITIES
    # ─────────────────────────────────────

    def _make_distance_area(self, layer):
        """Return a QgsDistanceArea configured with the specified ellipsoid."""
        d = QgsDistanceArea()
        d.setSourceCrs(layer.crs(), QgsProject.instance().transformContext())
        if self.ellipsoid and self.ellipsoid != 'NONE':
            d.setEllipsoid(self.ellipsoid)
        return d


# ─────────────────────────────────────────────
# EXPORT THREAD
# ─────────────────────────────────────────────

class ExportThread(QThread):
    progress = pyqtSignal(int)
    finished = pyqtSignal()
    error = pyqtSignal(str)

    def __init__(self, selected_layer, csv_file_path):
        super().__init__()
        self.selected_layer = selected_layer
        self.csv_file_path = csv_file_path

    def run(self):
        try:
            # 创建计算器实例（使用WGS84椭球体）
            calculator = Calculator(ellipsoid='WGS84')

            # 定义进度回调
            def progress_callback(current, total):
                progress_value = int(current / total * 100)
                self.progress.emit(progress_value)

            # 根据图层类型计算
            geom_type, results = calculator.calculate(
                self.selected_layer,
                progress_callback
            )

            # 写入CSV文件
            with open(self.csv_file_path, mode='w', newline='', encoding='utf-8-sig') as csv_file:
                if results:
                    # 获取所有字段名
                    field_names = list(results[0].keys())
                    writer = csv.DictWriter(csv_file, fieldnames=field_names)
                    writer.writeheader()
                    writer.writerows(results)

            self.finished.emit()

        except Exception as e:
            self.error.emit(str(e))


# ─────────────────────────────────────────────
# MAIN GUI CLASS
# ─────────────────────────────────────────────

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


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

    def __init__(self, parent=None):
        super(cyanlove_calc_length, self).__init__(parent)

        self.export_thread = None
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.setupUi(self)
        self.setFloating(True)
        self.property_dict = None
        self.is_running = False
        self.setWindowFlags(QtCore.Qt.Dialog)
        self.btn_calc_area.clicked.connect(self.calc_area)

    def calc_area(self):
        options = QFileDialog.Options()
        options |= QFileDialog.ReadOnly
        csv_file_path, _ = QFileDialog.getSaveFileName(
            self,
            "保存CSV文件",
            "",
            "CSV文件 (*.csv);;所有文件 (*)",
            options=options
        )

        if csv_file_path:
            current_layer = self.mMapLayerComboBox.currentLayer()
            if current_layer is None or current_layer.type() != QgsMapLayer.VectorLayer:
                self.label_show.setText("选择的图层不是矢量图层！")
                return

            # 显示进度条
            self.progressBar.setRange(0, 100)
            self.progressBar.setValue(0)
            self.progressBar.show()

            geom_type = current_layer.wkbType()
            # 支持 Polygon、MultiPolygon、Line、MultiLineString、Point、MultiPoint
            if geom_type in [QgsWkbTypes.Polygon, QgsWkbTypes.MultiPolygon,
                             QgsWkbTypes.LineString, QgsWkbTypes.MultiLineString,
                             QgsWkbTypes.Point, QgsWkbTypes.MultiPoint]:
                # 启动导出线程
                self.export_thread = ExportThread(current_layer, csv_file_path)
                self.export_thread.progress.connect(self.update_progress)
                self.export_thread.finished.connect(self.export_finished)
                self.export_thread.error.connect(self.export_error)
                self.export_thread.start()
            else:
                self.label_show.setText("不支持的图层类型！请使用点、线、面图层")

    def update_progress(self, value):
        self.progressBar.setValue(value)

    def export_finished(self):
        self.label_show.setText('导出完毕')
        self.progressBar.setValue(100)

    def export_error(self, error_msg):
        self.label_show.setText(f'导出失败: {error_msg}')
        self.progressBar.setValue(0)