package _VisualDVM.ProjectData.SapforData.Arrays;
import Common.Database.Objects.DBObject;
import Common.Utils.IntegerPairJson;
import Common.Utils.Utils_;
import _VisualDVM.Global;
import _VisualDVM.Passes.PassCode;
import _VisualDVM.ProjectData.DBArray.DBArray;
import _VisualDVM.ProjectData.SapforData.Arrays.Templates.TemplateDimension;
import _VisualDVM.ProjectData.SapforData.Arrays.Templates.TemplateDimensionState;
import _VisualDVM.ProjectData.SapforData.Regions.ParallelRegion;
import _VisualDVM.ProjectData.SapforData.Regions.UI.ArrayAlignmentBar;
import com.google.gson.annotations.Expose;

import javax.swing.*;
import java.math.BigInteger;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Vector;
import java.util.stream.Collectors;
public class ProjectArray extends DBObject {
    //--
    //----------------------------------------------------------------------
    public static final String[] alignNames = {"i", "j", "k", "l", "m", "n", "q", "r", "s", "t", "u", "w", "x", "y", "z"};
    @Expose
    public long id;
    @Expose
    public String packedAddress;
    @Expose
    public String name;
    @Expose
    public String shortName;
    @Expose
    public int dimSize;
    @Expose
    public int typeSize;
    @Expose
    public String locName;
    @Expose
    public int isTemplFlag;
    @Expose
    public int isLoopArrayFlag;
    @Expose
    public Vector<ArrayDecl> declPlaces = new Vector<>();
    public String UniqKey;
    public BigInteger address;
    //только для шаблонов. для ФИЛЬТРА управления распределением измерений.
    //<editor-fold desc="Функционал шаблона">
    public Vector<BigInteger> regIDs;
    public Vector<TemplateDimension> dimensions;
    public LinkedHashMap<Long, ProjectArray> links;
    public ProjectArray align_template = null; //ссылка на шаблон на который идет выравнивание.
    public String spaces_shift = ""; //смещение из пробелов
    public ParallelRegion parent_region = null; //родительяская область распараллеливания.
    //ИЗМЕНЕНИЕ РАСПРЕДЕЛЕНИЯ
    //текущие
    public LinkedHashMap<Integer, Dimension> ac_current;
    //кандидаты на замену.
    public LinkedHashMap<Integer, Dimension> ac_new;
    //https://stackoverflow.com/questions/4941372/how-to-insert-image-into-jtable-cell
    public ArrayAlignmentBar bar = null;
    @Expose
    int state;
    @Expose
    int location;
    @Expose
    List<Integer> deprecateToDist = new Vector<>();
    @Expose
    List<Integer> mappedDims = new Vector<>();
    @Expose
    Vector<IntegerPairJson> sizes = new Vector<>();
    @Expose
    Vector<String> regions = new Vector<>();
    public static String fill_binary(int d, String binary) {
        int delta = Math.abs(binary.length() - d);
        String res = binary;
        for (int i = 0; i < delta; ++i) {
            res = ("0" + res);
        }
        return res;
    }
    //длина должна быть равной, ищем есть ли совпадающие позиции с единицами
    public static boolean mask(String banned, String variant) {
        for (int i = 0; i < variant.length(); ++i)
            if ((variant.toCharArray()[i] == '1') && (banned.toCharArray()[i] == '1')) //попался, масконосец!
                return true;
        return false;
    }
    //------
    public void print() {
        Vector<String> res = new Vector<>();
        res.add(">");
        res.add("id=" + id);
        res.add("packed_address=" + packedAddress);
        res.add("name=" + name);
        res.add("shortName=" + shortName);
        res.add("dimSize=" + dimSize);
        res.add("typeSize=" + typeSize);
        res.add("state=" + state);
        res.add("location=" + location);
        res.add("locName=" + locName);
        res.add("isTempFlag=" + isTemplFlag);
        res.add("isLoopArrayFlag=" + isLoopArrayFlag);
        res.add("<");
        System.out.println(String.join("\n", res));
    }
    //--
    public ArrayState getState() {
        return ArrayState.fromInt(state);
    }
    public void setState(ArrayState state_in) {
        state = state_in.ordinal();
    }
    public ArrayLocation getLocation() {
        return ArrayLocation.fromInt(location);
    }
    //------
    public void Init() {
        //короткое имя+ функция/модуль/комон+ размерность
        UniqKey = shortName + locName + dimSize;
        if (packedAddress != null) address = new BigInteger(packedAddress);
        for (ArrayDecl arrayDecl : declPlaces) {
            arrayDecl.arrayName = shortName;
            arrayDecl.file = Utils_.toW(arrayDecl.file);
            arrayDecl.messages_presence = "OK";
        }
        //-- полей нет в json поэтому они null
        regIDs = new Vector<>();
        dimensions = new Vector<>();
        links = new LinkedHashMap<>();
        ac_current = new LinkedHashMap<>();
        ac_new = new LinkedHashMap<>();
        //--FileForm
    }
    public String printLinks() {
        Vector<String> res = links.keySet().stream().map(Object::toString).collect(Collectors.toCollection(Vector::new));
        return String.join(" ", res);
    }
    //----------------------------------------------------------------------
    @Override
    public Object getPK() {
        return id;
    }
    public boolean has_decl_place(String bg_file) {
        for (ArrayDecl decl : declPlaces)
            if (decl.file.startsWith(bg_file)) return true;
        return false;
    }
    public boolean has_region(String bg_region) {
        if (bg_region.equals("*"))
            return true;
        for (String region : regions)
            if (region.startsWith(bg_region)) return true;
        return false;
    }
    public String TypeString() {
        return (isLoopArrayFlag == 0) ? ((isTemplFlag == 0) ? "МАССИВ" : "ШАБЛОН") : "ЦИКЛ";
    }
    public String state_symbol() {
        return getState() == ArrayState.Selected ? "☑" : "☐";
    }
    @Override
    public String toString() {
        return id + " " + state_symbol() + " " +
                getState() + " " +
                shortName + " " +
                locName + " : " + location + " " + dimSize + " " + typeSize;
    }
    public String GetShortNameWithDim() {
        String res = shortName + "( ";
        if (sizes != null && sizes.size() != dimSize) {
            for (int i = 0; i < dimSize; ++i) {
                res += alignNames[i];
                if (i < dimSize - 1)
                    res += ", ";
                else res += ")";
            }
        } else {
            for (int i = 0; i < dimSize; ++i) {
                if (sizes.get(i).getKey() != sizes.get(i).getValue() && sizes.get(i).getKey() != -1)
                    res += sizes.get(i).getKey() + ":" + sizes.get(i).getValue();
                else
                    res += alignNames[i];
                if (i < dimSize - 1)
                    res += ", ";
                else res += ")";
            }
        }
        return res;
    }
    //-
    //для таблицы вариантов, измерения (i,j,k,..)
    public String GetShortNameWithDimLetters() {
        String res = shortName + "(";
        for (int i = 0; i < dimSize; ++i) {
            res += alignNames[i];
            if (i < dimSize - 1)
                res += ",";
            else res += ")";
        }
        return res;
    }
    //-
    public Vector<String> GetDeclPlacesList() {
        Vector<String> links = new Vector<>();
        for (ArrayDecl d : declPlaces)
            links.add(d.file + ":" + d.line);
        return links;
    }
    public String GetRegionsText() {
        String res = "";
        for (int i = 0; i < regions.size(); ++i)
            res += regions.get(i) + ((i != regions.size() - 1) ? "," : "");
        return res;
    }
    public boolean DimDisabled(int dim) {
        // не участвует в шаблонах. или явно запрещено
        return (mappedDims.get(dim) == 0) || (deprecateToDist.get(dim) == 1);
    }
    public void CreateDimensions() {
        for (BigInteger regID : regIDs) {
            ParallelRegion region = Global.mainModule.getProject().parallelRegions.get(regID);
            for (int i = 0; i < Global.mainModule.getProject().maxdim; ++i) {
                dimensions.add(new TemplateDimension(
                        i, this, region
                ));
            }
        }
    }
    public Object[] CreateTemplateCells() {
        Vector<Object> res = new Vector<>();
        res.add(this.shortName);
        for (int i = 0; i < dimensions.size(); ++i) {
            res.add(dimensions.get(i));
        }
        return res.toArray();
    }
    //получить двоичное представление варианта распределения и дополнить его нулями.
    public String get_binary(long varID) {
        String binary = Long.toBinaryString(varID);
        return fill_binary(dimSize, binary);
    }
    public Vector<Long> get_varIDs(boolean filter) {
        long d = (long) Math.pow(2, dimSize);
        Vector<Long> varIDs = new Vector<>();
        Vector<String> DimsDep = new Vector<>();
        for (int z = 0; z < mappedDims.size(); ++z)
            DimsDep.add(
                    String.valueOf((DimDisabled(z) ? 1 : 0)));
        String banned_dimensions = String.join("", DimsDep);
        //------ Дляполностью размноженного нужен d-1 ?
        for (long i = 0; i < d; ++i) {
            String binary = get_binary(i);
            if (!mask(banned_dimensions, binary)) {
                if (filter) {
                    boolean valid = true;
                    if (Global.mainModule.getProject().f_distributed())
                        valid = CheckFilterMask(i, true, binary);
                    if (valid && Global.mainModule.getProject().f_multiplied())
                        valid = CheckFilterMask(i, false, binary);
                    if (valid)
                        varIDs.add(i);
                } else varIDs.add(i);
            }
        }
        return varIDs;
    }
    //проверка одиночного вар ида. нужна для минимального покрытия.
    public boolean checkVarID(long i, boolean filter) {
        Vector<String> DimsDep = new Vector<>();
        for (int z = 0; z < mappedDims.size(); ++z)
            DimsDep.add(String.valueOf((DimDisabled(z) ? 1 : 0)));
        String banned_dimensions = String.join("", DimsDep);
        String binary = get_binary(i);
        if (!mask(banned_dimensions, binary)) {
            if (filter) {
                boolean valid = true;
                if (Global.mainModule.getProject().f_distributed())
                    valid = CheckFilterMask(i, true, binary);
                if (valid && Global.mainModule.getProject().f_multiplied())
                    valid = CheckFilterMask(i, false, binary);
                return valid;
            } else return true;
        }
        return false;
    }
    public Vector<Long> get_varIDs() {
        return get_varIDs(true);
    }
    //для первичного заполнения рекурсии
    public Vector<Vector<BigInteger>> get_triples(boolean filter) {
        Vector<Vector<BigInteger>> res = new Vector<>();
        Vector<Long> varIDs = get_varIDs(filter);
        for (long varID : varIDs) {
            for (BigInteger regID : regIDs) //для всех областей
            {
                Vector<BigInteger> tripple = new Vector<>();
                tripple.add(address);
                tripple.add(BigInteger.valueOf(varID));
                tripple.add(regID);
                res.add(tripple);
            }
        }
        return res;
    }
    public String GetShortNameForVariant(long varID) {
        String binary = get_binary(varID);
        String res = shortName + "(";
        for (int i = 0; i < dimSize; ++i) {
            if (i < binary.length()) {
                switch (binary.toCharArray()[i]) {
                    case '0':
                        res += TemplateDimensionState.multiplied.getDescription();
                        break;
                    case '1':
                        res += TemplateDimensionState.distributed.getDescription();
                        break;
                    default:
                        break;
                }
                res += Utils_.ending(i == binary.length() - 1);
            }
        }
        return res;
    }
    //</editor-fold>
    public String getFilterMask() {
        String res = "";
        for (int i = 0; i < dimensions.size(); ++i) {
            res += (dimensions.get(i).state.ordinal());
        }
        return res;
    }
    public boolean CheckFilterMask(long varID, boolean distributed, String binary_in) {
        char s = distributed ? '1' : '0';
        String filterMask = getFilterMask();
        String variant = binary_in.isEmpty() ? get_binary(varID) : binary_in;
        for (int i = 0; i < variant.length(); ++i) {
            if ((filterMask.toCharArray()[i] == s) && (variant.toCharArray()[i] != s)) return false;
        }
        return true;
    }
    public boolean CheckFilterMask(long varID, boolean distributed) {
        return CheckFilterMask(varID, distributed, "");
    }
    public void SaveUserState() throws Exception {
        if (Global.mainModule.getProject().db.savedArrays.Data.containsKey(UniqKey)) {
            DBArray sa = Global.mainModule.getProject().db.savedArrays.Data.get(UniqKey);
            sa.State = getState();
            Global.mainModule.getProject().db.Update(sa);
        } else
            Global.mainModule.getProject().db.Insert(new DBArray(this));
    }
    //-----------------------------------------------------------------------
    @Override
    public boolean isSelectionEnabled() {
        return (getState() == ArrayState.Selected) || (getState() == ArrayState.None);
    }
    @Override
    public ImageIcon GetDisabledIcon() {
        return Utils_.getIcon("/icons/Arrays/" + getState().toString() + ".png");
    }
    @Override
    public void select(boolean flag) {
        Global.mainModule.getPass(PassCode.MassSelectArrays).Do(flag, this);
    }
    @Override
    public boolean isSelected() {
        return (getState() == ArrayState.Selected);
    }
    //-
    public void init_new_ac() {
        ac_new.clear();
        for (int dim : ac_current.keySet())
            ac_new.put(dim, ac_current.get(dim).clone_());
    }
    public boolean ac_need_change() {
        for (int dim : ac_current.keySet()) {
            Dimension old_ = ac_current.get(dim);
            Dimension new_ = ac_new.get(dim);
            if ((old_.K != new_.K) || (old_.B != new_.B))
                return true;
        }
        return false;
    }
    public boolean canBeDistributed() {
        return (isLoopArrayFlag == 0) && (isTemplFlag == 0);
    }
}