package _VisualDVM.TestingSystem.DVM;
import Common.Utils.Utils_;
import _VisualDVM.Constants;
import _VisualDVM.Global;
import _VisualDVM.GlobalData.RemoteFile.RemoteFile;
import _VisualDVM.Passes.All.UnzipFolderPass;
import _VisualDVM.TestingSystem.Common.TasksPackageState;
import _VisualDVM.TestingSystem.DVM.DVMTasks.DVMCompilationTask;
import _VisualDVM.TestingSystem.DVM.DVMTasks.DVMRunTask;
import _VisualDVM.TestingSystem.DVM.json.DVMCompilationPackage_json;
import _VisualDVM.TestingSystem.DVM.json.CompilationTask_json;
import _VisualDVM.TestingSystem.DVM.json.DVMRunPackage_json;
import _VisualDVM.TestingSystem.DVM.json.RunTask_json;

import java.io.File;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Vector;
public class RemoteDVMTestingPlanner extends DVMTestingPlanner {
    RemoteFile packageRemoteWorkspace = null;
    public RemoteDVMTestingPlanner(String... args) {
        super(args);
    }
    @Override
    protected boolean Connect() {
        if (user.connection == null) {
            try {
                user.connection = new UserConnection(machine, user);
                Print("Соединение c " + machine.getURL() + " " + user.login + " успешно установлено.");
                Print("Проверка инициализации..");
                user.connection.CheckUserInitialization(testingPackage.sender_address);
            } catch (Exception ex) {
                Print(ex.toString());
                user.connection = null;
                Print("Не удалось установить соединение.");
            }
        }
        return user.connection != null;
    }
    @Override
    protected void Disconnect() {
        if (user.connection != null) {
            user.connection.Disconnect();
            user.connection = null;
            Print("Соединение c " + machine.getURL() + " " + user.login + " сброшено.");
        }
    }
    //----
    @Override
    protected void TestsSynchronize() throws Exception {
        testingPackage.readJson();
        LinkedHashMap<Integer, File> tests = getTestsFromJson();
        //синхронизировать их.
        for (int test_id : tests.keySet()) {
            File test = tests.get(test_id);
            RemoteFile test_dst = new RemoteFile(testingPackage.user_workspace + "/projects/" + test_id, true);
            user.connection.MKDIR(test_dst);
            user.connection.SynchronizeSubDirsR(test, test_dst);
        }
    }
    @Override
    protected boolean checkIfPaused() throws Exception {
        RemoteFile pausedFile = new RemoteFile(packageRemoteWorkspace, Constants.pause);
        return user.connection.Exists(pausedFile);
    }
    @Override
    protected void PackageWorkspaceCreation() throws Exception {
        if (!CheckModules()) {
            return;
        }
        //--
        testingPackage.readJson();
        //--
        LinkedHashMap<Integer, File> tests = getTestsFromJson();
        //создать папку для пакета.
        user.connection.RMDIR(packageRemoteWorkspace.full_name);
        user.connection.sftpChannel.mkdir(packageRemoteWorkspace.full_name);
        //--
        DVMCompilationPackage_json compilationPackage_json = new DVMCompilationPackage_json();
        DVMRunPackage_json runPackage_json = new DVMRunPackage_json();
        //положить туда запакованные тексты задач.
        for (DVMCompilationTask compilationTask : testingPackage.package_json.compilationTasks) {
            String makefileText = generateMakefile(tests.get(compilationTask.test_id), compilationTask.language, testingPackage.drv, compilationTask.flags);
            //--
            compilationPackage_json.tasks.add(new CompilationTask_json(compilationTask, makefileText));
            //--
            for (DVMRunTask runTask : compilationTask.runTasks) {
                //--
                runPackage_json.tasks.add(new RunTask_json(runTask));
            }
        }
        //--
        RemoteFile compilationFile_json = new RemoteFile(packageRemoteWorkspace, "compilationTasks.json");
        RemoteFile runFile_json = new RemoteFile(packageRemoteWorkspace, "runTasks.json");
        //--
        user.connection.writeToFile(Utils_.gson.toJson(compilationPackage_json),compilationFile_json);
        user.connection.writeToFile(Utils_.gson.toJson(runPackage_json), runFile_json);
        // --
        user.connection.MKDIR(new RemoteFile(packageRemoteWorkspace, "state"));
    }
    @Override
    protected void AnalyseResults() throws Exception {
        testingPackage.checkFinishState();
        UpdatePackageState();
        //теперь удалить распакованную папку с результатами. для скачки нужен только архив.
        File unpacked_results = new File(packageLocalWorkspace.getAbsolutePath(), "results");
        try {
            Utils_.forceDeleteWithCheck(unpacked_results);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    @Override
    protected void PackageStart() throws Exception {
        String plannerStartCommand = String.join(" ",
                Utils_.DQuotes(getPlanner()),
                Utils_.DQuotes(user.workspace),
                Utils_.DQuotes(packageRemoteWorkspace.full_name),
                Utils_.DQuotes(testingPackage.kernels),
                Utils_.DQuotes(testingPackage.drv));
        user.connection.startShellProcess(packageRemoteWorkspace, "planner_output",
                "ulimit -s unlimited", plannerStartCommand);
        //---
        RemoteFile PID = new RemoteFile(packageRemoteWorkspace, "PID");
        while (!user.connection.Exists(PID)) {
            Print("PID not found");
            Utils_.sleep(1000);
        }
        testingPackage.PID = user.connection.readFromFile(PID).replace("\n", "").replace("\r", "");
        //---
        System.out.println("PID=" + Utils_.Brackets(testingPackage.PID));
        RemoteFile STARTED = new RemoteFile(packageRemoteWorkspace, "STARTED");
        while (!user.connection.Exists(STARTED)) {
            Print("waiting for package start...");
            Utils_.sleep(1000);
        }
    }
    @Override
    protected boolean CheckNextState() throws Exception {
        boolean progress_changed = false;
        boolean state_changed = false;
        RemoteFile progress = new RemoteFile(packageRemoteWorkspace, "progress");
        if (user.connection.Exists(progress)) {
            String s = user.connection.readFromFile(progress);
            int current_progress = Integer.parseInt(s);
            if (current_progress != testingPackage.progress) {
                Print("progress changed: " + current_progress);
                testingPackage.progress = current_progress;
                progress_changed = true;
            }
        }
        RemoteFile stateDir = new RemoteFile(packageRemoteWorkspace, "state");
        //состояния пакета могут меняться только по возрастанию. ищем, появилось ли такое.
        Vector<TasksPackageState> higherStates = testingPackage.state.getHigherStates();
        Collections.reverse(higherStates); //берем в обратном порядке, чтобы быстрее найти высшее.
        for (TasksPackageState state : higherStates) {
            RemoteFile file = new RemoteFile(stateDir, state.toString());
            if (user.connection.Exists(file)) {
                Print("found new state: " + file.name);
                testingPackage.state = state;
                state_changed = true;
                break;
            }
        }
        //--
        user.connection.iterations++;
        if (user.connection.iterations == 100) {
            Disconnect();
        }
        //--
        if (state_changed)
            switch (testingPackage.state) {
                case CompilationExecution:
                case RunningExecution:
                case Aborted:
                    EmailPackage();
                    break;
            }
        //--
        return progress_changed || state_changed;
    }
    @Override
    protected void DownloadResults() throws Exception {
        Utils_.CheckDirectory(packageLocalWorkspace);
        RemoteFile killedFile = new RemoteFile(packageRemoteWorkspace, "kill");
        if (user.connection.Exists(killedFile)) {
            UpdatePackageState(TasksPackageState.Aborted);
        } else {
            RemoteFile remote_results_archive = new RemoteFile(packageRemoteWorkspace, "results.zip");
            File results_archive = new File(packageLocalWorkspace, "results.zip");
            user.connection.performScript(packageRemoteWorkspace, "zip -r " + Utils_.DQuotes("results.zip") + " " + Utils_.DQuotes("results"));
            //---
            if (user.connection.Exists(remote_results_archive)) {
                user.connection.getSingleFile(remote_results_archive.full_name, results_archive.getAbsolutePath());
                UnzipFolderPass unzipFolderPass = new UnzipFolderPass();
                unzipFolderPass.Do(results_archive.getAbsolutePath(), packageLocalWorkspace.getAbsolutePath(), false);
            }
            UpdatePackageState(TasksPackageState.Analysis);
        }
        //---
        if (testingPackage.eraseWorkspace != 0 && user.connection.Exists(packageRemoteWorkspace))
            user.connection.RMDIR(packageRemoteWorkspace.full_name);
    }
    @Override
    protected void MachineConnectionError() {
        Finalize("Количество безуспешных попыток соединения с машиной " + machine.getURL() + " превысило 10");
    }
    @Override
    protected void Kill() throws Exception {
        if (!testingPackage.PID.isEmpty()) {
            RemoteFile killFile = new RemoteFile(packageRemoteWorkspace, "kill");
            user.connection.writeToFile("fatality", killFile);
        }
    }
    @Override
    protected void InitSessionCredentials() {
        packageRemoteWorkspace = new RemoteFile(user.workspace + "/tests", String.valueOf(testingPackage.id), true);
        packageLocalWorkspace = new File(Global.DVMPackagesDirectory, String.valueOf(testingPackage.id));
    }
    @Override
    protected boolean CheckModules() throws Exception {
        String log = user.connection.CheckModulesVersion();
        if (!log.isEmpty()) {
            testingPackage.description = log;
            testingPackage.state = TasksPackageState.Aborted;
            return false;
        }
        return true;
    }
    @Override
    public String packageDescription() {
        return "DVM";
    }
}
