import xarray as xr
import numpy as np
import sys
import ctypes
from datetime import datetime, timedelta
# from WindDataHandler import DataFileInfo,WindDataHandler
from PySide6.QtWidgets import (QApplication, QMainWindow, QPushButton,
                               QVBoxLayout, QWidget, QProgressDialog)
from PySide6.QtCore import QTimer, Qt
from PySide6.QtWidgets import QMessageBox
import argparse


import ctypes

# 定义与 C++ 中 DataFileInfo 对应的结构体
class DataFileInfo(ctypes.Structure):
    _fields_ = [
        ("Height", ctypes.c_int64),
        ("Width", ctypes.c_int64),
        ("Num", ctypes.c_int64),
        ("firstTimestamp", ctypes.c_int64),
        ("lastTimestamp", ctypes.c_int64),
        ("minLon", ctypes.c_double),
        ("maxLon", ctypes.c_double),
        ("minLat", ctypes.c_double),
        ("maxLat", ctypes.c_double),
        ("ESPGCODE", ctypes.c_int64),
        ("T11", ctypes.c_double),
        ("T12", ctypes.c_double),
        ("T13", ctypes.c_double),
        ("T21", ctypes.c_double),
        ("T22", ctypes.c_double),
        ("T23", ctypes.c_double),
        ("fileSize", ctypes.c_int64)
    ]


class WindDataHandler:
    def __init__(self, dll_path='./LAMPWindData.dll'):
        self.dll_path = dll_path
        self.wind_lib = ctypes.CDLL(dll_path)
        self._setup_prototypes()
        self.wind_lib.getDataFileInfo.argtypes = [ctypes.c_char_p]  # 参数是 const char*
        self.wind_lib.getDataFileInfo.restype = DataFileInfo  # 返回类型是 DataFileInfo 结构体
        self.current_file_info = None


    def _setup_prototypes(self):
        """设置 DLL 函数的参数和返回类型"""
        # ... 函数原型设置代码见上一节，此处省略 ...

    def get_data_file_info(self, filepath):
        """获取风场数据文件信息"""
        # 将Python字符串转换为字节字符串
        filepath_bytes = filepath.encode('utf-8')
        info = self.wind_lib.getDataFileInfo(filepath_bytes)
        self.current_file_info = info
        return info

    def get_wind_data_file_time_arr(self, filepath, info):
        """获取时间数组"""
        filepath_bytes = filepath.encode('utf-8')
        if info.Num <= 0:
            return []

        # 创建足够大的数组来存储时间戳
        time_arr = (ctypes.c_int64 * info.Num)()
        result = self.wind_lib.get_WindDataFileTimeArr(filepath_bytes, info, time_arr)

        if result == 0:  # 假设返回0表示成功
            return list(time_arr)
        else:
            print(f"Error getting time array: {result}")
            return []

    def read_wind_data_file(self, filepath, info, time_id):
        """读取指定时间ID的风场数据 (U/V分量)"""
        filepath_bytes = filepath.encode('utf-8')
        layer_size = info.Height * info.Width

        # 创建数组来存储U和V分量数据
        u_arr = (ctypes.c_double * layer_size)()
        v_arr = (ctypes.c_double * layer_size)()

        result = self.wind_lib.Read_WindDataFile(filepath_bytes, info, time_id, u_arr, v_arr)

        if result >= 0:
            # 将ctypes数组转换为Python列表或numpy数组（如果可用）
            return list(u_arr), list(v_arr)
        else:
            print(f"Error reading wind data: {result}")
            return None, None

    def write_wind_data_file(self, filepath, info, timeInt64,time_id, u_arr, v_arr):
        """将风场数据写入文件"""
        filepath_bytes = filepath.encode('utf-8')
        layer_size = info.Height * info.Width

        # 确保输入数据长度正确
        if len(u_arr) != layer_size or len(v_arr) != layer_size:
            raise ValueError(f"UArr and VArr must have length {layer_size}")

        # 将Python列表转换为ctypes数组
        u_arr_ctypes = (ctypes.c_double * layer_size)(*u_arr)
        v_arr_ctypes = (ctypes.c_double * layer_size)(*v_arr)

        result = self.wind_lib.Write_WindDataFile(filepath_bytes, info, timeInt64,time_id, u_arr_ctypes, v_arr_ctypes)
        return result
















class ERA5ToWindDataConverter:
    def __init__(self, dll_path='./LAMPWindData.dll'):
        self.wind_handler = WindDataHandler(dll_path)

    def read_era5_data(self, nc_file_path):
        """读取ERA5数据文件"""
        try:
            # 使用xarray读取NetCDF文件[6,8](@ref)
            ds = xr.open_dataset(nc_file_path)
            print("ERA5数据集变量:", list(ds.variables.keys()))

            # 提取u10和v10分量（10米高度风场）[1,5](@ref)
            u10 = ds['u10'].values  # 东西风分量
            v10 = ds['v10'].values  # 南北风分量
            lon = ds['longitude'].values  # 经度
            lat = ds['latitude'].values  # 纬度
            time = ds['time'].values  # 时间

            ds.close()

            return u10, v10, lon, lat, time
        except Exception as e:
            print(f"读取ERA5数据失败: {e}")
            return None, None, None, None, None


    def create_data_file_info(self, lon, lat, time):
        """创建DataFileInfo结构体"""
        info = DataFileInfo()

        # 设置网格尺寸[1](@ref)
        info.Height = len(lat)  # 纬度维度
        info.Width = len(lon)  # 经度维度
        info.Num = len(time)  # 时间步数

        # 设置地理范围[1](@ref)
        info.minLon = float(lon.min())
        info.maxLon = float(lon.max())
        info.minLat = float(lat.min())
        info.maxLat = float(lat.max())


        time_deltas = time
        info.firstTimestamp = int(time_deltas[0])
        info.lastTimestamp = int(time_deltas[-1])

        # 设置坐标系（WGS84的EPSG代码为4326）
        info.ESPGCODE = 4326

        # 变换参数（单位矩阵表示无变换）
        info.T11, info.T12, info.T13 = lon[0],lon[1]-lon[0],0.0 # 计算 lon X_geo = GT(0) + X_col * GT(1) + Y_row * GT(2)
        info.T21, info.T22, info.T23 = lat[0], 0.0, lat[1]-lat[0] # 计算 lat Y_geo = GT(3) + X_col * GT(4) + Y_row * GT(5)

        return info

    def convert_era5_time(self, time_values):
        """转换ERA5时间格式为datetime对象[1,5](@ref)"""
        # ERA5时间通常是从1900-01-01开始的小时数
        return [time_val.astype("int64") for time_val in time_values]
        # base_time = datetime(1900, 1, 1, 0, 0, 0)
        #
        # converted_times = []
        # for time_val in time_values:
        #     if isinstance(time_val, np.datetime64):
        #         # 如果是numpy datetime64格式
        #         dt = time_val.astype(datetime)
        #     else:
        #         # 如果是数值格式（从1900-01-01开始的小时数）
        #         hours = float(time_val)
        #         dt = base_time + timedelta(hours=hours)
        #     converted_times.append(dt)
        #
        # return converted_times

    def convert_era5_to_winddata(self, era5_file, output_file):
        """将ERA5数据转换为WindData格式"""
        # 1. 读取ERA5数据
        u10, v10, lon, lat, time = self.read_era5_data(era5_file)
        if u10 is None:
            return False

        # 2. 转换时间格式
        time_datetime = self.convert_era5_time(time)


        # 4. 创建文件信息头
        file_info = self.create_data_file_info(lon, lat, time_datetime)

        # 5. 写入WindData文件
        try:
            progress_dialog = QProgressDialog(
                "文件写入进度，请稍候...",  # 标签文本
                "取消",  # 取消按钮文本
                0, len(time_datetime),  # 最小值和最大值
                None  # 父窗口
            )
            progress_dialog.setWindowTitle("文件写入进度，请稍候...")
            progress_dialog.setFixedSize(500, 50)
            progress_dialog.setCancelButton(None)
            progress_dialog.setWindowModality(Qt.NonModal)
            progress_dialog.setMinimumDuration(0)
            progress_dialog.show()
            # 逐个时间步写入数据
            for time_idx in range(len(time_datetime)):
                if progress_dialog is None or not progress_dialog.isVisible() or progress_dialog.wasCanceled():
                    print("窗口已关闭")
                    continue
                # 获取当前时间步的u、v分量
                u_slice = u10[time_idx, :, :].flatten()
                v_slice = v10[time_idx, :, :].flatten()

                timeInt=time_datetime[time_idx]
                print("时间：",timeInt)
                # 写入数据
                result = self.wind_handler.write_wind_data_file(
                    output_file, file_info,ctypes.c_int64(timeInt), time_idx, list(u_slice), list(v_slice)
                )
                progress_dialog.setValue(time_idx)
                QApplication.processEvents()
                if result != 0:
                    print(f"写入时间步 {time_idx} 失败: {result}")
                    return False

            print(f"转换成功！输出文件: {output_file}")
            if progress_dialog is None or not progress_dialog.isVisible() or progress_dialog.wasCanceled():
                return True
            else:
                progress_dialog.close()
            return True

        except Exception as e:
            print(f"转换过程出错: {e}")
            QMessageBox.critical(None, "错误", f"文件保存错误: {e}\n{output_file}")
            return False

    def compareResult(self,era5_file, bin_file):
        u10, v10, lon, lat, time = self.read_era5_data(era5_file)
        time_datetime = self.convert_era5_time(time)
        info = converter.wind_handler.get_data_file_info(bin_file)
        print(f"文件信息: {info.Height}x{info.Width} 网格, {info.Num} 个时间步")
        # 获取时间数组
        time_arr = converter.wind_handler.get_wind_data_file_time_arr(bin_file, info)
        print(f"时间范围: {time_arr[0]} 到 {time_arr[-1]}")

        print("比对时间")
        for tid in range(info.Num):
            if time_arr[tid]-time_datetime[tid]==0:
                continue
            else:
                print("时间错误，",time_arr[tid],time_datetime[tid])

        print("比对实际数据值")

        for tid in range(info.Num):
            u_slice = u10[tid, :, :].flatten()
            v_slice = v10[tid, :, :].flatten()

            u_wind,v_wind=self.wind_handler.read_wind_data_file(bin_file,info,tid)

            u_wind=np.array(u_wind)
            v_wind=np.array(v_wind)

            u_d=u_slice-u_wind
            v_d=v_slice-v_wind

            u_d_min=np.max(np.abs(u_d))
            v_d_min=np.max(np.abs(v_d))

            if u_d_min>0 or v_d_min>0:
                print("tid:(u_d,v_d)",tid,u_d_min,v_d_min)


        pass


def getParams():
    parser = argparse.ArgumentParser()
    parser.add_argument('-i', '--infile', type=str,default=r"D:\Programs\SpacetyLabelAIPlante\Manual-Labeling-Tool\WindFileData\data\total_precipitation202001.nc",help='输入ERA5 的netcdf文件')
    parser.add_argument('-o', '--outfile', type=str, default=r"D:\Programs\SpacetyLabelAIPlante\Manual-Labeling-Tool\WindFileData\output\converted_wind_data.bin",help='输出自定义风场文件')
    parser.add_argument('-d', '--dllfile', type=str, default=r"D:\Programs\SpacetyLabelAIPlante\Manual-Labeling-Tool\WindFileData\dllFolder\WindDataOperator.dll",help='输出自定义风场文件')

    args = parser.parse_args()

    return args


# 使用示例
if __name__ == "__main__":

    app = QApplication(sys.argv)

    parser = getParams()
    dllpath=parser.dllfile
    ncpath=parser.infile
    binpath=parser.outfile
    # 创建转换器实例
    converter = ERA5ToWindDataConverter(dllpath)

    # 执行转换
    era5_file = ncpath  # ERA5输入文件
    output_file = binpath  # 输出文件

    success = converter.convert_era5_to_winddata(era5_file, output_file)
    print('写入结果',success)
    if success:
        QMessageBox.information(None, "操作成功", "文件保存成功！\n{}".format(output_file))
        # 验证转换结果
        info = converter.wind_handler.get_data_file_info(output_file)
        print(f"文件信息: {info.Height}x{info.Width} 网格, {info.Num} 个时间步")
    else:
        # 错误框
        pass

    sys.exit(app.exec())









