package _VisualDVM.TestingSystem.DVM.DVMPackage;
import Common.CommonConstants;
import Common.Database.Objects.DBObject;
import Common.Utils.Index;
import Common.Utils.Pair;
import Common.Utils.Utils_;
import _VisualDVM.ComponentsServer.UserAccount.UserAccount;
import _VisualDVM.Constants;
import _VisualDVM.Global;
import _VisualDVM.GlobalData.Compiler.Compiler;
import _VisualDVM.GlobalData.Machine.Machine;
import _VisualDVM.GlobalData.Machine.MachineType;
import _VisualDVM.GlobalData.Tasks.TaskState;
import _VisualDVM.GlobalData.User.User;
import _VisualDVM.TestingSystem.Common.Configuration.Configuration;
import _VisualDVM.TestingSystem.Common.Group.Group;
import _VisualDVM.TestingSystem.Common.TasksPackageState;
import _VisualDVM.TestingSystem.Common.Test.Test;
import _VisualDVM.TestingSystem.Common.Test.TestType;
import _VisualDVM.TestingSystem.Common.TestingPackage.TestingPackage;
import _VisualDVM.TestingSystem.DVM.DVMConfiguration.DVMConfiguration;
import _VisualDVM.TestingSystem.DVM.DVMPackageConfiguration.DVMPackageConfiguration;
import _VisualDVM.TestingSystem.DVM.DVMSettings.DVMSettings;
import _VisualDVM.TestingSystem.DVM.DVMTasks.DVMCompilationTask;
import _VisualDVM.TestingSystem.DVM.DVMTasks.DVMRunTask;
import _VisualDVM.TestingSystem.DVM.DVMTasks.DVMTask;
import _VisualDVM.Utils;
import com.sun.org.glassfish.gmbal.Description;
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.LinkedHashMap;
import java.util.List;
import java.util.Vector;
public class DVMPackage extends TestingPackage<DVMPackage_json> {
    //---
    public String machine_name = "";
    public String machine_address = "";
    public int machine_port = 22;
    //---
    public String user_name = "";
    public String user_workspace;
    //---
    @Description("DEFAULT 1")
    public int eraseWorkspace = 1; //удалять рабочее пространство пакета на целевой машине.(отключать для отладки)
    //---
    @Description("IGNORE")
    public Vector<DVMPackageConfiguration> configurations = null;
    public DVMPackage() {
    }
    public DVMPackage(UserAccount account,
                      Machine machine,
                      User user,
                      Compiler compiler,
                      Vector<DVMConfiguration> dvmConfigurations,
                      int neeedsEmail_in, int eraseWorkspace_in) {
        id = CommonConstants.Nan;
        //-
        sender_name = account.name;
        sender_address = account.email;
        //-
        machine_name = machine.name;
        machine_address = machine.address;
        machine_port = machine.port;
        //-
        user_name = user.login;
        user_workspace = user.workspace;
        //-
        drv = compiler.call_command;
        version = compiler.getVersionInfo();
        //-
        needsEmail = neeedsEmail_in;
        eraseWorkspace = eraseWorkspace_in;
        //--
        state = TasksPackageState.Queued;
        //--
        for (DVMConfiguration configuration : dvmConfigurations)
            kernels = Math.max(configuration.kernels, kernels);
        //-
        tasksCount = 0;
        Index index = new Index();
        //---
        Vector<DVMCompilationTask> compilationTasks = new Vector<DVMCompilationTask>();
        for (DVMConfiguration configuration : dvmConfigurations) {
            //--
            LinkedHashMap<Integer, Vector<Integer>> groupsTestsIds = Global.testingServer.db.getDVMConfigurationGroupsTestsIds(configuration);
            Vector<Integer> settingsIds = Global.testingServer.db.getDVMConfigurationSettingsIds(configuration);
            for (int group_id : groupsTestsIds.keySet()) {
                Group group = Global.testingServer.db.groups.get(group_id);
                for (int test_id : groupsTestsIds.get(group_id)) {
                    Test test = Global.testingServer.db.tests.get(test_id);
                    //--
                    DVMCompilationTask compilationTask = null;
                    DVMRunTask runTask = null;
                    Vector<DVMRunTask> testRunTasks = new Vector<>(); //для выставления эталона
                    DVMRunTask ethalon = null; //эталон. последовательная задача -s без окружения и матрицы.
                    //--
                    for (int settings_id : settingsIds) {
                        DVMSettings dvmSettings = Global.testingServer.db.dvmSettings.get(settings_id);
                        Vector<Pair<String, String>> params = Global.testingServer.db.getTasksParameters(dvmSettings);
                        //--
                        for (Pair<String, String> p : params) {
                            //--
                            String options_ = Global.normalProperties.CompleteCompilationOptions ?
                                    completeOptions(p.getKey()) : p.getKey();
                            String environments_ = Global.normalProperties.CompleteRunEnvironments ?
                                    completeEnvironments(p.getValue()) : p.getValue();
                            //--
                            compilationTasks.add(compilationTask = new DVMCompilationTask(index, configuration, options_, group, test));
                            //--
                            Vector<String> matrixes = dvmSettings.getMatrixes(test.max_dim);
                            if (p.getKey().trim().equalsIgnoreCase("-s")) {
                                compilationTask.runTasks.add(runTask = new DVMRunTask(
                                        index,
                                        compilationTask,
                                        configuration,
                                        dvmSettings.cube,
                                        dvmSettings.getParamsText(),
                                        group,
                                        test,
                                        "",
                                        options_,
                                        environments_,
                                        kernels
                                ));
                                testRunTasks.add(runTask);
                                if (ethalon == null) ethalon = runTask;
                            } else {
                                for (String matrix : matrixes) {
                                    compilationTask.runTasks.add(runTask = new DVMRunTask(
                                            index,
                                            compilationTask,
                                            configuration,
                                            dvmSettings.cube,
                                            dvmSettings.getParamsText(),
                                            group,
                                            test,
                                            matrix,
                                            options_,
                                            environments_,
                                            kernels));
                                    testRunTasks.add(runTask);
                                }
                            }
                            //--
                            tasksCount += compilationTask.runTasks.size();
                        }
                    }
                    //---
                    if (Global.normalProperties.CreateEthalonTasks &&
                            !group.type.equals(TestType.Performance)) {
                        if (ethalon == null) {
                            String options_ = "-s";
                            String environments_ = "";
                            if (Global.normalProperties.CompleteCompilationOptions)
                                options_ = completeOptions(options_);
                            if (Global.normalProperties.CompleteRunEnvironments)
                                environments_ = completeEnvironments(environments_);
                            compilationTasks.add(compilationTask = new DVMCompilationTask(
                                    index, configuration,
                                    options_,
                                    group, test));
                            compilationTask.runTasks.add(ethalon = new DVMRunTask(
                                    index,
                                    compilationTask,
                                    configuration,
                                    0,
                                    "Is_DVM_STAT=0",
                                    group,
                                    test,
                                    "",
                                    options_, environments_,
                                    kernels));
                            tasksCount++;
                        }
                        //--
                        for (DVMRunTask dvmRunTask : testRunTasks) {
                            dvmRunTask.ethalon_id = ethalon.id;
                        }
                    }
                }
            }
        }
        package_json = new DVMPackage_json(compilationTasks);
        saveConfigurations(dvmConfigurations);
    }
    public DVMPackage(DVMPackage p) {
        super(p);
        this.SynchronizeFields(p);
    }
    public static String completeOptions(String flags_in) {
        if (!flags_in.contains("-shared-dvm")) {
            if (flags_in.isEmpty())
                return "-shared-dvm";
            else return flags_in + " -shared-dvm";
        } else
            return flags_in;
    }
    public static String completeEnvironments(String environmentsSet_in) {
        if (environmentsSet_in.isEmpty()){
            return "DVMH_NO_DIRECT_COPY=" + Utils_.DQuotes("1")+" "+ "DVMH_SET_AFFINITY="+Utils_.DQuotes("0");
        }
        //-
        String res = environmentsSet_in;
        if (!environmentsSet_in.contains("DVMH_NO_DIRECT_COPY")){
            res+=" DVMH_NO_DIRECT_COPY=" + Utils_.DQuotes("1");
        }
        if (!environmentsSet_in.contains("DVMH_SET_AFFINITY")){
            res+=" DVMH_SET_AFFINITY=" + Utils_.DQuotes("0");
        }
        return res;
    }
    @Override
    public Class getJsonClass() {
        return DVMPackage_json.class;
    }
    @Override
    public File getHomeDirectory() {
        return Global.DVMPackagesDirectory;
    }
    //---
    @Override
    public void SynchronizeFields(DBObject src) {
        super.SynchronizeFields(src);
        DVMPackage tasksPackage = (DVMPackage) src;
        machine_name = tasksPackage.machine_name;
        machine_address = tasksPackage.machine_address;
        machine_port = tasksPackage.machine_port;
        user_name = tasksPackage.user_name;
        user_workspace = tasksPackage.user_workspace;
        eraseWorkspace = tasksPackage.eraseWorkspace;
    }
    public Machine getMachine() {
        return new Machine(machine_name, machine_address, machine_port, MachineType.Server);
    }
    public User getUser() {
        return new User(user_name, "", user_workspace);
    }
    public String getMachineURL() {
        return machine_address + ":" + machine_port;
    }
    //получение запакованных планировщиком результатов задач.
    public void getTasksInfo(File packageLocalWorkspace, List<? extends DVMTask> tasks, String file_name) throws Exception {
        LinkedHashMap<Integer, DVMTask> sorted_tasks = new LinkedHashMap<>();
        for (DVMTask task : tasks)
            sorted_tasks.put(task.id, task);
        //--
        File info_file = Utils_.getFile(packageLocalWorkspace.getAbsolutePath(), "results", file_name);
        List<String> lines = FileUtils.readLines(info_file, Charset.defaultCharset());
        for (String packed : lines) {
            if (!packed.isEmpty()) {
                String[] data = packed.split(" ");
                int id = Integer.parseInt(data[0]);
                TaskState state = TaskState.valueOf(data[1]);
                double time = Double.parseDouble(data[2]);
                //--
                DVMTask task = sorted_tasks.get(id);
                task.state = state;
                task.Time = state.equals(TaskState.AbortedByTimeout) ? (task.maxtime + 1) : time;
            }
        }
    }
    @Override
    public void checkFinishState() throws Exception {
        File packageLocalWorkspace = new File(Global.DVMPackagesDirectory, String.valueOf(id));
        boolean hasErrors = false;
        readJson();
        Vector<DVMRunTask> runTasks = new Vector<>();
        for (DVMCompilationTask compilationTask : package_json.compilationTasks)
            runTasks.addAll(compilationTask.runTasks);
        //----
        getTasksInfo(packageLocalWorkspace, package_json.compilationTasks, "CompilationInfo.txt");
        getTasksInfo(packageLocalWorkspace, runTasks, "RunningInfo.txt");
        //--
        int ct_count = 0;
        int rt_count = 0;
        int good = 0;
        //--
        LinkedHashMap<Integer, DVMRunTask> sortedRunTasks = new LinkedHashMap<>();
        Vector<Integer> rte_ids = new Vector<>(); //id задач у которых есть эталон.
        //-
        for (DVMCompilationTask compilationTask : package_json.compilationTasks) {
            compilationTask.dvm_package_id = id;
            if (!compilationTask.state.equals(TaskState.Done))
                hasErrors = true;
            ct_count++;
            File ct_workspace = Paths.get(packageLocalWorkspace.getAbsolutePath(), "results", String.valueOf(compilationTask.id)).toFile();
            if (ct_workspace.exists()) {
                //1 проход. анализ всех задач подряд
                for (DVMRunTask runTask : compilationTask.runTasks) {
                    sortedRunTasks.put(runTask.id, runTask);
                    //--
                    runTask.dvm_package_id = id;
                    rt_count++;
                    runTask.compilation_state = compilationTask.state;
                    runTask.compilation_time = compilationTask.Time;
                    if (compilationTask.state != TaskState.Done) {
                        runTask.state = TaskState.Canceled;
                    } else {
                        File rt_workspace = Paths.get(packageLocalWorkspace.getAbsolutePath(), "results", String.valueOf(runTask.id)).toFile();
                        if (rt_workspace.exists() && runTask.state.equals(TaskState.Finished)
                        ) {
                            //анализ задачи на запуск.
                            File outFile = new File(rt_workspace, Constants.out_file);
                            File errFile = new File(rt_workspace.getAbsolutePath(), Constants.err_file);
                            //--
                            String output = FileUtils.readFileToString(outFile);
                            String errors = FileUtils.readFileToString(errFile);
                            //--
                            List<String> output_lines = Arrays.asList(output.split("\n"));
                            List<String> errors_lines = Arrays.asList(errors.split("\n"));
                            //---
                            if (output.trim().isEmpty() || Utils.isCrushed(output_lines, errors_lines)) {
                                runTask.state = TaskState.Crushed;
                            } else {
                                if (Utils.hasNan(output_lines))
                                    runTask.state = TaskState.NaN;
                                else {
                                    //--
                                    Pair<TaskState, Integer> results = new Pair<>(TaskState.Done, 100);
                                    switch (runTask.test_type) {
                                        case Performance:
                                            results = Utils.analyzePerformance(output_lines);
                                            runTask.CleanTime = Utils.parseCleanTime(output);
                                            break;
                                        default:
                                            results = Utils.analyzeCorrectness(output_lines);
                                            break;
                                    }
                                    //---
                                    runTask.state = results.getKey();
                                    runTask.progress = results.getValue();
                                }
                            }
                        }
                    }
                    if (runTask.state.equals(TaskState.Done)) {
                        good++;
                        if (runTask.ethalon_id != CommonConstants.Nan) rte_ids.add(runTask.id);
                        //с эталоном сверяем только если задача выполнена
                    } else {
                        hasErrors = true;
                    }
                }
            }
        }
        //-
        //2 проход. если у успешных задач есть эталоны.
        for (int task_id : rte_ids) {
            DVMRunTask runTask = sortedRunTasks.get(task_id);
            DVMRunTask ethalon = sortedRunTasks.get(runTask.ethalon_id);
            if (ethalon.state.equals(TaskState.Done)) {
                //проверяем только если эталон выполнен.
                //--------------------------------------
                File taskOutFile = Utils_.getFile(
                        packageLocalWorkspace.getAbsolutePath(),
                        "results",
                        String.valueOf(runTask.id),
                        Constants.out_file
                );
                File ethalonOutFile = Utils_.getFile(
                        packageLocalWorkspace.getAbsolutePath(),
                        "results",
                        String.valueOf(ethalon.id),
                        Constants.out_file
                );
                String taskOut = FileUtils.readFileToString(taskOutFile);
                String ethalonOut = FileUtils.readFileToString(ethalonOutFile);
                if (!taskOut.equals(ethalonOut)) {
                    runTask.state = TaskState.DoneWithDiffs;
                    hasErrors = true;
                    good--;
                }
            }
        }
        //-
        progress = 100;
        doneTasksCount = good;
        saveJson(); //запись обновленных результатов пакета в json!
        state = hasErrors ? TasksPackageState.DoneWithErrors : TasksPackageState.Done;
        double percent = (((double) (good)) / tasksCount) * 100.0;
        description = "Выполнено на " + ((int) percent) + "%\n" +
                "Всего задач: " + tasksCount + ", из них с ошибками " + (tasksCount - good);
    }
    @Override
    public void saveConfigurations(Vector<? extends Configuration> configurations_in) {
        configurations = new Vector<>();
        for (Configuration configuration : configurations_in) {
            configurations.add(new DVMPackageConfiguration((DVMConfiguration) configuration));
        }
    }
}

