package com.njcn.influx.event.cause.jna;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;

import java.io.*;
import java.util.Arrays;
import java.util.List;

/**
 * JNA接口调用qvvr_dll.dll
 */
public interface QvvrCauseDLL extends Library {

    /**
     * 加载DLL - 从resource目录或classpath
     */
    QvvrCauseDLL INSTANCE = loadLibrary();

    /**
     * 支持jar打包的库加载方法 - 从jar内resources提取到临时目录后加载
     */
    static QvvrCauseDLL loadLibrary() {
        String osName = System.getProperty("os.name").toLowerCase();
        String libFileName;
        String resourcePath;

        // 根据操作系统确定库文件名
        if (osName.contains("windows")) {
            libFileName = "qvvr_cause_dll.dll";
            resourcePath = "/qvvr_cause_dll.dll";
        } else if (osName.contains("linux")) {
            libFileName = "libqvvr_cause_dll.so";
            resourcePath = "/libqvvr_cause_dll.so";
        } else {
            throw new UnsupportedOperationException("不支持的操作系统: " + osName);
        }

        try {
            // 从jar中提取库文件到临时目录
            File tempLibFile = extractLibraryFromJar(resourcePath, libFileName);

            // 加载提取的库文件
            System.out.println("加载库文件: " + tempLibFile.getAbsolutePath());
            return Native.load(tempLibFile.getAbsolutePath(), QvvrCauseDLL.class);

        } catch (Exception e) {
            throw new RuntimeException(
                    "无法加载QVVR库文件。\n" +
                            "请确保文件 " + libFileName + " 存在于 src/main/resources/ 目录下。\n" +
                            "当前操作系统: " + osName,
                    e
            );
        }
    }

    /**
     * 从jar中提取库文件到临时目录
     */
    static File extractLibraryFromJar(String resourcePath, String libFileName) throws IOException {
        // 获取资源输入流
        InputStream libStream = QvvrCauseDLL.class.getResourceAsStream(resourcePath);
        if (libStream == null) {
            throw new FileNotFoundException("在jar中找不到库文件: " + resourcePath);
        }

        // 创建临时文件
        String tempDir = System.getProperty("java.io.tmpdir");
        File tempLibFile = new File(tempDir, libFileName);

        //File tempLibFile = new File(tempDir, "qvvr_" + System.currentTimeMillis() + "_" + libFileName);

        // 提取库文件到临时目录
        try (FileOutputStream out = new FileOutputStream(tempLibFile)) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = libStream.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        } finally {
            libStream.close();
        }

        // 设置为可执行
        tempLibFile.setExecutable(true);
        tempLibFile.setReadable(true);

        // JVM退出时删除临时文件
        tempLibFile.deleteOnExit();

        System.out.println("已提取库文件到: " + tempLibFile.getAbsolutePath());
        return tempLibFile;
    }

    /**
     * 直接调用C DLL的qvvr_fun_cause函数
     * void __stdcall qvvr_fun_cause(void *data)
     */
    void qvvr_fun_cause(QvvrDataStruct data);

    /**
     * 对应C语言的qvvr_data_struct结构体
     * 完全按照qvvr_dll.h中的定义映射
     */
    public static class QvvrDataStruct extends Structure {

        // 输入参数定义
        public float[] smp_va = new float[128 * 50 * 120];   // A相电压采样数据
        public float[] smp_vb = new float[128 * 50 * 120];   // B相电压采样数据
        public float[] smp_vc = new float[128 * 50 * 120];   // C相电压采样数据
        public int smp_rate;                                 // 采样率参数
        public int smp_len;                                  // 每个通道的采样数据个数

        // 输入阈值参数
        public float[] threshold = new float[50];            // 预设阈值时间参数

        // 输出结果参数定义
        public int cause;                                    // 电压暂降判断出暂降原因 0-未知,1-短路,2-电压调节器,3-感动电机
        public int no_cal;                                   // 未计算判断标志，该位1表示输入数据有问题（数据缺失或异常等）未进行电压暂降判断暂降原因

        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList("smp_va", "smp_vb", "smp_vc", "smp_rate", "smp_len", "threshold", "cause", "no_cal");
        }

        public QvvrDataStruct() {
            super();
            // 初始化输出参数
            this.cause = 0;
            this.no_cal = 0;
        }
    }
}