package SapforTestingSystem;
import Common.Constants;
import Common.Global;
import Common.Utils.Utils;
import GlobalData.Tasks.TaskState;
import ProjectData.LanguageName;
import ProjectData.Messages.Errors.MessageError;
import ProjectData.Project.db_project_info;
import SapforTestingSystem.Json.SapforConfiguration_json;
import SapforTestingSystem.Json.SapforVersion_json;
import SapforTestingSystem.SapforTask.SapforTask;
import Visual_DVM_2021.Passes.PassCode_2021;
import Visual_DVM_2021.Passes.Pass_2021;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Vector;

import static java.lang.Character.isDigit;
public class PerformSapforTask extends Pass_2021<SapforTask> {
    @Override
    public String getDescription() {
        return "Запуск задачи SAPFOR";
    }
    @Override
    protected boolean needsAnimation() {
        return false;
    }
    //--
    File sapfor_drv;
    SapforConfiguration_json sapforConfiguration_json;
    //-----
    File parentTask;
    File task;
    //-----
    Process process = null;
    int exit_code = Constants.Nan;
    //----
    File outputFile = null;
    File errorsFile = null;
    //--
    Vector<String> outputLines;
    Vector<String> errorsLines;
    //---
    @Override
    protected boolean canStart(Object... args) throws Exception {
        sapfor_drv = (File) args[0];
        sapforConfiguration_json = (SapforConfiguration_json) args[1];
        target = (SapforTask) args[2];
        //--->>
        parentTask = Paths.get(Global.Home, sapforConfiguration_json.id, target.test_description).toFile();
        task = null;
        //--->>
        return true;
    }
    protected static boolean checkLines(Vector<String> lines) {
        for (String line : lines) {
            if (line.toLowerCase().contains("internal error")) {
                return false;
            }
            if (line.toLowerCase().contains("exception")) {
                return false;
            }
            if (line.contains("[ERROR]")) {
                return false;
            }
            if (line.toLowerCase().contains("segmentation fault")) {
                return false;
            }
        }
        return true;
    }
    protected boolean performSapforScript(String name, File workspace, String command, String outName, String errName) throws Exception {
        process = null;
        exit_code = Constants.Nan;
        //---
        File data_workspace = new File(workspace, Constants.data);
        Utils.CheckDirectory(data_workspace);
        outputFile = new File(data_workspace, outName);
        errorsFile = new File(data_workspace, errName);
        Utils.delete_with_check(outputFile);
        Utils.delete_with_check(errorsFile);
        //---
        File file = new File(data_workspace, name + (Global.isWindows ? ".bat" : ".sh"));
        FileUtils.write(file,
                Utils.DQuotes(sapfor_drv.getAbsolutePath())
                        + (target.flags.isEmpty() ? "" : (" " + target.flags))
                        + " -noLogo"
                        + " " + command +
                        " 1>" +
                        Utils.DQuotes(outputFile.getAbsolutePath()) +
                        " 2>" +
                        Utils.DQuotes(errorsFile.getAbsolutePath()),
                Charset.defaultCharset());
        if (!file.setExecutable(true))
            throw new Exception("Не удалось сделать файл скрипта " + name + " исполняемым!");
        //--
        boolean flag = false;
        do {
            try {
                ProcessBuilder procBuilder = new ProcessBuilder(file.getAbsolutePath());
                procBuilder.directory(workspace);
                process = procBuilder.start();
                exit_code = process.waitFor();
                flag = true;
            } catch (Exception ex) {
                Global.Log.PrintException(ex);
                Utils.sleep(1000);
            }
        }
        while (!flag);
        process = null;
        //---
        outputLines = new Vector<>(FileUtils.readLines(outputFile));
        errorsLines = new Vector<>(FileUtils.readLines(errorsFile));
        return (exit_code == 0) &&
                checkLines(outputLines) &&
                checkLines(errorsLines);
    }
    protected boolean parse() throws Exception {
        if (performSapforScript("parse", parentTask,
                "-parse *.f *.for *.fdv *.f90 *.f77",
                Constants.parse_out_file,
                Constants.parse_err_file)
                && (new File(parentTask, "dvm.proj")).exists()) {
            return true;
        } else {
            target.state = TaskState.DoneWithErrors;
            return false;
        }
    }
    //слегка изменить подход.
    protected boolean transformation(PassCode_2021 code) throws Exception {
        task = new File(parentTask, "v1");
        Utils.CheckAndCleanDirectory(task); //папка для преобразования.
        //если версия пустая, это тоже результат тестирования. Поэтому должна учитываться в древе.
        target.versions.add(new SapforVersion_json(task.getAbsolutePath(), code.getDescription()));
        //---
        if (performSapforScript("transformation", parentTask,
                code.getTestingCommand() + " -F " + Utils.DQuotes(task.getAbsolutePath()),
                Constants.out_file,
                Constants.err_file
        )) {
            target.state = TaskState.Done;
            parentTask = task;
            return true;
        }
        target.state = TaskState.DoneWithErrors;
        return false;
    }
    protected void variants() throws Exception {
        //папки вариантов создается самим сапфором.
        target.state = performSapforScript("create_variants", parentTask, " -t 13 -allVars"
                        + " -tinfo"
                ,
                Constants.out_file,
                Constants.err_file
        ) ? TaskState.Done : TaskState.DoneWithErrors;
        //найти папки с вариантами.
        File[] files_ = parentTask.listFiles((dir, name) -> dir.isDirectory() && Utils.isParallelVersionName(name));
        if ((files_ != null) && (files_.length > 0)) {
            Vector<File> files = new Vector<>(Arrays.asList(files_));
            files.sort(Comparator.comparingInt(o -> Integer.parseInt(o.getName().substring(1))));
            for (File file : files)
                target.variants.add(new SapforVersion_json(file.getAbsolutePath(), PassCode_2021.SPF_CreateParallelVariant.getDescription()));
        }
    }
    //-------------------------------------------------->>
    public MessageError unpackMessage(String line_in) throws Exception {
        MessageError res = new MessageError();
        res.file = "";
        res.line = Constants.Nan;
        res.value = "";
        String line = line_in.substring(9);
        //System.out.println(line);
        int i = 0;
        int s = 0;
        String lexeme = "";
        //#1020: red43.fdv: line 988]: Active DVM directives are not supported (turn on DVM-directive support option)
        for (char c : line.toCharArray()) {
            //  System.out.print("<s=" + s + ">");
            //  System.out.println(c);
            switch (s) {
                case 0:
                    //поиск groups_s
                    if (c == '#') {
                        s = 1;
                        lexeme = "";
                    } else return null;
                    break;
                case 1:
                    //group_s
                    if (isDigit(c)) {
                        res.group_s += c;
                        lexeme += c;
                    } else if (c == ':') {
                        s = 2;
                        res.group = Integer.parseInt(lexeme);
                    } else return null;
                    break;
                case 2:
                    //поиск filename
                    if (c == ' ') {
                        s = 3;
                    } else return null;
                    break;
                case 3:
                    //filename
                    if (c == ':') {
                        s = 4;
                    } else {
                        res.file += c;
                    }
                    break;
                case 4:
                    //поиск line
                    if (c == ' ') {
                        s = 5;
                        lexeme = "";
                    } else return null;
                    break;
                case 5:
                    //line
                    if (c == ' ') {
                        if (!lexeme.equals("line"))
                            return null;
                        else {
                            s = 6;
                            lexeme = "";
                        }
                    } else {
                        lexeme += c;
                    }
                    break;
                case 6:
                    //line number
                    if (isDigit(c)) {
                        lexeme += c;
                    } else if (c == ']') {
                        res.line = Integer.parseInt(lexeme);
                        s = 7;
                    } else return null;
                    break;
                case 7:
                    //Поиск value
                    if (c == ':') {
                        s = 8;
                    } else return null;
                    break;
                case 8:
                    if (c == ' ') {
                        s = 9;
                    } else return null;
                    break;
                case 9:
                    //value
                    res.value += c;
                    break;
            }
            ;
            ++i;
        }
        //--
        if (s != 9)
            return null;
        //--
        return res;
    }
    public void readMessagesFromFileDump(File file, Vector<MessageError> messages) throws Exception {
        //Образец запакованного сообщения
        //ERROR - [#1020: red43.fdv: line 988]: Active DVM directives are not supported (turn on DVM-directive support option)
        Vector<String> lines = new Vector<>(FileUtils.readLines(file));
        if (!lines.isEmpty()) {
            for (int i = lines.size() - 1; i >= 0; --i) {
                String line = lines.get(i);
                if (line.startsWith("ERROR - ")) {
                    MessageError message = unpackMessage(line);
                    if (message != null)
                        messages.add(message);
                    //--
                } else break;
            }
        }
    }
    //--
    protected void createVersionProjectData(SapforVersion_json version, boolean isTransformation) throws Exception {
        db_project_info project = new db_project_info();
        project.Home = new File(version.version);
        project.name = project.Home.getName();
        project.description = version.description;
        project.languageName = LanguageName.fortran;
        project.creationDate = Utils.getDateNumber();
        //--
        Vector<MessageError> messages = new Vector<>();
        //--
        if (isTransformation) {
            File p_out = Paths.get(project.Home.getAbsolutePath(), Constants.data, Constants.parse_out_file).toFile();
            File p_err = Paths.get(project.Home.getAbsolutePath(), Constants.data, Constants.parse_err_file).toFile();
            File out = Paths.get(project.Home.getAbsolutePath(), Constants.data, Constants.out_file).toFile();
            File err = Paths.get(project.Home.getAbsolutePath(), Constants.data, Constants.err_file).toFile();
            //--
            if (p_out.exists()) {
                project.Log += (FileUtils.readFileToString(p_out));
                readMessagesFromFileDump(p_out, messages);
            }
            if (out.exists()) {
                project.Log += "\n" + FileUtils.readFileToString(out);
                readMessagesFromFileDump(out, messages);
            }
            //в потоки ошибок идет информация от операционной системы. сообщений там быть не должно.
            if (p_err.exists())
                project.Log += (FileUtils.readFileToString(p_err));
            if (err.exists())
                project.Log += "\n" + FileUtils.readFileToString(err);
            //--
        }
        project.CreateVisualiserData();
        //---
        /*
        if (isTransformation && !messages.isEmpty()) {
            project.Open(); //нельзя!!! сначала надо определиться с версиями. И только потом, получать файлы.
            //а так же, убрать dep и txt
            project.db.BeginTransaction();
            for (MessageError m : messages) {
                if (project.db.files.containsKey(m.file)) {
                    DBProjectFile file = project.db.files.Data.get(m.file);
                    file.CreateAndAddNewMessage(1, m.value, m.line, m.group);
                    //update file
                    project.db.Update(file);
                }
            }
            project.db.Commit();
            project.db.Disconnect();
        }
         */
    }
    //-------------------------------------------------->>
    @Override
    protected void body() throws Exception {
        target.versions.add(new SapforVersion_json(
                Paths.get(Global.Home,
                        sapforConfiguration_json.id,
                        target.test_description).toFile().getAbsolutePath(), "исходная"));
        for (PassCode_2021 code : sapforConfiguration_json.codes) {
            if (parse()) {
                if (code.equals(PassCode_2021.CreateParallelVariants))
                    variants();
                else if (!transformation(code))
                    break;
            } else
                break;
        }
    }
    @Override
    protected void performFinish() throws Exception {
        /*
        //теперь строим деревья версий. нельзя делать в body. так как могут быть исключения например неверный код процесса.
        for (SapforVersion_json version : target.versions)
            createVersionProjectData(version, true);
        for (SapforVersion_json version : target.variants)
            createVersionProjectData(version, false);
         */
    }
}
