package _VisualDVM.ProjectData.SapforData.Functions.UI.Graph;
import Common.Utils.Utils_;
import Common.Visual.UI;
import _VisualDVM.Current;
import _VisualDVM.Global;
import _VisualDVM.Passes.All.SPF_GetGraphFunctionPositions;
import _VisualDVM.Passes.PassCode;
import _VisualDVM.ProjectData.SapforData.Functions.FuncCoordinates;
import _VisualDVM.ProjectData.SapforData.Functions.FuncInfo;
import _VisualDVM.ProjectData.SapforData.Functions.FunctionType;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxStylesheet;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Hashtable;
import java.util.LinkedHashMap;

import static com.mxgraph.util.mxConstants.SHAPE_DOUBLE_ELLIPSE;
import static com.mxgraph.util.mxConstants.SHAPE_ELLIPSE;
public class FunctionsGraphUI extends mxGraph {
    //--
    public final static Timer ffTimer = new Timer(1000, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            Global.mainModule.getPass(PassCode.SPF_GetGraphFunctionPositions).Do();
        }
    });
    //---
    private static final int default_width = 80;
    private static final int default_height = 30;
    //форма узлов?
    // https://progi.pro/kak-sdelat-parallelogrammnuyu-vershinu-v-jgraphx-13032580
    //мануал
    //https://jgraph.github.io/mxgraph/docs/manual_javavis.html
    //https://jgraph.github.io/mxgraph/java/docs/com/mxgraph/swing/mxGraphComponent.html
    //общий манул
    //https://jgraph.github.io/mxgraph/docs/manual_javavis.html#1.3
    //манул п кликам?
    //https://github.com/jgraph/mxgraph/blob/master/java/examples/com/mxgraph/examples/swing/ClickHandler.java
    //все образцы
    //https://github.com/jgraph/mxgraph/tree/master/java/examples/com/mxgraph/examples/swing
    //https://github.com/jgraph/jgraphx/blob/master/src/com/mxgraph/util/mxConstants.java
    //про размеры
    //https://stackoverflow.com/questions/51619879/mxgraph-how-to-automatically-resize-mxcell-to-content-width-if-it-exceeds-the
    //---------------------
    //координаты вершин графа
    // public LinkedHashMap<String, Pair<Double, Double>> vertexCoordinates = new LinkedHashMap<>();
    protected GraphInfo graph = null; //инфа о вершинах и ребрах.
    public FunctionsGraphUI(GraphInfo graph_in) {
        graph = graph_in;
        //настройки графа
        setGridEnabled(true);
        //setAllowNegativeCoordinates(false);
        setAllowDanglingEdges(false); //запрет на висячие ребра.
        setCellsEditable(false); //запрет на редактирование клеток
        setCellsResizable(false); //запрет изменения размера клеток
        setKeepEdgesInBackground(true); // ребра на задний план. очень важно.
        setDropEnabled(false);
        setAutoSizeCells(true);
        setConnectableEdges(false);
        setCellsCloneable(false);
        //--------------------
        mxStylesheet stylesheet = getStylesheet();
        Hashtable<String, Object> style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_SHAPE, SHAPE_ELLIPSE);
        style.put(mxConstants.STYLE_FONTCOLOR, "black");
        style.put(mxConstants.STYLE_FILLCOLOR, "yellow");
        style.put(mxConstants.STYLE_FONTSIZE, "16");
        style.put(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_BOLD);
        stylesheet.putCellStyle(FunctionType.Default.toString(), style);
        //------------------------------------------
        style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_SHAPE, SHAPE_ELLIPSE);
        style.put(mxConstants.STYLE_FONTCOLOR, "black");
        style.put(mxConstants.STYLE_FILLCOLOR, "lightgreen");
        style.put(mxConstants.STYLE_FONTSIZE, "16");
        style.put(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_BOLD);
        stylesheet.putCellStyle(FunctionType.Main.toString(), style);
        //------------------------------------------
        style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_SHAPE, SHAPE_ELLIPSE);
        style.put(mxConstants.STYLE_FONTCOLOR, "black");
        style.put(mxConstants.STYLE_FILLCOLOR, "lightgray");
        style.put(mxConstants.STYLE_FONTSIZE, "16");
        style.put(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_BOLD);
        //  style.put(mxConstants.STYLE_STROKECOLOR, "lawngreen"); это и есть границы.
        //  style.put(mxConstants.STYLE_STROKEWIDTH, 5);
        stylesheet.putCellStyle(FunctionType.Standard.toString(), style);
        //------------------------------------------
        style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_SHAPE, SHAPE_ELLIPSE);
        style.put(mxConstants.STYLE_FONTCOLOR, "white");
        style.put(mxConstants.STYLE_FILLCOLOR, "darkred");
        style.put(mxConstants.STYLE_FONTSIZE, "16");
        style.put(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_BOLD);
        stylesheet.putCellStyle(FunctionType.NotFound.toString(), style);
        //------------------------------------------
        style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_SHAPE, SHAPE_DOUBLE_ELLIPSE);
        style.put(mxConstants.STYLE_FONTCOLOR, "black");
        style.put(mxConstants.STYLE_FILLCOLOR, "lightblue");
        style.put(mxConstants.STYLE_FONTSIZE, "16");
        style.put(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_BOLD);
        //  style.put(mxConstants.STYLE_STROKECOLOR, "lawngreen"); это и есть границы.
        //  style.put(mxConstants.STYLE_STROKEWIDTH, 5);
        stylesheet.putCellStyle("current", style);
    }
    //---
    // еще один туториал?
    // https://www.47.gbmse.ru/resources/utility/mxgraph/docs/tutorial.html
    //
    //https://github.com/jgraph/mxgraph/tree/master/java/examples/com/mxgraph/examples/swing
    //https://jgraph.github.io/mxgraph/java/docs/com/mxgraph/swing/mxGraphComponent.html
    //https://www.47.gbmse.ru/resources/utility/mxgraph/docs/tutorial.html#3.2
    public mxGraphComponent Draw() {
        FuncInfo funcInfo = null;
        //установить координаты вершин.
        SetCoordinates(); //пустышка.
        Object parent = getDefaultParent();
        getModel().beginUpdate();
        //непосредственное добавление графики
        //------------------------------------
        //размеры
        //https://stackoverflow.com/questions/51619879/mxgraph-how-to-automatically-resize-mxcell-to-content-width-if-it-exceeds-the
        // UI.Info("++");
        LinkedHashMap<String, Object> mxVertexes = new LinkedHashMap<>();
        for (String name : graph.vertexCoordinates.keySet()) {
            funcInfo = Global.mainModule.getProject().allFunctions.get(name);
            String vertexType = funcInfo.type.toString();
            if (SPF_GetGraphFunctionPositions.showByCurrentFunction) {
                FuncInfo current_fi = (FuncInfo) Global.mainModule.get(Current.Function);
                if ((current_fi != null) && (funcInfo.funcName.equals(current_fi.funcName))) {
                    vertexType = "current";
                }
            }
            mxVertexes.put(name,
                    insertVertex(parent,
                            name, //идентификатор вершины. должен быть уникальным.
                            name, //объект не стоит делать. какие то мутки при перетаскивании объектов.
                            //строки-ключа уже достаточно
                            //координаты вершины
                            graph.vertexCoordinates.get(name).getKey(),
                            graph.vertexCoordinates.get(name).getValue(),
                            default_width, default_height,
                            vertexType
                    ));
        }
        for (String name : graph.vertexMap.keySet()) {
            mxCell cell = (mxCell) mxVertexes.get(name);
            mxRectangle preferred = getPreferredSizeForCell(cell);
            mxGeometry current = cell.getGeometry();
            current.setWidth((preferred.getWidth() > default_width) ? preferred.getWidth() : default_width);
            //updateCellSize(cell, true); это если просто везде выставить авторазмер.
            //тут же нам нужно применить его только в случае если ширина меньше чем надо.
            for (String neigbor : graph.vertexMap.get(name)) {
                insertEdge(parent, null,
                        //надпись над ребром.
                        "",
                        //вершина 1
                        mxVertexes.get(name),
                        //вершина 2
                        mxVertexes.get(neigbor));
            }
        }
        //------------------------------------
        //https://java.hotexamples.com/examples/com.mxgraph.view/mxGraph/setAutoSizeCells/java-mxgraph-setautosizecells-method-examples.html
        groupCells();
        getModel().endUpdate();
        //------------------------------------
        //обертка графа контролом
        mxGraphComponent graphComponent = new mxGraphComponent(this);
        graphComponent.setZoomFactor(1.10);
        //--
        // https://programtalk.com/java-more-examples/com.mxgraph.util.mxEvent.UP/
        addListener(mxEvent.CELLS_MOVED, new mxIEventListener() {
            @Override
            public void invoke(Object o, mxEventObject mxEventObject) {
                //Сохранение координат графа при сдвиге узла
                if (mxEventObject.getProperties().containsKey("cells")) {
                    Object[] cells = (Object[]) mxEventObject.getProperties().get("cells");
                    for (Object cell_ : cells) {
                        if (cell_ instanceof mxCell) {
                            mxCell cell = (mxCell) cell_;
                            if (cell.isVertex()) {
                                String funcName = (String) cell.getValue();
                                FuncCoordinates coords = null;
                                //  UI.Info(cell.getGeometry().getPoint().toString());
                                Point point = cell.getGeometry().getPoint();
                                try {
                                    if (Global.mainModule.getProject().db.funcCoordinates.containsKey(funcName)) {
                                        coords = Global.mainModule.getProject().db.funcCoordinates.get(funcName);
                                        coords.name = funcName;
                                        coords.X = point.getX();
                                        coords.Y = point.getY();
                                        Global.mainModule.getProject().db.Update(coords);
                                    } else {
                                        coords = new FuncCoordinates();
                                        coords.name = funcName;
                                        coords.X = point.getX();
                                        coords.Y = point.getY();
                                        Global.mainModule.getProject().db.Insert(coords);
                                    }
                                } catch (Exception ignored) {
                                }
                            }
                        }
                    }
                }
            }
        });
        graphComponent.getGraphControl().setComponentPopupMenu(new FunctionsGraphMenu());
        //обработка клика мышом.
        graphComponent.getGraphControl().addMouseListener(new MouseAdapter() {
            public void mouseReleased(MouseEvent e) {
                Object cell = graphComponent.getCellAt(e.getX(), e.getY());
                if (cell != null) {
                    mxCell mx_cell = (mxCell) cell;
                    if (mx_cell.isVertex()) {
                        String func_name = (String) mx_cell.getValue();
                        FuncInfo fi = Global.mainModule.getProject().allFunctions.get(func_name);
                        switch (e.getClickCount()) {
                            case 1:
                                Global.mainModule.set(Current.SelectedFunction, fi);
                                break;
                            case 2:
                                switch (fi.type) {
                                    case Default:
                                    case Main:
                                        fi.Show(true);
                                        break;
                                    case Standard:
                                    case NotFound:
                                        UI.Info("процедура " + Utils_.Brackets(func_name) + " " + fi.type.getDescription());
                                        break;
                                }
                                break;
                        }
                    }
                }
            }
        });
        graphComponent.getViewport().setBackground(Color.WHITE);
        return graphComponent;
    }
    //по умолчанию ничего не меняем.
    public void SetCoordinates() {
    }
    public void SaveCoords() {
    }
}
