package com.pdaaphm.biz.service.impl;

import com.pdaaphm.biz.domain.AlgorithmIoField;
import com.pdaaphm.biz.domain.BaseResponse;
import com.pdaaphm.biz.domain.File;
import com.pdaaphm.biz.domain.SubAlgorithm;
import com.pdaaphm.biz.dto.RequestDTO;
import com.pdaaphm.biz.mapper.AlgorithmIoFieldMapper;
import com.pdaaphm.biz.mapper.FileMapper;
import com.pdaaphm.biz.mapper.SubAlgorithmMapper;
import com.pdaaphm.biz.service.RunAlgorithmService;
import com.pdaaphm.common.config.PadaphmConfig;
import com.pdaaphm.common.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.io.IOException;

@Service
public class RunMatlabImpl implements RunAlgorithmService {

    @Autowired
    private FileMapper fileMapper;

    @Autowired
    private SubAlgorithmMapper subAlgorithmMapper;

    @Autowired
    private AlgorithmIoFieldMapper algorithmIoFieldMapper;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public BaseResponse runAlgorithm(Long id, String url, RequestDTO requestDto) {

        //初始化res
        BaseResponse res = new BaseResponse();
        res.setCode(200);

        //把文件从inputPath文件系统copy到matlab要求的输入位置matlabInputPath，记录matlab的输出文件位置到matlabOutputPathList

        List<String> doctList = requestDto.getDocList();
        //docList的映射指针
        int p = 0;
        List<Map> matlabIoMapList = algorithmIoFieldMapper.selectMatlabIoPathTypeIndexByAlgoId(id);
        List<String> matlabOutputPathList = new LinkedList<>();
        for (Map matlabIoMap : matlabIoMapList) {
            if (AlgorithmIoField.inputCode.equals(matlabIoMap.get("type"))) {
                String matlabInputPathWithNoFileType = matlabIoMap.get("matlab_io_path") + "\\input" + matlabIoMap.get("index");
                String inputPath = doctList.get(p);
                p++;
                int index = inputPath.lastIndexOf('.');
                String extension = inputPath.substring(index + 1);
                String matlabInputPath = matlabInputPathWithNoFileType + '.' + extension;
                try {
                    copyFile(inputPath, matlabInputPath);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } else {
                String matlabOutputPathWithNoFileType = matlabIoMap.get("matlab_io_path") + "\\output" + matlabIoMap.get("index");
                matlabOutputPathList.add(matlabOutputPathWithNoFileType);
            }
        }

        //run matlab exe
        runMatExe(url);

        //将matlab输出的文件从matlabOutputPathList里copy到文件系统outputPath

        List<String> resultList = requestDto.getResultList();
        //resultList的映射指针
        int q = 0;
        for (String matlabOutputPath : matlabOutputPathList) {
            int index = matlabOutputPath.lastIndexOf("\\");
            String dir = matlabOutputPath.substring(0, index);
            String prefixName = matlabOutputPath.substring(index + 1);
            String matFinalOutPath = getABSFinalPath(dir, prefixName);
            int index2 = matFinalOutPath.lastIndexOf('.');
            String extension = matFinalOutPath.substring(index2 + 1);
            String outputPath = resultList.get(q) + '.' + extension;
            try {
                copyFile(matFinalOutPath, outputPath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            q++;
        }


        // save result path to subAlgorithm
        List<Map> subAlgorithmOutputList = subAlgorithmMapper.selectSubAlgorithmOutputList(id);
        if (resultList == null) {
            return res.error("算法未配置输出");
        }
        for (int i = 0; i < resultList.size(); i++) {
            String resultPath = resultList.get(i);
            Map subAlgorithmOutput = subAlgorithmOutputList.get(i);
            int lastIndex = resultPath.lastIndexOf("/");
            String dirs = resultPath.substring(0, lastIndex);
            String prefixName = resultPath.substring(lastIndex + 1);
            Map<String, String> map = getFinalNameAndPath(dirs, prefixName);
            if (map != null) {
                String prefix = PadaphmConfig.getResultPath();
                String s = map.get("path");
                if (s.startsWith(prefix)) {
                    s = s.replace(prefix, "/resultPath");
                    map.put("path", s);
                }
                File resultFile = new File();
                resultFile.setName(map.get("name"));
                resultFile.setPath(map.get("path"));
                resultFile.setType(File.ouputFlag);
                resultFile.setCreateTime(DateUtils.getNowDate());
                fileMapper.insertFile(resultFile);
                SubAlgorithm subAlgorithm = new SubAlgorithm();
                subAlgorithm.setId((Long) subAlgorithmOutput.get("id"));
                subAlgorithm.setAlgorithmId((Long) subAlgorithmOutput.get("algorithm_id"));
                subAlgorithm.setFieldId((Long) subAlgorithmOutput.get("field_id"));
                subAlgorithm.setUploadId(resultFile.getId());
                subAlgorithm.setCreateTime(DateUtils.getNowDate());
                subAlgorithmMapper.updateSubAlgorithm(subAlgorithm);
            } else {
                logger.error("输出文件未生成！");
                // todo 对程序结果产生负面影响的分子，应反馈给前端
                // update algorithm
            }
        }
        return null;
    }

    public String getABSFinalPath(String dirs, String prefixName) {
        // 指定目录路径
        java.io.File directory = new java.io.File(dirs);

        // 使用FilenameFilter筛选与已知文件名开头的文件
        String[] matchingFiles = directory.list((dir, name) -> name.startsWith(prefixName));

        String res;

        if (matchingFiles != null && matchingFiles.length == 1) {
            System.out.println("找到匹配的文件：");
            res = dirs + '\\' + matchingFiles[0];
            return res;
        } else if (matchingFiles != null && matchingFiles.length > 1) {
            logger.error("找到多个匹配的文件，这不应该发生。");
            return null;
        } else {
            logger.error("没有找到匹配的文件。");
            return null;
        }
    }

    // todo return type is Map<String,String>
    public Map getFinalNameAndPath(String dirs, String prefixName) {
        // 指定目录路径
        java.io.File directory = new java.io.File(dirs);

        // 使用FilenameFilter筛选与已知文件名开头的文件
        String[] matchingFiles = directory.list((dir, name) -> name.startsWith(prefixName));

        Map<String, String> map = new HashMap<>();

        if (matchingFiles != null && matchingFiles.length == 1) {
            System.out.println("找到匹配的文件：");
            map.put("name", matchingFiles[0]);
            map.put("path", dirs + '/' + matchingFiles[0]);
            return map;
        } else if (matchingFiles != null && matchingFiles.length > 1) {
            logger.error("找到多个匹配的文件，这不应该发生。");
            return null;
        } else {
            logger.error("没有找到匹配的文件。");
            return null;
        }
    }

    /**
     * copy file
     */
    public void copyFile(String sourcePathStr, String destinationPathStr) throws IOException {
        Path sourcePath = Paths.get(sourcePathStr);
        Path destinationPath = Paths.get(destinationPathStr);
        Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING);
    }

    /**
     * this is a demo
     */
    public void runMatExe(String url) {
        try {
            // 指定要执行的外部程序和参数
            ProcessBuilder processBuilder = new ProcessBuilder(url);
            // 启动外部程序
            Process process = processBuilder.start();

            // Thread to handle the output stream.
            Thread outputThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        logger.info(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

            // Thread to handle the error stream.
            Thread errorThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        logger.error(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

            outputThread.start();
            errorThread.start();

            // Wait for the external process to finish.
            int exitCode = process.waitFor();

            // Optionally, wait for the threads to finish reading.
            outputThread.join();
            errorThread.join();

            System.out.println("外部程序执行完成，退出码：" + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return;
    }

    public static void main(String args[]) {
        String url = "D:\\pdaaphm\\matlabexefu\\NARX_main.exe";
        try {
            // 指定要执行的外部程序和参数
            ProcessBuilder processBuilder = new ProcessBuilder(url);
            // 启动外部程序
            Process process = processBuilder.start();

            // Thread to handle the output stream.
            Thread outputThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        System.out.println(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

            // Thread to handle the error stream.
            Thread errorThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        System.err.println(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

            outputThread.start();
            errorThread.start();

            // Wait for the external process to finish.
            int exitCode = process.waitFor();

            // Optionally, wait for the threads to finish reading.
            outputThread.join();
            errorThread.join();

            System.out.println("外部程序执行完成，退出码：" + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return;
    }
}
