package _VisualDVM.Passes.All;
import Common.Utils.Pair;
import Common.Utils.Utils_;
import _VisualDVM.Global;
import _VisualDVM.Passes.PassCode;
import _VisualDVM.Passes.Sapfor.SilentSapforPass;
import _VisualDVM.ProjectData.SapforData.Functions.FuncCall;
import _VisualDVM.ProjectData.SapforData.Functions.FuncCallH;
import _VisualDVM.ProjectData.SapforData.Functions.FuncInfo;
import _VisualDVM.ProjectData.SapforData.Functions.FunctionType;
import _VisualDVM.ProjectData.SapforData.Functions.Json.FunctionGraphPositionJson;
import _VisualDVM.ProjectData.SapforData.Functions.Json.FunctionsGraphPositionsJson;

import java.util.Vector;
public class SPF_GetGraphFunctionPositions extends SilentSapforPass {
    //-ФИЛЬТРАЦИЯ ГРАФА    -----------------------------------
    public static boolean showStandardFunctions = true;
    public static boolean showExternalFunctions = true;
    public static boolean showUnreachableFunctions = true;
    public static boolean showByCurrentFunction = false;
    public static boolean showIn = true;
    public static boolean showOut = true;
    public static int depth = 1;
    public static String filterName = "";
    @Override
    public String getIconPath() {
        return "/icons/screen.png";
    }
    @Override
    public String getButtonText() {
        return "";
    }
    @Override
    protected boolean canStart(Object... args) throws Exception {
        return Global.mainModule.getPass(PassCode.SPF_GetGraphFunctions).isDone() && super.canStart(args);
    }
    //--------------------
    @Override
    public boolean needsConfirmations() {
        return false;
    }
    @Override
    protected boolean needsAnimation() {
        return false;
    }
    @Override
    protected PassCode necessary() {
        return null;
    }
    //    return PassCode_2021.SPF_GetGraphFunctions;
    @Override
    protected void performPreparation() throws Exception {
        target.functionsGraph.Clear();
    }
    @Override
    protected void showPreparation() {
        Global.mainModule.getUI().getMainWindow().getProjectWindow().ShowNoFunctions();
    }
    @Override
    protected void performDone() throws Exception {
        if (!sapfor.getResult().isEmpty())
            unpack(sapfor.getResult());
    }
    public void findFuncMatches_r(FuncCallH level, String funcName, Vector<FuncCallH> matches) {
        if (funcName.equals(level.funcName)) {
            matches.add(level);
            return;
        }
        for (FuncCallH callH : level.calls)
            findFuncMatches_r(callH, funcName, matches);
    }
    public boolean isFunctionUnreachable(String funcName) {
        if (target.main_functionH == null) return true; //нет гпе. недостижимо всё!
        Vector<FuncCallH> res = new Vector<>();
        findFuncMatches_r(target.main_functionH, funcName, res);
        return res.isEmpty();
    }
    public boolean isVisible(FuncInfo fi) {
        return (showStandardFunctions || !fi.type.equals(FunctionType.Standard)) &&
                (showExternalFunctions || !fi.type.equals(FunctionType.NotFound)) &&
                (showUnreachableFunctions || !isFunctionUnreachable(fi.funcName)) &&
                fi.funcName.contains(filterName);
    }
    public void getNeighbors_r(Vector<String> res, String name, int depth, boolean in, boolean out) {
        if (!res.contains(name)) {
            res.add(name);
            if (depth > 0) {
                if (out) {
                    for (FuncCall call : target.allFunctions.get(name).callsFrom)
                        getNeighbors_r(res, call.funcName, depth - 1, in, true);
                }
                if (in) {
                    for (FuncInfo parent : target.allFunctions.values()) {
                        for (FuncCall call : parent.callsFrom) {
                            if (call.funcName.equals(name)) {
                                getNeighbors_r(res, parent.funcName, depth - 1, true, out);
                            }
                        }
                    }
                }
            }
        }
    }
    public void getNeighborsNoDepth_r(Vector<String> res, String name, boolean in, boolean out) {
        if (!res.contains(name)) {
            res.add(name);
            if (out) {
                for (FuncCall call : target.allFunctions.get(name).callsFrom)
                    getNeighborsNoDepth_r(res, call.funcName, in, true);
            }
            if (in) {
                for (FuncInfo parent : target.allFunctions.values()) {
                    for (FuncCall call : parent.callsFrom) {
                        if (call.funcName.equals(name)) {
                            getNeighborsNoDepth_r(res, parent.funcName, true, out);
                        }
                    }
                }
            }
        }
    }
    public Vector<String> getNeighbors(String name, int depth, boolean in, boolean out) {
        Vector<String> res = new Vector<>();
        if (depth > 0)
            getNeighbors_r(res, name, depth, in, out);
        else getNeighborsNoDepth_r(res, name, in, out);
        return res;
    }
    public String packFgSettings() {
        Pair<Integer, Integer> screenDims = Global.mainModule.getUI().getMainWindow().getProjectWindow().getFunctionsWindow().getFunctionsGraphPanelSizes();
        int x = (int) (screenDims.getKey() * target.fgScreen);
        int y = (int) (screenDims.getValue() * target.fgScreen);
        Vector<String> visibleFuncNames = new Vector<>();
        if (showByCurrentFunction && Global.mainModule.HasFunction()) {
            Vector<String> rawVisible =
                    getNeighbors(Global.mainModule.getFunction().funcName,
                            depth,
                            showIn,
                            showOut);
            //   String text = String.join("\n", rawVisible);
            //   UI.Info(text);
            for (String funcName : rawVisible)
                if (!visibleFuncNames.contains(funcName) && isVisible(target.allFunctions.get(funcName)))
                    visibleFuncNames.add(funcName);
        } else {
            for (FuncInfo fi : target.allFunctions.values()) {
                if (isVisible(fi))
                    visibleFuncNames.add(fi.funcName);
            }
        }
        String res = "|" + target.fgIterations + "|" + target.fgResistance + "|" + x + "|" + y + "|" + visibleFuncNames.size();
        if (visibleFuncNames.size() > 0) res += "|" + String.join("|", visibleFuncNames);
        return res;
    }
    /*
    public static void getNearestFunctions_r(Vector<String> res, String funcName, int depth, boolean in, boolean out) {
        if (depth > 0) {
            res.add(funcName);
            if (out) {
                FuncInfo fi = Current.getProject().allFunctions.get(funcName);
                for (FuncCall fc : fi.calls) {
                    if (!res.contains(fc.funcName)) {
                        res.add(fc.funcName);
                        getNearestFunctions_r(res, fc.funcName, depth - 1, in, out);
                    }
                }
            }
            //!!
            if (in) {
                //ищем кто нас вызывает
                for (FuncInfo fi : Current.getProject().allFunctions.values()) {
                    for (FuncCall fc : fi.calls)
                        if (fc.funcName.equals(funcName)) {
                            res.add(fi.funcName);
                            getNearestFunctions_r(res, fi.funcName, depth - 1, in, out);
                        }
                }
            }
        }
    }
    public static void getNearestFunctions_r(Vector<String> res, String funcName, boolean in, boolean out) {
        res.add(funcName);
        if (out) {
            FuncInfo fi = Current.getProject().allFunctions.get(funcName);
            for (FuncCall fc : fi.calls) {
                if (!res.contains(fc.funcName)) {
                    res.add(fc.funcName);
                    getNearestFunctions_r(res, fc.funcName, in, true);
                }
            }
        }


        if (in) {
            //ищем кто нас вызывает
            for (FuncInfo fi : Current.getProject().allFunctions.values()) {
                for (FuncCall fc : fi.calls)
                    if (fc.funcName.equals(funcName)) {
                        res.add(fi.funcName);
                        getNearestFunctions_r(res, fi.funcName, true, out);
                    }
            }
        }
    }
    public static Vector<String> getNearestFunctions(String funcName, int depth, boolean in, boolean out) {
        Vector<String> res = new Vector<>();
        if (depth > 0)
            getNearestFunctions_r(res, funcName, depth, in, out);
        else
            getNearestFunctions_r(res, funcName, in, out);
        return res;
    }
     */
    protected void unpack(String packed) throws Exception {
        FunctionsGraphPositionsJson positionsJson = Utils_.gson.fromJson(packed, FunctionsGraphPositionsJson.class);
        for (FunctionGraphPositionJson positionJson : positionsJson.allPositions) {
            target.functionsGraph.addVertex(positionJson.functionName);
            target.functionsGraph.vertexCoordinates.put(positionJson.functionName, new Pair<>(positionJson.x, positionJson.y));
        }
        //теперь добавить ребер.
        for (String funcName : target.functionsGraph.vertexMap.keySet()) {
            FuncInfo fi = target.allFunctions.get(funcName);
            for (FuncCall fc : fi.callsFrom) {
                if (target.functionsGraph.vertexMap.containsKey(fc.funcName))
                    target.functionsGraph.addEdge(
                            funcName,
                            fc.funcName
                    );
            }
        }
        //---
        //теперь соотнести с сохранением.
        for (String funcName : target.db.funcCoordinates.Data.keySet()) {
            if (target.functionsGraph.vertexCoordinates.containsKey(funcName)) {
                target.functionsGraph.vertexCoordinates.replace(funcName,
                        new Pair<>(
                                target.db.funcCoordinates.get(funcName).X,
                                target.db.funcCoordinates.get(funcName).Y
                        ));
            }
        }
    }
    @Override
    protected void body() throws Exception {
        sapfor.RunAnalysis(
                getSapforPassName(),
                -Global.messagesServer.getPort(),
                target.sapforProperties.pack() +
                        packFgSettings(),
                target.getProjFile().getAbsolutePath());
    }
    @Override
    protected void showDone() throws Exception {
        Global.mainModule.getUI().getMainWindow().getProjectWindow().ShowFunctions();
    }
}
