package com.njcn.gather.tools.comtrade.comparewave.core.algorithm;

import com.njcn.gather.tools.comtrade.comparewave.core.model.ClockStruct;
import com.njcn.gather.tools.comtrade.comparewave.core.model.DataPq;
import lombok.extern.slf4j.Slf4j;

import static com.njcn.gather.tools.comtrade.comparewave.core.Constants.*;
import static com.njcn.gather.tools.comtrade.comparewave.core.algorithm.FloatMath.*;


/**
 * 基础库函数
 * <p>对应C代码：lib.c</p>
 * <p><b>重要：所有计算必须与C代码完全一致</b></p>
 * <p>提供电能质量分析所需的基础数学计算和工具函数</p>
 * 
 * @author hongawen
 * @since 1.0
 */
@Slf4j
public class LibraryFunctions {
    
    /** 月份天数查找表 */
    private static final int[][] DATE_IN_MONTH = {
        {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
        {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };
    
    /**
     * 计算有效值（RMS）
     * <p>对应C代码：rms_cal</p>
     * <p>使用滑动窗口计算数据的均方根值</p>
     * 
     * @param in 输入数据数组
     * @param out 输出结果数组
     * @param smp 滑动窗口大小
     * @param len 数据长度
     * @return 计算状态码（0表示成功）
     */
    public static int rmsCal(float[] in, float[] out, int smp, int len) {
        float data;
        
        // 从第一个完整窗口开始计算
        for (int i = (smp - 1); i < len; i++) {
            data = 0;
            for (int j = 0; j < smp; j++) {
                data += in[i - j] * in[i - j];
            }
            out[i] = sqrt(data / smp);
        }
        
        // 用第一个有效值填充前面的位置
        for (int i = 0; i < (smp - 1); i++) {
            out[i] = out[smp - 1];
        }
        
        return 0;
    }
    
    /**
     * 判断是否为闰年
     * <p>对应C代码：look_leap_year</p>
     * <p>按照公历闰年规则：4年一闰，百年不闰，四百年又闰</p>
     * 
     * @param year 年份
     * @return 是否为闰年
     */
    public static boolean lookLeapYear(int year) {
        boolean leapYear = false;
        
        if ((year % 4) == 0) {
            if ((year % 100) == 0) {
                if (year % 400 == 0) {
                    leapYear = true;
                }
            } else {
                leapYear = true;
            }
        }
        
        return leapYear;
    }
    
    /**
     * 时钟转换为秒
     * <p>对应C代码：clock_to_second</p>
     * <p>将指定日期时间转换为从基准年份开始的秒数</p>
     * 
     * @param year 年份
     * @param month 月份
     * @param day 日期
     * @param hour 小时
     * @param minute 分钟
     * @param second 秒数
     * @param sinceYear 基准年份
     * @return 总秒数
     */
    public static int clockToSecond(int year, int month, int day, 
                                    int hour, int minute, int second, int sinceYear) {
        int leap;
        int secs = 0;
        int year0 = year;
        
        // 计算年份差值对应的秒数
        while (year0 > sinceYear) {
            year0--;
            leap = lookLeapYear(year0) ? 1 : 0;
            secs += (int)(SEC_PER_365_DAY + leap * SEC_PER_DAY);
        }
        
        // 计算月份对应的秒数
        leap = lookLeapYear(year) ? 1 : 0;
        if (month > 0) {
            month--;
        }
        while (month > 0) {
            secs += (int)(SEC_PER_DAY * DATE_IN_MONTH[leap][month - 1]);
            month--;
        }
        
        // 计算日期对应的秒数
        if (day > 0) {
            day--;
        }
        secs += (int)(day * SEC_PER_DAY);
        
        // 计算小时对应的秒数
        secs += (int)(hour * SEC_PER_HOUR);
        
        // 计算分钟对应的秒数
        secs += (int)(minute * SEC_PER_MIN);
        
        secs += second;
        
        return secs;
    }
    
    /**
     * 秒转换为时钟
     * <p>对应C代码：second_to_clock</p>
     * <p>将秒数转换为时间结构体，包含北京时间校正</p>
     * 
     * @param sec 秒数
     * @return 时间结构体
     */
    public static ClockStruct secondToClock(int sec) {
        ClockStruct clock = new ClockStruct();
        
        // 调整为北京时区（UTC+8）
        long lt = sec - 8 * 60 * 60;
        
        // 使用Java Calendar进行时间解析
        java.util.Date date = new java.util.Date(lt * 1000L);
        java.util.Calendar cal = java.util.Calendar.getInstance();
        cal.setTime(date);
        
        clock.setYear(cal.get(java.util.Calendar.YEAR));
        clock.setMonth(cal.get(java.util.Calendar.MONTH) + 1);
        clock.setDay(cal.get(java.util.Calendar.DAY_OF_MONTH));
        clock.setHour(cal.get(java.util.Calendar.HOUR_OF_DAY));
        clock.setMinute(cal.get(java.util.Calendar.MINUTE));
        clock.setSecond(cal.get(java.util.Calendar.SECOND));
        
        return clock;
    }
    
    /**
     * 生成测试采样数据
     * <p>对应C代码：smp_test_data_init</p>
     * <p>生成标准的正弦波测试数据，包含基波和谐波分量</p>
     * 
     * @param dataBuf 数据缓冲区
     */
    public static void smpTestDataInit(DataPq dataBuf) {
        float r, ang, wt, t, freq;
        
        // 初始化测试数据的幅值和相位数组
        float[][] testDataRms = new float[6][50];
        float[][] testDataAng = new float[6][50];
        int[][] smvTestData = new int[2560][6];
        int smvTestDatNum = 2560;
        int smpRate = 256;
        
        // 清零所有数组元素
        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 50; j++) {
                testDataRms[i][j] = 0;
                testDataAng[i][j] = 0;
            }
        }
        
        // 设置三相电压电流的初始相位
        testDataAng[0][0] = 0;
        testDataAng[1][0] = -120;
        testDataAng[2][0] = 120;
        testDataAng[3][0] = 0;
        testDataAng[4][0] = -120;
        testDataAng[5][0] = 120;
        
        // 设置基波幅值
        testDataRms[0][0] = 50.000f * CAL_XS;
        testDataRms[1][0] = 50.000f * CAL_XS;
        testDataRms[2][0] = 50.000f * CAL_XS;
        testDataRms[3][0] = 5f * CAL_XS;
        testDataRms[4][0] = 5f * CAL_XS;
        testDataRms[5][0] = 5f * CAL_XS;
        
        // 设置谐波分量幅值（1%的基波）
        for (int i = 0; i < 6; i++) {
            for (int j = 1; j < 50; j++) {
                testDataRms[i][j] = (float)(testDataRms[i][0] * 0.01);
                testDataAng[i][j] = testDataAng[i][0];
            }
        }
        
        // 根据频域参数生成时域采样数据
        freq = 50.0f;
        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < smvTestDatNum; j++) {
                r = 0;
                for (int k = 0; k < 50; k++) {
                    ang = testDataAng[i][k] * 2.0f * PI / 360.0f;
                    t = (float)j / (50.0f * smpRate);
                    wt = 2.0f * PI * freq * (k + 1) * t + ang;
                    r += sin(wt) * 1.41421356f * testDataRms[i][k];
                }
                smvTestData[j][i] = (int)r;
            }
        }
        
        // 将生成的数据填入缓冲区
        dataBuf.setSmpNum(0);
        dataBuf.setSmpRate(256);
        dataBuf.getCfg().setHarmTime(50);
        dataBuf.setF(50);
        dataBuf.setUn(100.0f);
        
        for (int j = 0; j < smvTestDatNum; j++) {
            for (int i = 0; i < 6; i++) {
                dataBuf.getSmpData()[i][j] = smvTestData[j][i];
            }
            dataBuf.setSmpNum(dataBuf.getSmpNum() + 1);
        }
        
        for (int i = 0; i < 6; i++) {
            dataBuf.getUiGainXs()[i] = 0.01f;
        }
    }
    
    /**
     * 计算频率（从采样数据）
     * <p>对应C代码：smp_to_freq</p>
     * <p>使用过零检测法从采样数据中提取信号频率</p>
     * 
     * @param data 采样数据数组
     * @param smpRate 采样率
     * @param smpNum 数据点数
     * @return 信号频率（Hz）
     */
    public static float smpToFreq(int[] data, int smpRate, int smpNum) {
        // 使用过零检测算法计算频率
        int zeroCrossings = 0;
        int lastSign = 0;
        
        for (int i = 1; i < smpNum; i++) {
            int currentSign = data[i] >= 0 ? 1 : -1;
            if (i > 0) {
                int prevSign = data[i-1] >= 0 ? 1 : -1;
                if (prevSign != currentSign) {
                    zeroCrossings++;
                }
            }
        }
        
        // 根据过零次数和时间窗口计算频率
        float timeWindow = (float)smpNum / smpRate;
        float freq = zeroCrossings / (2.0f * timeWindow);
        
        return freq;
    }
}