// 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.edge;

import graphtea.graph.GraphUtils;
import graphtea.graph.atributeset.GraphAttrSet;
import graphtea.graph.event.GraphEvent;
import graphtea.graph.event.VertexEvent;
import graphtea.graph.graph.*;
import graphtea.graph.old.AcceleratedRenderer;
import graphtea.graph.old.GStroke;
import graphtea.library.util.Pair;
import graphtea.platform.core.AbstractAction;
import graphtea.platform.core.BlackBoard;
import graphtea.plugins.main.select.Select;
import graphtea.plugins.main.select.SelectPluginMethods;

import java.awt.*;
import java.awt.event.MouseEvent;

/**
 * @author azin azadi
 */

public class AddEdge extends AbstractAction implements PaintHandler {
    //    private double lasty;
    //    private double lastx;
    private GPoint lastPos;
    private final SelectPluginMethods selMethods;

    /**
     * constructor
     *
     * @param bb the blackboard of the action
     */
    public AddEdge(BlackBoard bb) {
        super(bb);
        selMethods = new SelectPluginMethods(bb);
        listen4Event(VertexEvent.EVENT_KEY);
//        listen4Event(GraphEvent.name);
//        listen4Event(VertexDropData.event);
//        listen4Event(VertexSelectData.name);
    }

    protected Vertex v1, vc1;
    protected GraphModel g;
    protected AbstractGraphRenderer gv;
    protected boolean isClick = false;
    protected boolean isDrag = false;

    /**
     * used for determining whether user has exited a vertex while dragging
     */
    boolean exitedFromV1;

    public void track(){}
    public void performAction(String eventName, Object value) {
        VertexEvent ve = blackboard.getData(VertexEvent.EVENT_KEY);

        if ((ve.modifiers & MouseEvent.SHIFT_DOWN_MASK) == MouseEvent.SHIFT_DOWN_MASK)
            return;

        SubGraph sd = Select.getSelection(blackboard);
        g = blackboard.getData(GraphAttrSet.name);
        gv = blackboard.getData(AbstractGraphRenderer.EVENT_KEY);


        if (eventName.equals(VertexEvent.EVENT_KEY)) {
            if (ve.eventType == VertexEvent.DRAGGING_STARTED) {
                if (!sd.vertices.contains(ve.v)) {   //start if the vertex is not selected
//            VertexDragData vdrag = blackboard.get(VertexDragData.name);
                    v1 = ve.v;
                    startPainting();
                    exitedFromV1 = false;
//                    if (!selMethods.getSelectedVertices().contains(v1)) {
//                        //the vertex doesn't selected
//                    } else
//                        v1 = null;
                }
            }
            if (ve.eventType == VertexEvent.DROPPED) {
                if (v1 != null) {
                    Vertex v2 = ve.v;
                    if (v2 != null && isDrag)   //!it was released on empty space
                        if (!v1.equals(v2) || exitedFromV1) {
                            doJob(g, v1, v2);
                            super.track();
                        }
//            unListenEvent(VertexMouseDraggingData.event);
                }
                stopPainting();
                v1 = null;
            }

            if (ve.eventType == VertexEvent.DRAGGING) {
                lastPos = ve.mousePos;
                GPoint absPosOnGraph = GraphUtils.createGraphPoint(g, x1, y1);
                absPosOnGraph.add(lastPos);
                if (g.getVerticesCount() < 300) {
                    Pair<Vertex, Double> p = GraphControl.mindistv(g, absPosOnGraph);
                    if (p.first != null)
                        curVertexUnderMouse = GraphControl.isPointOnVertex(g, p.first, absPosOnGraph) ? p.first : null;
                }
                if (!(gv instanceof AcceleratedRenderer))
                    gv.repaint();

                isDrag = true;
                isClick = false;

                if (v1 != null) {
                    if (curVertexUnderMouse == null)
                        exitedFromV1 = true;
                }
            }

            if (ve.eventType == VertexEvent.RELEASED) { //mouse released on a blank area of graph
                v1 = null;
                stopPainting();
            }
        } else if (eventName.equals(GraphEvent.EVENT_KEY)) {
//                if (ge.eventType == GraphEvent.) {
//                    stopPainting();
//                }
        }
    }

    protected void stopPainting() {
        gv.removePaintHandler(this);
//        g.view.repaint();
    }

    protected void startPainting() {
        GPoint location = v1.getLocation();
        Point viewPoint = GraphUtils.createViewPoint(g, location);
        cx = v1.getCenter().x / 2;
        x1 = viewPoint.x;
        cy = v1.getCenter().y / 2;
        y1 = viewPoint.y;
//        listen4Event(VertexMouseDraggingData.event);
        gv.addPostPaintHandler(this);
    }

    public static Edge doJob(GraphModel g, Vertex v1, Vertex v2) {
        Edge e = new Edge(v1, v2);
        g.insertEdge(e);
        return e;
    }

    protected int x1;
    protected int y1;
    protected int cx;
    protected int cy;

    protected Vertex curVertexUnderMouse = null;

    /**
    * paints incomplete edges for the user while he/she is adding the edge. (A preview)
    */
    public void paint(Graphics g, Object component, Boolean drawExtras) {
        if (!drawExtras)
            return;
        Graphics2D gg = (Graphics2D) g;
        gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Stroke stroke = gg.getStroke();
        gg.setStroke(GStroke.dashed.stroke);
        if (lastPos != null) {
            Point viewPoint = GraphUtils.createViewPoint(this.g, lastPos);
            gg.setColor(Color.DARK_GRAY);
            if (curVertexUnderMouse == null)
                gg.drawLine(x1, y1, x1 + viewPoint.x, y1 + viewPoint.y);
            else if (v1.equals(curVertexUnderMouse) && exitedFromV1) {
                Edge e = new Edge(v1, v1);
                double zf = this.g.getZoomFactor();
//                gg.setStroke(GStroke.strong.stroke);
                gg.drawOval(
                    ((int) (zf * (e.getLoopCenter().x - e.getLoopWidth()/2))),
                    ((int) (zf * (e.getLoopCenter().y - e.getLoopWidth()/2))),
                    ((int) (zf * (e.getLoopWidth()))),
                    ((int) (zf * (e.getLoopWidth()))));
            } 
            else {
                gg.setStroke(GStroke.strong.stroke);
                GPoint loc = curVertexUnderMouse.getLocation();
                viewPoint = GraphUtils.createViewPoint(this.g, loc);
                gg.drawLine(x1, y1, viewPoint.x, viewPoint.y);
            }
        }
        gg.setStroke(stroke);
    }
}
