package _VisualDVM.TestingSystem.SAPFOR;
import Common.CommonConstants;
import Common.Utils.Pair;
import Common.Utils.TextLog;
import Common.Utils.Utils_;
import _VisualDVM.ComponentsServer.Component.Sapfor.Sapfor;
import _VisualDVM.*;
import _VisualDVM.Passes.All.DownloadSapforRepository;
import _VisualDVM.ProjectData.LanguageName;
import _VisualDVM.Repository.EmailMessage;
import _VisualDVM.Repository.Server.ServerCode;
import _VisualDVM.TestingSystem.Common.TasksPackageState;
import _VisualDVM.TestingSystem.Common.TestingPlanner;
import _VisualDVM.TestingSystem.SAPFOR.Json.SapforConfiguration_json;
import _VisualDVM.TestingSystem.SAPFOR.Json.SapforTest_json;
import _VisualDVM.TestingSystem.SAPFOR.Json.SapforTestingSet_json;
import _VisualDVM.TestingSystem.SAPFOR.SapforPackage.SapforPackage;
import _VisualDVM.TestingSystem.SAPFOR.SapforTask.ComparisonState;
import _VisualDVM.TestingSystem.SAPFOR.SapforTask.SapforTask;
import _VisualDVM.TestingSystem.SAPFOR.ServerSapfor.ServerSapfor;
import _VisualDVM.TestingSystem.SAPFOR.ServerSapfor.ServerSapforState;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.FileFilter;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
public class SapforTestingPlanner extends TestingPlanner<SapforPackage> {
    File workspace;
    ServerSapfor sapfor;
    //--
    File repo;
    File repo_sapfor;
    File repo_sapfor_installation_directory;
    int max_version;
    int current_version;
    //--
    File repo_sapfor_assembly; //бинарник собранный в репозитории.
    File repo_out;
    File repo_err;
    //--
    public SapforTestingPlanner() {
        repo = new File(Utils_.getHomeDirectory(), "Repo");
        repo_sapfor = new File(repo, "SAPFOR");
        //-
        repo_sapfor_installation_directory = new File(repo_sapfor, "install");
        repo_out = new File(repo_sapfor_installation_directory, Constants.out_file);
        repo_err = new File(repo_sapfor_installation_directory, Constants.err_file);
        repo_sapfor_assembly = new File(repo_sapfor_installation_directory, "Sapfor_F");
    }
    @Override
    public String packageDescription() {
        return "SAPFOR";
    }
    @Override
    protected ServerCode getActivePackagesCode() {
        return ServerCode.GetFirstActiveSapforPackages;
    }
    @Override
    protected ServerCode getCheckIfNeedsKillCode() {
        return ServerCode.SapforPackageNeedsKill;
    }
    @Override
    protected TasksPackageState getStateAfterStart() {
        return TasksPackageState.RunningExecution;
    }
    @Override
    protected void InitSessionCredentials() {
        workspace = testingPackage.getLocalWorkspace();
    }
    @Override
    protected void TestsSynchronize() throws Exception {
        testingPackage.readJson();
        //--
        for (SapforTestingSet_json set_json : testingPackage.package_json.testingSets) {
            File setWorkspace = new File(workspace, String.valueOf(set_json.id));
            FileUtils.forceMkdir(setWorkspace);
            //копирование тестов по конфигурациям.
            for (SapforConfiguration_json configuration_json : set_json.configurations) {
                //--
                File configurationWorkspace = new File(setWorkspace, String.valueOf(configuration_json.id));
                FileUtils.forceMkdir(configurationWorkspace);
                //--->>>
                for (SapforTest_json test_json : set_json.tests) {
                    File test_root = new File(configurationWorkspace, test_json.description);
                    Utils_.CheckAndCleanDirectory(test_root);
                    FileUtils.copyDirectory(new File(Global.TestsDirectory, String.valueOf(test_json.id)), test_root);
                }
            }
        }
    }
    @Override
    protected void PackageWorkspaceCreation() throws Exception {
        //копирование визуализатора
        File visualiser = new File(workspace, "VisualSapfor.jar");
        Files.createSymbolicLink(visualiser.toPath(), new File(Utils_.getHomeDirectory(), Constants.ApplicationFileName).toPath());
        //создание настроек
        VisualDVMProperties properties = new VisualDVMProperties();
        properties.Mode = Mode.Package;
        Utils_.jsonToFile(properties, new File(workspace, "properties"));
        //подготовка пакетного режима. Запустит его уже очередь.
        Utils.createScript(workspace, workspace, "start", "java -jar VisualSapfor.jar");
    }
    @Override
    protected void PackageStart() throws Exception {
        File script = new File(workspace, "start");
        ProcessBuilder procBuilder = new ProcessBuilder(script.getAbsolutePath());
        procBuilder.directory(workspace);
        procBuilder.start();
        //--->>
        File started = new File(workspace, CommonConstants.STARTED);
        while (!started.exists()) {
            Print("waiting for package start...");
            Utils_.sleep(1000);
        }
        File pid = new File(workspace, "PID");
        testingPackage.PID = FileUtils.readFileToString(pid, Charset.defaultCharset());
    }
    @Override
    protected boolean CheckNextState() throws Exception {
        boolean progress_changed = false;
        boolean state_changed = false;
        //--
        File workspace = testingPackage.getLocalWorkspace();
        //--
        File progress = new File(workspace, "progress");
        if (progress.exists()) {
            String s = FileUtils.readFileToString(progress);
            int current_progress = Integer.parseInt(s);
            if (current_progress != testingPackage.progress) {
                Print("progress changed: " + current_progress);
                testingPackage.progress = current_progress;
                progress_changed = true;
            }
        }
        //--
        File done = new File(workspace, CommonConstants.DONE);
        File aborted = new File(workspace, CommonConstants.ABORTED);
        if (done.exists()) {
            testingPackage.state = TasksPackageState.Analysis;
            state_changed = true;
        } else if (aborted.exists()) {
            testingPackage.state = TasksPackageState.Aborted;
            state_changed = true;
        }
        return progress_changed || state_changed;
    }
    @Override
    protected void DownloadResults() throws Exception {
        //-
        UpdatePackageState(TasksPackageState.Analysis);
    }
    @Override
    protected void AnalyseResults() throws Exception {
        //не требуется.
        testingPackage.progress = 100;
        testingPackage.checkFinishState();
        //--
        File workspace = testingPackage.getLocalWorkspace();
        File[] links = workspace.listFiles();
        if (links!=null){
            Print("files found: "+links.length);
            for (File file: links) {
                if (file.isFile() && file.getName().startsWith("SAPFOR"))
                FileUtils.forceDelete(file);
            }
        }
        //--для эталона
        if (testingPackage.ethalon_id != CommonConstants.Nan) {
            TextLog log = new TextLog();
            try {
                CheckEthalon(log);
            } catch (Exception ex) {
                ex.printStackTrace();
                log.Writeln_("При сравнении с эталоном возникло исключение:");
                log.Writeln_(ex.getMessage());
            } finally {
                if (!log.isEmpty())
                    testingPackage.description += "\n" + log.text;
            }
        }
        UpdatePackageState();
    }
    protected void CheckEthalon(TextLog log) throws Exception {
        Print("эталон = " + testingPackage.ethalon_id);
        SapforPackage ethalon = (SapforPackage) ServerCommand(ServerCode.GetObjectCopyByPK, "", new Pair<>(SapforPackage.class, testingPackage.ethalon_id));
        if (ethalon != null) {
            Print("найден эталон " + testingPackage.ethalon_id);
            ethalon.readJson();
            Print("задач в эталоне: " + ethalon.package_json.tasks.size());
            Print("задач в пакете: " + testingPackage.package_json.tasks.size());
            if (ethalon.canCompare(testingPackage, log)) {
                Print("сравнение...");
                //--
                testingPackage.package_json.getVersionsFiles(testingPackage);
                ethalon.package_json.getVersionsFiles(ethalon);
                //--
                for (SapforTask task1 : ethalon.package_json.tasks) {
                    SapforTask task2 = testingPackage.package_json.getTaskByKey(task1.getUniqueKey());
                    task1.checkMatch(task2); //тексты сравниваются просто посимвольно.
                    if (task1.comparisonState.equals(ComparisonState.NotMatch)) {
                        testingPackage.mismatchesCount++;
                    }
                }
                Print("сравнение завершено");
                log.Writeln_("Различий с эталоном: " + testingPackage.mismatchesCount);
                if (testingPackage.mismatchesCount > 0) {
                    //  testingPackage.doneTasksCount -= testingPackage.mismatchesCount;
                    testingPackage.state = TasksPackageState.DoneWithErrors;
                }
                return;
            } else {
                Print("сравнение с эталоном невозможно");
                log.Writeln_("Cравнение с эталоном невозможно");
            }
        }
    }
    @Override
    protected void Kill() throws Exception {
        File workspace = testingPackage.getLocalWorkspace();
        //----
        File interrupt_file = new File(workspace, CommonConstants.INTERRUPT);
        //----
        FileUtils.writeStringToFile(interrupt_file, new Date().toString());
        File aborted_file = new File(workspace, CommonConstants.ABORTED);
        do {
            Print("waiting for interrupt...");
            Thread.sleep(1000);
        } while (!aborted_file.exists());
        Print("coup de grace..");
      //  String kill_command = "killall -SIGKILL " + testingPackage.PID;
        String kill_command = "pkill -SIGKILL -f " + testingPackage.PID;
        Print(kill_command);
        Process killer = Runtime.getRuntime().exec(kill_command);
        killer.waitFor();
        Print("done!");
        UpdatePackageState(TasksPackageState.Aborted);
    }
    //--
    @Override
    public void perform() throws Exception {
        checkServerSapforsForCompilation();
        super.perform();
    }
    //--------------------
    public void getServerSapforForCompilation() throws Exception {
        sapfor = (ServerSapfor) ServerCommand(ServerCode.GetSapforForCompilation);
    }
    void UpdateSapforState(ServerSapforState state_in) throws Exception {
        sapfor.state = state_in;
        ServerCommand(ServerCode.EditObject, sapfor);
    }
    void CompileSapfor() throws Exception {
        UpdateSapforState(ServerSapforState.Compilation);
        //-
        Utils_.CheckAndCleanDirectory(repo_sapfor_installation_directory);
        //--
        Utils.startScript(repo_sapfor_installation_directory, repo_sapfor_installation_directory, "build_sapfor",
                "cmake .. 1>" + Constants.out_file + " 2>" + Constants.err_file +
                        "\nmake -j 14 1>>" +
                        Constants.out_file +
                        " 2>>" +
                        Constants.err_file +
                        "\n").waitFor();
    }
    //--------------------
    public void checkServerSapforsForCompilation() throws Exception {
        sapfor = null;
        getServerSapforForCompilation();
        if (sapfor != null) {
            //---
            max_version = CommonConstants.Nan;
            current_version = CommonConstants.Nan;
            //--
            UpdateSapforState(ServerSapforState.SAPFORRepositorySynchronization);
            DownloadSapforRepository pass = new DownloadSapforRepository();
            pass.Do();
            //-
            max_version = (int) ServerCommand(ServerCode.GetMaxSapforVersion);
            current_version = Sapfor.readVersionFromCode(Paths.get(repo.getAbsolutePath(),
                    "/SAPFOR/src/Utils/version.h").toFile());
            if (current_version == max_version) {
                ServerCommand(ServerCode.DeleteObjectByPK, new Pair(ServerSapfor.class, sapfor.id));
                return;
            }
            //-
            File sapforHome = new File(Global.SapforsDirectory, Utils_.getDateName("sapfor"));
            //--
            sapfor.home_path = sapforHome.getAbsolutePath();
            sapfor.languageName = LanguageName.fortran;
            //--
            File sapforBin = new File(sapforHome, "Sapfor_F");
            File sapforOut = new File(sapforHome, Constants.out_file);
            File sapforErr = new File(sapforHome, Constants.err_file);
            //-
            CompileSapfor();
            sapforHome.mkdir();
            //--
            if (repo_out.exists())
                FileUtils.copyFile(repo_out, sapforOut);
            if (repo_err.exists())
                FileUtils.copyFile(repo_err, sapforErr);
            if (repo_sapfor_assembly.exists()) {
                FileUtils.copyFile(repo_sapfor_assembly, sapforBin);
                sapforBin.setExecutable(true, false);
                //--
                sapfor.call_command = sapforBin.getAbsolutePath();
                sapfor.buildDate = new Date().getTime();
                //--
                UpdateSapforState(ServerSapforState.Done);
                EmailSapforAssembly(current_version, true, sapforOut, sapforErr);
                //запуск автоматического тестирования.
                ServerCommand(ServerCode.PerformAutoSapforTesting, String.valueOf(sapfor.id), null);
            } else {
                UpdateSapforState(ServerSapforState.DoneWithErrors);
                EmailSapforAssembly(current_version, false, sapforOut, sapforErr);
            }
        }
    }
    void EmailSapforAssembly(int version, boolean done, File out, File err) throws Exception {
        String version_s = (version == CommonConstants.Nan) ? "?" : String.valueOf(version);
        String status = done ? "Успешно" : "С ошибками";
        //-
        EmailMessage message = new EmailMessage(
                "Выполнена сборка системы SAPFOR",
                "Версия: " + version_s + "\n" + "Статус: " + status
        );
        message.addAttachement(out);
        message.addAttachement(err);
        //-
        for (String address : Constants.admins_mails) {
            ServerCommand(ServerCode.Email, address, message);
        }
    }
}
