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;

/**
 * 波形对齐器
 * 对应C代码：main_pro.c中的find_start_pos函数
 * <p>
 * 实现步骤：
 * 1. 根据CFG文件的时间戳，找到两个波形中时间较晚的作为基准
 * 2. 计算时间差，转换为采样点偏移
 * 3. 从计算出的位置开始，在两个波形中分别寻找A相电压从负到正的过零点
 * 4. 以找到的过零点作为真正的对齐位置
 * 5. 从对齐位置开始进行200ms窗口的电能质量计算
 * @author hongawen
 */
@Slf4j
public class WaveformAligner {

    /**
     * 对齐结果结构
     * 包含两个波形对齐后的位置信息、时间信息和计算参数
     */
    public static class AlignmentResult {
        /** 第一个波形的最终对齐开始位置（过零点位置） */
        private int startPos1;
        
        /** 第二个波形的最终对齐开始位置（过零点位置） */
        private int startPos2;
        
        /** 第一个波形基于时间戳计算的初始位置 */
        private int timeStartPos1;
        
        /** 第二个波形基于时间戳计算的初始位置 */
        private int timeStartPos2;
        
        /** 第一个波形的计算开始时间 */
        private ClockStruct startTime1;
        
        /** 第二个波形的计算开始时间 */
        private ClockStruct startTime2;
        
        /** 可计算的200ms窗口数量，每个窗口包含10个周波 */
        private int calNum;
        
        /** 对齐操作是否成功 */
        private boolean success;
        
        /** 对齐失败时的错误信息 */
        private String errorMessage;

        // Getters and Setters
        public int getStartPos1() {
            return startPos1;
        }

        public void setStartPos1(int startPos1) {
            this.startPos1 = startPos1;
        }

        public int getStartPos2() {
            return startPos2;
        }

        public void setStartPos2(int startPos2) {
            this.startPos2 = startPos2;
        }

        public int getTimeStartPos1() {
            return timeStartPos1;
        }

        public void setTimeStartPos1(int timeStartPos1) {
            this.timeStartPos1 = timeStartPos1;
        }

        public int getTimeStartPos2() {
            return timeStartPos2;
        }

        public void setTimeStartPos2(int timeStartPos2) {
            this.timeStartPos2 = timeStartPos2;
        }

        public ClockStruct getStartTime1() {
            return startTime1;
        }

        public void setStartTime1(ClockStruct startTime1) {
            this.startTime1 = startTime1;
        }

        public ClockStruct getStartTime2() {
            return startTime2;
        }

        public void setStartTime2(ClockStruct startTime2) {
            this.startTime2 = startTime2;
        }

        public int getCalNum() {
            return calNum;
        }

        public void setCalNum(int calNum) {
            this.calNum = calNum;
        }

        public boolean isSuccess() {
            return success;
        }

        public void setSuccess(boolean success) {
            this.success = success;
        }

        public String getErrorMessage() {
            return errorMessage;
        }

        public void setErrorMessage(String errorMessage) {
            this.errorMessage = errorMessage;
        }
    }

    /**
     * 寻找波形对齐的起始位置
     * 对应C代码：find_start_pos函数
     *
     * @param data1 第一个波形数据
     * @param data2 第二个波形数据
     * @return 对齐结果
     */
    public static AlignmentResult findStartPosition(DataPq data1, DataPq data2) {
        AlignmentResult result = new AlignmentResult();

        try {
            // 获取两个波形的记录开始时间戳
            ClockStruct time1 = data1.getLbStartTime();
            ClockStruct time2 = data2.getLbStartTime();

            if (time1 == null || time2 == null) {
                result.setSuccess(false);
                result.setErrorMessage("时间戳信息缺失");
                return result;
            }

            log.info("波形1开始时间: {}-{}-{} {}:{}:{}.{}",
                    time1.getYear(), time1.getMonth(), time1.getDay(),
                    time1.getHour(), time1.getMinute(), time1.getSecond(), time1.getMicroSecond() / 1000);
            log.info("波形2开始时间: {}-{}-{} {}:{}:{}.{}",
                    time2.getYear(), time2.getMonth(), time2.getDay(),
                    time2.getHour(), time2.getMinute(), time2.getSecond(), time2.getMicroSecond() / 1000);

            // 将时间转换为秒数（从1970年开始）
            int seconds1 = LibraryFunctions.clockToSecond(
                    time1.getYear(), time1.getMonth(), time1.getDay(),
                    time1.getHour(), time1.getMinute(), time1.getSecond(), 1970);
            int seconds2 = LibraryFunctions.clockToSecond(
                    time2.getYear(), time2.getMonth(), time2.getDay(),
                    time2.getHour(), time2.getMinute(), time2.getSecond(), 1970);

            /*
             * 确定哪个时间更晚作为参考基准
             * ref=1表示time2更晚，ref=0表示time1更晚或相等
             */
            int ref = 0;
            int diffMs = 0;

            if (seconds1 < seconds2) {
                ref = 1;
            } else if (seconds1 == seconds2) {
                if (time1.getMicroSecond() / 1000 < time2.getMicroSecond() / 1000) {
                    ref = 1;
                }
            }

            // 计算时间差并转换为采样点偏移
            int startCalPos1, startCalPos2;

            if (ref == 1) {
                // time2更晚，以time2为参考基准
                diffMs = (seconds2 - seconds1) * 1000 +
                        (time2.getMicroSecond() / 1000) - (time1.getMicroSecond() / 1000);
                startCalPos2 = 0;
                
                /*
                 * 采样率转换：diffMs * smpRate / 20
                 * 计算原理：
                 * - smpRate是每周波采样点数
                 * - 50Hz电网每秒50个周波
                 * - 每ms的采样点数 = smpRate * 50 / 1000 = smpRate / 20
                 */
                startCalPos1 = (int) (diffMs * data1.getSmpRate() / 20);
            } else {
                // time1更晚或相等，以time1为参考基准
                diffMs = (seconds1 - seconds2) * 1000 +
                        (time1.getMicroSecond() / 1000) - (time2.getMicroSecond() / 1000);
                startCalPos1 = 0;
                startCalPos2 = (int) (diffMs * data2.getSmpRate() / 20);
            }

            // 记录基于时间戳的初始位置
            result.setTimeStartPos1(startCalPos1);
            result.setTimeStartPos2(startCalPos2);

            log.info("基于时间戳计算的偏移 - 波形1: {}, 波形2: {}, 时间差: {}ms",
                    startCalPos1, startCalPos2, diffMs);

            // 检查偏移是否超出数据范围
            if (startCalPos1 >= data1.getSmpNum() || startCalPos2 >= data2.getSmpNum()) {
                result.setSuccess(false);
                result.setErrorMessage("时间偏移超出数据范围");
                return result;
            }

            // 在A相电压通道中寻找从负到正的过零点
            // 对应C代码中的过零点检测逻辑

            /*
             * 寻找波形1的A相电压过零点
             * 在初始位置后的2个周波范围内搜索从负到正的过零点
             */
            // 2个周波的采样点数作为搜索范围
            int searchRange1 = (int) (data1.getSmpRate() * 2);
            int zeroCrossingPos1 = findZeroCrossing(data1.getSmpData()[0], startCalPos1,
                    Math.min(startCalPos1 + searchRange1, data1.getSmpNum() - 1));

            if (zeroCrossingPos1 == -1) {
                result.setSuccess(false);
                result.setErrorMessage("波形1未找到A相电压过零点");
                return result;
            }

            // 寻找波形2的A相电压过零点
            // 2个周波的采样点数作为搜索范围
            int searchRange2 = (int) (data2.getSmpRate() * 2);
            int zeroCrossingPos2 = findZeroCrossing(data2.getSmpData()[0], startCalPos2,
                    Math.min(startCalPos2 + searchRange2, data2.getSmpNum() - 1));

            if (zeroCrossingPos2 == -1) {
                result.setSuccess(false);
                result.setErrorMessage("波形2未找到A相电压过零点");
                return result;
            }

            log.info("找到过零点 - 波形1: {}, 波形2: {}", zeroCrossingPos1, zeroCrossingPos2);

            // 更新起始位置为过零点位置
            startCalPos1 = zeroCrossingPos1;
            startCalPos2 = zeroCrossingPos2;

            /*
             * 检查两个装置时钟同步情况
             * 如果过零点位置相对于时间戳位置的偏移差异过大，需要进行调整
             * 对应C代码中的同步检查逻辑
             */
            int diff1 = startCalPos1 - result.getTimeStartPos1();
            int diff2 = startCalPos2 - result.getTimeStartPos2();
            int diffOffset = Math.abs(diff1 - diff2);

            /*
             * 周波边界跨越检测和调整
             * 如果偏差超过半个周波，认为可能跨越了一个周波边界
             */
            if (diffOffset > data1.getSmpRate() / 2) {
                if (diff1 > diff2) {
                    // 波形2需要向前移动一个周波
                    startCalPos2 += (int) data2.getSmpRate();
                } else {
                    // 波形1需要向前移动一个周波
                    startCalPos1 += (int) data1.getSmpRate();
                }
                log.info("检测到周波边界跨越，调整后位置 - 波形1: {}, 波形2: {}",
                        startCalPos1, startCalPos2);
            }

            // 最终检查位置是否有效
            if (startCalPos1 >= data1.getSmpNum() || startCalPos2 >= data2.getSmpNum()) {
                result.setSuccess(false);
                result.setErrorMessage("调整后的起始位置超出数据范围");
                return result;
            }

            // 设置最终的起始位置
            result.setStartPos1(startCalPos1);
            result.setStartPos2(startCalPos2);

            // 计算起始时间
            result.setStartTime1(calculateStartTime(time1, startCalPos1, data1.getSmpRate()));
            result.setStartTime2(calculateStartTime(time2, startCalPos2, data2.getSmpRate()));

            /*
             * 计算可进行计算的200ms窗口数量
             * 每个窗口需要10个周波的数据用于电能质量分析
             * 重要：使用降采样后的采样率(getSmpRate)
             */
            int windowSamples1 = (int) (data1.getSmpRate() * 10);  // 使用降采样后的采样率
            int windowSamples2 = (int) (data2.getSmpRate() * 10);  // 每个文件可能有不同的采样率
            int availableWindows1 = (data1.getSmpNum() - startCalPos1) / windowSamples1;
            int availableWindows2 = (data2.getSmpNum() - startCalPos2) / windowSamples2;
            int maxWindows = Math.min(availableWindows1, availableWindows2);

            // 限制最大窗口数为100（对应C代码中的MAX_DATA_NUM常量）
            if (maxWindows > 50) {
                maxWindows = 50;
            }

            result.setCalNum(maxWindows);

            if (maxWindows <= 0) {
                result.setSuccess(false);
                result.setErrorMessage("数据长度不足，无法进行计算");
                return result;
            }

            result.setSuccess(true);

            log.info("波形对齐完成 - 最终位置: 波形1={}, 波形2={}, 可计算窗口数: {}",
                    startCalPos1, startCalPos2, maxWindows);

            return result;

        } catch (Exception e) {
            log.error("波形对齐过程出错", e);
            result.setSuccess(false);
            result.setErrorMessage("对齐过程出错: " + e.getMessage());
            return result;
        }
    }

    /**
     * 寻找从负到正的过零点
     * 对应C代码中的过零点检测逻辑
     *
     * @param voltageData 电压数据数组
     * @param startIndex  开始搜索的索引
     * @param endIndex    结束搜索的索引
     * @return 过零点位置，-1表示未找到
     */
    private static int findZeroCrossing(int[] voltageData, int startIndex, int endIndex) {
        for (int i = startIndex; i < endIndex - 1; i++) {
            /*
             * 寻找从负值到正值的过零点
             * 注意：使用严格不等号（< 和 >），避免0值影响判断
             */
            if (voltageData[i] <= 0 && voltageData[i + 1] >= 0) {
                // 返回过零点后的第一个正值位置
                return i + 1;
            }
        }
        // 未找到过零点
        return -1;
    }

    /**
     * 计算实际的开始时间
     *
     * @param baseTime      基准时间
     * @param offsetSamples 采样点偏移
     * @param sampleRate    每周波采样点数
     * @return 计算后的开始时间
     */
    private static ClockStruct calculateStartTime(ClockStruct baseTime, int offsetSamples, float sampleRate) {
        ClockStruct startTime = new ClockStruct();

        // 复制基准时间
        startTime.setYear(baseTime.getYear());
        startTime.setMonth(baseTime.getMonth());
        startTime.setDay(baseTime.getDay());
        startTime.setHour(baseTime.getHour());
        startTime.setMinute(baseTime.getMinute());
        startTime.setSecond(baseTime.getSecond());
        startTime.setMicroSecond(baseTime.getMicroSecond());

        /*
         * 计算偏移时间（毫秒）
         * 计算公式：偏移时间 = 偏移采样点数 / 每周波采样点数 * 20ms
         * 其中20ms是50Hz电网一个周波的时间周期
         */
        int offsetMs = (int) FloatMath.round((float) offsetSamples / sampleRate * 20.0f);

        // 将偏移时间加到基准时间的毫秒部分
        int totalMs = startTime.getMicroSecond() / 1000 + offsetMs;

        // 处理毫秒溢出
        if (totalMs >= 1000) {
            int extraSeconds = totalMs / 1000;
            totalMs = totalMs % 1000;

            startTime.setSecond(startTime.getSecond() + extraSeconds);

            // 处理秒溢出（进位到分钟）
            if (startTime.getSecond() >= 60) {
                startTime.setMinute(startTime.getMinute() + startTime.getSecond() / 60);
                startTime.setSecond(startTime.getSecond() % 60);

                // 处理分钟溢出（进位到小时）
                if (startTime.getMinute() >= 60) {
                    startTime.setHour(startTime.getHour() + startTime.getMinute() / 60);
                    startTime.setMinute(startTime.getMinute() % 60);

                    /*
                     * 处理小时溢出（简化处理，不考虑日期变化）
                     * 实际应用中可能需要更复杂的日期处理逻辑
                     */
                    if (startTime.getHour() >= 24) {
                        startTime.setHour(startTime.getHour() % 24);
                    }
                }
            }
        }

        startTime.setMicroSecond((int) (totalMs * 1000));

        return startTime;
    }

    /**
     * 应用对齐结果到数据缓冲区
     *
     * @param data1  第一个波形数据
     * @param data2  第二个波形数据
     * @param result 对齐结果
     */
    public static void applyAlignment(DataPq data1, DataPq data2, AlignmentResult result) {
        if (!result.isSuccess()) {
            log.error("无法应用对齐结果：{}", result.getErrorMessage());
            return;
        }

        // 设置起始计算位置
        data1.setStartCalPos(result.getStartPos1());
        data2.setStartCalPos(result.getStartPos2());

        // 设置起始计算时间
        data1.setStartCalTime(result.getStartTime1());
        data2.setStartCalTime(result.getStartTime2());

        // 设置可计算的窗口数量
        data1.setCalNum(result.getCalNum());
        data2.setCalNum(result.getCalNum());

        log.info("对齐结果已应用到数据缓冲区");
    }
}