package _VisualDVM.GlobalData.RunConfiguration;
import Common.CommonConstants;
import Common.Database.Objects.iDBObject;
import Common.Passes.PassException;
import Common.Utils.TextLog;
import Common.Utils.Utils_;
import _VisualDVM.Global;
import _VisualDVM.GlobalData.Compiler.Compiler;
import _VisualDVM.GlobalData.DVMParameter.DVMParameter;
import _VisualDVM.GlobalData.EnvironmentValue.EnvironmentValue;
import _VisualDVM.GlobalData.Tasks.CompilationTask.CompilationTask;
import _VisualDVM.GlobalData.Tasks.RunTask.RunTask;
import _VisualDVM.GlobalData.Tasks.TaskState;
import _VisualDVM.ProjectData.Project.db_project_info;
import com.sun.org.glassfish.gmbal.Description;

import java.util.LinkedHashMap;
import java.util.Vector;
import java.util.stream.IntStream;
public class RunConfiguration extends iDBObject {
    public static final int maxProc = 16;
    public int machine_id;
    //---------------------------------------->
    @Description("DEFAULT -1")
    public int compiler_id = CommonConstants.Nan;
    public String LauncherCall = ""; //например DVM или mpirun
    public String LauncherOptions = ""; //например run
    //--------------------------------------
    //---------------------------------------
    //в случае mpi всегда одномерная.  в случае DVM может быть многомерной
    //------------------------>>
    @Description("DEFAULT ''")
    public String matrix = ""; //решетка. устаревшее поле.
    //------------------------>>
    @Description("DEFAULT ''")
    public String minMatrix = "";
    @Description("DEFAULT ''")
    public String maxMatrix = "";
    @Description("DEFAULT 0")
    public int dim = 0;
    @Description("DEFAULT 0")
    public int cube = 0;
    //------------------------>>
    //аргументы командной строки - в линию- для запуска
    public String args = ""; //аргументы КС
    //---------------------------------------
    @Description("DEFAULT 0")
    public int gcov = 0; //совместимость. гков отныне только на локалке.
    public static Vector<Integer> getBounds(String bounds) {
        String[] dims_ = bounds.split(" ");
        Vector<Integer> res = new Vector<>();
        for (String dim_ : dims_) {
            int dim = 1;
            try {
                dim = Integer.parseInt(dim_);
            } catch (Exception ex) {
                Utils_.MainLog.PrintException(ex);
            }
            res.add(dim);
        }
        return res;
    }
    public static boolean checkCube(Vector<Integer> m) {
        return IntStream.range(1, m.size()).allMatch(j -> m.get(j).equals(m.get(0)));
    }
    public static void gen_rec(Vector<Integer> from, Vector<Integer> to, Vector<Vector<Integer>> res, int index, int dim_, boolean cube_) {
        if (index < dim_) {
            Vector<Vector<Integer>> old = new Vector<>();
            for (Vector<Integer> L : res)
                old.add(L);
            res.clear();
            for (int i = from.get(index); i <= to.get(index); ++i) {
                for (Vector<Integer> L : old) {
                    Vector<Integer> buffer = new Vector<>(L);
                    buffer.add(i);
                    if (!cube_ || checkCube(buffer))
                        res.add(buffer);
                }
            }
            gen_rec(from, to, res, index + 1, dim_, cube_);
        }
    }
    //для окна конфигурации.
    public static void validateMatrixes(String minMatrix_, String maxMatrix_, int dim_, boolean cube_, TextLog log) {
        Vector<Vector<Integer>> res_ = new Vector<>();
        Vector<String> res = new Vector<>();
        if (dim_ > 0) {
            Vector<Integer> from = getBounds(minMatrix_);
            Vector<Integer> to = getBounds(maxMatrix_);
            if (from.size() != to.size()) {
                log.Writeln_("Верхняя и нижняя границы матриц конфигурации имеют разные размерности");
                return;
            }
            if (from.size() != dim_) {
                log.Writeln_("Границы матриц не совпадают с размерностью конфигурации");
                return;
            }
            //1 стадия. заполнение.
            for (int j = from.get(0); j <= to.get(0); ++j) {
                Vector<Integer> m = new Vector<>();
                res_.add(m);
                m.add(j);
            }
            //---
            if (dim_ > 1)
                gen_rec(from, to, res_, 1, dim_, cube_);
            for (Vector<Integer> m : res_) {
                Vector<String> ms = new Vector<>();
                for (int i : m)
                    ms.add(String.valueOf(i));
                res.add(String.join(" ", ms));
            }
        } else
            res.add("");
        if (res.isEmpty())
            log.Writeln_("По заданным границам не будет сгенерировано ни одной матрицы.");
    }
    //---------------------------------------->
    public Compiler getCompiler() {
        return Global.mainModule.getDb().getById(Compiler.class, compiler_id);
    }
    public boolean isCube() {
        return cube != 0;
    }
    public void setCube(boolean cube_in) {
        cube = cube_in ? 1 : 0;
    }
    public String printCube() {
        return isCube() ? "Да" : "Нет";
    }
    public void Patch() {
        if (!matrix.isEmpty()) {
            // узнать из старой матрицы параметры минимума и максимума, и размерность.
            String[] dims = matrix.split(" ");
            dim = dims.length;
            Vector<String> minDims = new Vector<>();
            for (int i = 1; i <= dim; ++i)
                minDims.add("1");
            minMatrix = String.join(" ", minDims);
            maxMatrix = matrix;
            cube = 1;
            matrix = "";
        }
    }
    public String getDescription() {
        String res = "";
        if (!LauncherCall.isEmpty()) {
            res += Utils_.Brackets(LauncherCall);
            if (!LauncherOptions.isEmpty())
                res += " " + Utils_.Brackets(LauncherOptions);
        } else res = " — ";
        return res;
    }
    public String getLaunchScriptText(String binary_name, String task_matrix) {
        String res = "";
        if (!LauncherCall.isEmpty()) {
            res += Utils_.DQuotes(LauncherCall);
            if (!LauncherOptions.isEmpty())
                res += " " + LauncherOptions;
            if (!task_matrix.isEmpty())
                res += " " + task_matrix;
        }
        if (!res.isEmpty())
            res += " ";
        res += Utils_.DQuotes("./" + binary_name);
        if (!args.isEmpty())
            res += " " + args;
        return res;
    }
    public String getLaunchShortDescription() {
        String res = "";
        if (compiler_id != CommonConstants.Nan) {
            res += getCompiler().description;
            if (!LauncherOptions.isEmpty())
                res += " " + LauncherOptions;
        }
        if (!res.isEmpty())
            res += " ";
        if (!args.isEmpty())
            res += " " + args;
        return res;
    }
    @Override
    public String getFKName() {
        return "run_configuration_id";
    }
    public Vector<String> getEnvList() {
        return Global.mainModule.getDb().getVectorStringByFK(this, EnvironmentValue.class);
    }
    public Vector<String> getParList() {
        return Global.mainModule.getDb().getVectorStringByFK(this, DVMParameter.class);
    }
    public LinkedHashMap<String, String> getEnvMap() {
        LinkedHashMap<Integer, EnvironmentValue> envs = Global.mainModule.getDb().getMapByFKi(this, EnvironmentValue.class);
        LinkedHashMap<String, String> res = new LinkedHashMap<>();
        for (EnvironmentValue e : envs.values()) {
            if (!res.containsKey(e.name))
                res.put(e.name, e.value);
        }
        return res;
    }
    public Vector<String> getMatrixes() throws Exception {
        Vector<Vector<Integer>> res_ = new Vector<>();
        Vector<String> res = new Vector<>();
        if (dim > 0) {
            Vector<Integer> from = getBounds(minMatrix);
            Vector<Integer> to = getBounds(maxMatrix);
            if (from.size() != to.size())
                throw new PassException("Верхняя и нижняя границы матриц конфигурации имеют разные размерности");
            if (from.size() != dim)
                throw new PassException("Границы матриц не совпадают с размерностью конфигурации");
            //1 стадия. заполнение.
            for (int j = from.get(0); j <= to.get(0); ++j) {
                Vector<Integer> m = new Vector<>();
                res_.add(m);
                m.add(j);
            }
            //---
            if (dim > 1)
                gen_rec(from, to, res_, 1, dim, isCube());
            for (Vector<Integer> m : res_) {
                Vector<String> ms = new Vector<>();
                for (int i : m)
                    ms.add(String.valueOf(i));
                res.add(String.join(" ", ms));
            }
        } else
            res.add("");
        if (res.isEmpty())
            throw new PassException("Конфигурация не содержит ни одной матрицы.");
        return res;
    }
    public Vector<RunTask> generateRunTasks(db_project_info info, CompilationTask ctask) throws Exception {
        Vector<RunTask> res = new Vector<>();
        Vector<String> matrixes_ = getMatrixes();
        for (String matrix_ : matrixes_) {
            RunTask task = new RunTask();
            //->
            task.machine_id = machine_id;
            task.user_id = ctask.user_id;
            task.run_configuration_id = id;
            task.compilation_task_id = ctask.id;
            task.project_path = info.Home.getAbsolutePath();
            task.project_description = info.description;
            //------------------------------------------
            task.matrix = matrix_;
            task.maxtime = info.run_maxtime;
            task.state = TaskState.Inactive;
            //->
            res.add(task);
        }
        return res;
    }
}
