// GraphTea Project: http://github.com/graphtheorysoftware/GraphTea
// Copyright (C) 2012 Graph Theory Software Foundation: http://GraphTheorySoftware.com
// Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology
// Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/
package graphtea.plugins.main.core.actions;

import graphtea.graph.atributeset.*;
import graphtea.graph.event.EdgeEvent;
import graphtea.graph.event.GraphSelectData;
import graphtea.graph.event.VertexEvent;
import graphtea.graph.graph.Edge;
import graphtea.graph.graph.GraphModel;
import graphtea.graph.graph.Vertex;
import graphtea.graph.old.GStroke;
import graphtea.platform.attribute.*;
import graphtea.platform.core.AbstractAction;
import graphtea.platform.core.BlackBoard;
import graphtea.plugins.main.select.Select;
import graphtea.plugins.main.select.SelectPluginMethods;
import graphtea.plugins.main.ui.GStrokeEditor;
import graphtea.ui.AttributeSetView;
import graphtea.ui.PortableNotifiableAttributeSetImpl;
import graphtea.ui.UIUtils;
import graphtea.ui.components.gpropertyeditor.GPropertyEditor;

import java.util.HashSet;
import java.util.Set;

import static graphtea.graph.old.GStroke.*;

/**
 * the left side property editor of graphtea gui
 *
 * @author Azin Azadi
 */
public class GraphPropertyEditor extends AbstractAction implements AttributeListener {
    public GPropertyEditor getPropertyEditor() {
        return prosheet;
    }

    private GPropertyEditor prosheet;

    public NotifiableAttributeSet NotifiableAttributeSet;

    public AttributeSetView viewer;

    Vertex lastVertex;

    Edge lastEdge;

    private TimeLimitedNotifiableAttrSet target;

    AttributeSetView edgeView = new AttributeSetView();

    AttributeSetView vertexView = new AttributeSetView();

    AttributeSetView graphView = new AttributeSetView();

    AttributeSetView selectView = new AttributeSetView();

    HashSet<String> hiddenVertexAttributes = new HashSet<>();

    HashSet<String> hiddenEdgeAttributes = new HashSet<>();

    PortableNotifiableAttributeSetImpl xx = new PortableNotifiableAttributeSetImpl();

    TimeLimitedNotifiableAttrSet selectionAttributes = new TimeLimitedNotifiableAttrSet(new AttributeSetImpl());

    SelectPluginMethods spm;

    /**
     * constructor
     *
     * @param bb the blackboard of the action
     */
    public GraphPropertyEditor(BlackBoard bb) {
        super(bb);
        spm = new SelectPluginMethods(blackboard);
        prosheet = new GPropertyEditor();
        prosheet.setSize(150, 100);
        UIUtils.setComponent(blackboard, "property editor", prosheet);
        listen4Event(EdgeEvent.EVENT_KEY);
        listen4Event(VertexEvent.EVENT_KEY);
        listen4Event(Select.EVENT_KEY);
        listen4Event(GraphSelectData.EVENT_KEY);

//        graphView.setVisible(GraphAttrSet.CENTERX, false);
//        graphView.setVisible(GraphAttrSet.CENTERY, false);
//        graphView.setVisible(GraphAttrSet.DIRECTED, false);
//        graphView.setVisible(GraphAttrSet.ZOOM, false);
        graphView.setIndex(GraphAttrSet.LABEL, 0);
        graphView.setIndex(GraphAttrSet.DIRECTED, 1);
        graphView.setIndex(GraphAttrSet.DRAW_VERTEX_LABELS, 5);
        graphView.setIndex(GraphAttrSet.DRAW_EDGE_LABELS, 6);
        graphView.setEditable(GraphAttrSet.DIRECTED, false);
        graphView.setEditable(GraphAttrSet.LABEL, true);

//        graphView.setIndex(EdgeAttrSet.SHOW_EDGE_LABELS, 3);

//Storing Id in vertex is deprecated
//        vertexView.setVisible(Vertex.ID, false);
//        vertexView.setVisible(Vertex.XPOS, false);
//        vertexView.setVisible(Vertex.YPOS, false);
        vertexView.setIndex(VertexAttrSet.LABEL, 0);
        vertexView.setIndex(VertexAttrSet.COLOR, 2);
        vertexView.setIndex(VertexAttrSet.SHAPE, 4);
        vertexView.setIndex(VertexAttrSet.BORDER, 6);
        vertexView.setVisible(VertexAttrSet.SELECTED, false);
        vertexView.setEditor(VertexAttrSet.BORDER, new GStrokeEditor() {
            public Object[] getValues() {
                return new GStroke[]{
                        GStroke.empty,
                        simple,
                        solid,
                        strong,
                        dashed,
                        dotted,
                        dashed_dotted,
                        dashed_dotted_dotted,
                        dashed_dashed_dotted
                };
            }
        });
        vertexView.setEditor(VertexAttrSet.COLOR, new GMergedColorEditor(true));
        vertexView.setrenderer(VertexAttrSet.COLOR, new GMergedColorRenderer(true));

//        edgeView.setVisible(EdgeAttrSet.DIRECTED, false);
//        edgeView.setVisible(EdgeAttrSet.LABEL, false);
        edgeView.setEditable(EdgeAttrSet.SOURCE, false);
        edgeView.setEditable(EdgeAttrSet.TARGET, false);
        edgeView.setIndex(EdgeAttrSet.LABEL, 0);
        edgeView.setIndex(EdgeAttrSet.WEIGHT, 1);
        edgeView.setIndex(EdgeAttrSet.ARROW, 4);
        edgeView.setEditor(EdgeAttrSet.COLOR, new GMergedColorEditor(false));
        edgeView.setrenderer(EdgeAttrSet.COLOR, new GMergedColorRenderer(false));
//        updateSelectView();

    }


    private Vertex lastVertex() {
        VertexEvent x = blackboard.getData(VertexEvent.EVENT_KEY);
        return (x == null ? null : x.v);
    }

    private Edge lastEdge() {
        EdgeEvent x = blackboard.getData(EdgeEvent.EVENT_KEY);
        return (x == null ? null : x.e);
    }

    private GraphModel lastGraph() {
        GraphSelectData x = blackboard.getData(GraphSelectData.EVENT_KEY);
        return (x == null ? null : x.g);
    }

    public void performAction(String eventName, Object value) {
        prosheet = (GPropertyEditor) UIUtils.getComponent(blackboard,
                "property editor");
        if (getTarget() != null)
            if (eventName == Select.EVENT_KEY) {
                if (!spm.isSelectionEmpty()) {
                    updatePropertyEditor_selected();
//                    viewer = selectView;
                } else {
                    setTarget(new GraphNotifiableAttrSet(lastGraph()));
                    viewer = graphView;
                }
            }
//        if (eventName ==VertexEvent.name)) {
//            VertexEvent ve = blackboard.get(VertexEvent.name);
//            if (ve.eventType == VertexEvent.PRESSED) {
//                if (spm.isSelectionEmpty()) {
//                    setTarget(new VertexNotifiableAttrSet(lastVertex()));
//                    viewer = vertexView;
//                }
//            }
//        }
//        if (eventName ==EdgeEvent.name)) {
//            EdgeEvent ve = blackboard.get(EdgeEvent.name);
//            if (ve.eventType == EdgeEvent.PRESSED) {
//                if (spm.isSelectionEmpty()) {
//                    setTarget(new EdgeNotifiableAttrSet(lastEdge()));
//                    viewer = edgeView;
//                }
//            }
//        }
        if (eventName == GraphSelectData.EVENT_KEY) {
            setTarget(new GraphNotifiableAttrSet(lastGraph()));
            viewer = graphView;
        }
        updateTarget();
    }

    private void updateTarget() {
        if (prosheet != null && getTarget() != null) {
            xx.setView(viewer);
            xx.setModel(getTarget());
            prosheet.connect(xx);
        }
        if(getTarget() != null) {
            getTarget().addAttributeListener((name, oldVal, newVal) -> blackboard.setData("undo point", lastGraph()));
        }
    }

    public void attributeUpdated(String name, Object oldVal, Object newVal) {
        //occurs whenever one attribute in selection attribute changed by user
        //note that the vertex and edge attributes are updates directly, not from here
        if (!iChangedTheAttribute) {
            String key = name.substring(1);     //the original name of attribute
            if (name.startsWith("v")) { //it was a vertex attribute
                for (Vertex v : spm.getSelectedVertices()) {
                    VertexAttrSet _v = new VertexAttrSet(v);
                    _v.put(key, newVal);
                }
            }
            if (name.startsWith("e")) { //it was an edge attribute
                for (Edge e : spm.getSelectedEdges()) {
                    EdgeAttrSet _e = new EdgeAttrSet(e);
                    _e.put(key, newVal);

                }
            }
        }
    }

    boolean iChangedTheAttribute = false;

    private void updatePropertyEditor_selected() {
        HashSet<Vertex> selectedVertices = spm.getSelectedVertices();
        HashSet<Edge> selectedEdges = spm.getSelectedEdges();

        int numOfSelectedVertices = selectedVertices.size();
        int numOfSelectedEdges = selectedEdges.size();

        //if there was only 1 selected vertex(edge) show the classic editor for it
        if (numOfSelectedVertices + numOfSelectedEdges == 1) {
            if (numOfSelectedVertices == 1) {
                setTarget(new VertexNotifiableAttrSet(selectedVertices.iterator().next()));
                viewer = vertexView;
            } else {
                setTarget(new EdgeNotifiableAttrSet(selectedEdges.iterator().next()));
                viewer = edgeView;
            }
        } else {
            viewer = selectView;

            iChangedTheAttribute = true;
            TimeLimitedNotifiableAttrSet retAtrs = new TimeLimitedNotifiableAttrSet(new AttributeSetImpl());
            retAtrs.removeAttributeListener(this);

            //adding each Vertex/Edge attribute to selectionAttributes
            int i = 0;
            retAtrs.put("Vertices", numOfSelectedVertices);
            selectView.setEditable("Vertices", false);
            selectView.setIndex("Vertices", i++);
            for (Vertex v : selectedVertices) {
                if (i < 500) {
                    AttributeSet vAtrs = new VertexAttrSet(v);
                    vertexView.setAttribute(vAtrs);
                    for (String atrName : vertexView.getNames()) {
                        String key = vertexAtrName(atrName);
                        insertAttributeToSelectionAtrs(retAtrs, key, vAtrs, atrName);
                        selectView.setDisplayName(key, atrName);
                        selectView.setIndex(key, i++);
                    }
                }
            }

            retAtrs.put("   ", "");
            selectView.setEditable("   ", false);
            retAtrs.put("Edges", numOfSelectedEdges);
            selectView.setIndex("   ", i++);
            selectView.setIndex("Edges", i++);
            selectView.setEditable("Edges", false);
            for (Edge e : selectedEdges) {
                if (i < 1000) {
                    AttributeSet eAtrs = new EdgeAttrSet(e);
                    for (String atrName : ((Set<String>)eAtrs.getAttrs().keySet())) {
                        String key = edgeAtrName(atrName);
                        insertAttributeToSelectionAtrs(retAtrs, key, eAtrs, atrName);
                        selectView.setDisplayName(key, atrName);
                        selectView.setIndex(key, i++);
                    }
                }
            }
            selectView.setVisible("v" + VertexAttrSet.LOCATION, false);
            selectView.setVisible("v" + VertexAttrSet.SELECTED, false);
            selectView.setEditor("v" + VertexAttrSet.BORDER, new GStrokeEditor() {
                public Object[] getValues() {
                    return new GStroke[]{
                            GStroke.empty,
                            simple,
                            solid,
                            strong,
                            dashed,
                            dotted,
                            dashed_dotted,
                            dashed_dotted_dotted,
                            dashed_dashed_dotted
                    };
                }
            });
            selectView.setEditor("v" + VertexAttrSet.COLOR, new GMergedColorEditor());
            selectView.setrenderer("v" + VertexAttrSet.COLOR, new GMergedColorRenderer());

            selectView.setEditor("e" + VertexAttrSet.COLOR, new GMergedColorEditor());
            selectView.setrenderer("e" + VertexAttrSet.COLOR, new GMergedColorRenderer());

            viewer = selectView;
            retAtrs.addAttributeListener(this);
//            updateSelectView();
            iChangedTheAttribute = false;

            setTarget(retAtrs);
        }

    }

    /**
     * adds the given atr from source to dest, and if there was some values
     * on the place before, invalidate the place
     *
     * @param dest The destination attribute set
     * @param key     reffers to the atr name in dest
     * @param source The source attribute set
     * @param atrName reffers to the atr name in source that should be inserted to dest
     */
    private void insertAttributeToSelectionAtrs(AttributeSet dest, String key, AttributeSet source, String atrName) {
        if (dest.getAttrs().containsKey(key)) {
            Object oldVal = dest.get(key);
            if (oldVal == null)
                return;
            Object newVal = source.get(atrName);
            if (!oldVal.equals(newVal)) {
                selectView.setValid(key, false);
            }
        } else {
            selectView.setValid(key, true);  //it is valid default (in first time)
        }
        dest.put(key, source.get(atrName));
    }

//    private void updateSelectView() {
//        //selectView is the union of vertexView and edgeView
////        int maxIndex = Integer.MIN_VALUE;
////        selectView.setIndex("Vertices", 0);
//        for (String key : selectionAttributes.getAttrs().keySet()) {
//            String name = key.substring(1);     //the original name of attribute
//            if (key.startsWith("v")) {
//                selectView.setVisible(key, vertexView.isVisible(name));
////                selectView.setDisplayName(key, name);
////                selectView.setCategory(key, vertexAtrName(vertexView.getCategory(name)));
//                selectView.setDescription(key, vertexView.getDescription(name));
//                selectView.setEditable(key, vertexView.isEditable(name));
////                selectView.setIndex(key, vertexView.getIndex(name));
////                maxIndex = Math.max(maxIndex, vertexView.getIndex(name));
//            }
////            selectView.setIndex("Edges", maxIndex + 1);
//            if (key.startsWith("e")) {
//                selectView.setVisible(key, edgeView.isVisible(name));
////                selectView.setDisplayName(key, name);
////                selectView.setCategory(key, vertexAtrName(edgeView.getCategory(name)));
//                selectView.setDescription(key, edgeView.getDescription(name));
//                selectView.setEditable(key, edgeView.isEditable(name));
////                selectView.setIndex(key, maxIndex + edgeView.getIndex(name));
//            }
//        }
//    }

    /**
     * suggests a name for the given vertex attribute to be used in selection attributed
     */
    private String vertexAtrName(String name) {
        return "v" + name;
    }

    /**
     * suggests a name for the given edge attribute to be used in selection attributed
     */
    private String edgeAtrName(String name) {
        return "e" + name;
    }


    private TimeLimitedNotifiableAttrSet getTarget() {
        return target;
    }

    public void track(){}
    
    private void setTarget(TimeLimitedNotifiableAttrSet target) {
        if (this.target != null) {
            this.target.stop();
        }
        this.target = target;
        this.target.stop();
    }
}