/**
 * This file is part of CloudML [ http://cloudml.org ]
 *
 * Copyright (C) 2012 - SINTEF ICT
 * Contact: Franck Chauvel <franck.chauvel@sintef.no>
 *
 * Module: root
 *
 * CloudML is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * CloudML is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with CloudML. If not, see
 * <http://www.gnu.org/licenses/>.
 */
/**
 * This file is part of CloudML [ http://cloudml.org ]
 * 
* Copyright (C) 2012 - SINTEF ICT Contact: Franck Chauvel
 * <franck.chauvel@sintef.no>
 *
 * Module: root
 * 
* CloudML is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
* CloudML is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
* You should have received a copy of the GNU Lesser General Public License
 * along with CloudML. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package org.cloudml.ui.graph;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.util.Iterator;
import java.util.Random;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.cloudml.codecs.Edge;
import org.cloudml.codecs.Vertex;
import org.cloudml.core.*;
import org.cloudml.core.VM;

import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;

public class MyEditingGraphMousePlugin extends AbstractGraphMousePlugin implements
        MouseListener, MouseMotionListener {

    private VisualizationViewer<Vertex, Edge> vv;
    private Graph<Vertex, Edge> graph;
    private JList nodeTypes;
    private Deployment dm;

    public MyEditingGraphMousePlugin(int modifiers, VisualizationViewer<Vertex, Edge> vv, Graph<Vertex, Edge> graph, JList nodeTypes, Deployment dm) {
        super(modifiers);
        this.vv = vv;
        this.graph = graph;
        this.nodeTypes = nodeTypes;
        this.dm = dm;
    }

    public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
    }

    public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
    }

    public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
    }

    public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
    }

    public void mouseReleased(MouseEvent e) {
        final Point2D p = e.getPoint();
        GraphElementAccessor<Vertex, Edge> pickSupport = vv.getPickSupport();
        if (e.getButton() == e.BUTTON1) {
            if (pickSupport != null && nodeTypes.getSelectedIndex() != -1) {
                String nodeType = (String) nodeTypes.getSelectedValue();
                Random r = new Random();
                int cnt = r.nextInt();
                final Vertex vertex = pickSupport.getVertex(vv.getModel().getGraphLayout(), p.getX(), p.getY());
//Not on a vertex
                if (vertex == null) {
                    for (Component a : dm.getComponents()) {
                        if (a instanceof InternalComponent) {
                            InternalComponent ic = (InternalComponent) a;
                            if (a.getName().equals(nodeType)) {
                                InternalComponentInstance ai = ic.instantiate(nodeType + cnt);
                                //ai.setRequiredExecutionPlatformInstance(selectDestination());
                                dm.getComponentInstances().add(ai);
//                                for (RequiredPort c : ic.getRequiredPorts()) {
//                                    ai.getRequiredPortInstances().add(new RequiredPortInstance(c.getName() + cnt, c, ai));
//                                }
//                                for (ProvidedPort s : ic.getProvidedPorts()) {
//                                    ai.getProvidedPortInstances().add(new ProvidedPortInstance(s.getName() + cnt, s, ai));
//                                }
                                Vertex v = new Vertex(nodeType + cnt, "soft", ai);
                                graph.addVertex(v);
                                vv.getModel().getGraphLayout().setLocation(v, vv.getRenderContext().getMultiLayerTransformer().inverseTransform(e.getPoint()));
                                Edge newEdge = new Edge(ai.getRequiredExecutionPlatform().getName() + cnt, "destination");
                                Vertex dest = null;
                                for (Vertex vDest : graph.getVertices()) {
                                    if (vDest.getName().equals(ai.getRequiredExecutionPlatform().getName())) {
                                        graph.addEdge(newEdge, v, vDest);
                                        break;
                                    }
                                }
                            }
                        }//TODO Else
                    }
                    for (ExternalComponent a : dm.getComponents().onlyExternals()) {
                        if (a.getName().equals(nodeType) && a instanceof ExternalComponent) {
                            VMInstance ai = ((VM) a).instantiates(nodeType + cnt);
                            dm.getComponentInstances().add(ai);
                            Vertex v = new Vertex(nodeType + cnt, "node", ai);
                            graph.addVertex(v);
                            vv.getModel().getGraphLayout().setLocation(v, vv.getRenderContext().getMultiLayerTransformer().inverseTransform(e.getPoint()));
                        }
                    }
                    for (Relationship b : dm.getRelationships()) {
                        if (b.getName().equals(nodeType)) {
                            RelationshipInstance bi = b.instantiates(nodeType + cnt);
                            dm.getRelationshipInstances().add(bi);
                            Edge edge;
                            if (b.getRequiredEnd().isOptional()) {
                                edge = new Edge(nodeType + cnt, "optional");
                            }
                            else {
                                edge = new Edge(nodeType + cnt, "mandatory");
                            }

                            String client = selectClientPortInstance(bi);
                            String server = selectServerPortInstance(bi);

                            Vertex v1 = null, v2 = null;
                            if (!client.equals("")) {
                                for (Vertex v : graph.getVertices()) {
                                    if (v.getName().equals(client)) {
                                        v1 = v;
                                    }
                                }
                            }
                            if (!server.equals("")) {
                                for (Vertex v : graph.getVertices()) {
                                    if (v.getName().equals(server)) {
                                        v2 = v;
                                    }
                                }
                            }
                            if (v1 != null && v2 != null) {
                                graph.addEdge(edge, v1, v2);
                            }
                        }
                    }
                }
            }
        }
        else {
            if (pickSupport != null) {
                final Vertex vertex = pickSupport.getVertex(vv.getModel().getGraphLayout(), p.getX(), p.getY());
                final Edge edge = pickSupport.getEdge(vv.getModel().getGraphLayout(), p.getX(), p.getY());
                if (vertex != null) {
                    if (vertex.getType().equals("node")) {
//need to remove also the bindings and the requirements !!!
                        dm.getComponentInstances().remove(vertex);
                        graph.removeVertex(vertex);
                    }
                    else { 
                        dm.getComponentInstances().remove(vertex);
                        graph.removeVertex(vertex);
                    }
                }
                else {
                    dm.getRelationshipInstances().remove(edge);
                    graph.removeEdge(edge);
                }
            }
        }
        nodeTypes.clearSelection();
        vv.repaint();
    }

    public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
    }

    public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
    }

    public String selectClientPortInstance(RelationshipInstance bi) {
        JPanel panel = new JPanel();
        panel.add(new JLabel("Please make a selection:"));
        DefaultComboBoxModel model = new DefaultComboBoxModel();
        for (ComponentInstance ai : dm.getComponentInstances()) {
            if (ai instanceof InternalComponentInstance) {
                for (RequiredPortInstance ci : ((InternalComponentInstance) ai).getRequiredPorts()) {
                    System.out.println(bi.getType().getRequiredEnd() + " #### " + ci.getType());
                    if (ci.getType().equals(bi.getType().getRequiredEnd())) {
                        model.addElement(ci);
                    }
                }
            }
        }
        JComboBox comboBox = new JComboBox(model);
        panel.add(comboBox);

        int result = JOptionPane.showConfirmDialog(null, panel, "RequiredPort", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
        switch (result) {
            case JOptionPane.OK_OPTION:
                bi.setRequiredEnd((RequiredPortInstance) comboBox.getSelectedItem());
                return ((RequiredPortInstance) comboBox.getSelectedItem()).getOwner().getName();
        }
        return "";
    }

    public VMInstance selectDestination() {
        JPanel panel = new JPanel();
        panel.add(new JLabel("Please make a selection:"));
        DefaultComboBoxModel model = new DefaultComboBoxModel();
        for (ExternalComponentInstance n : dm.getComponentInstances().onlyExternals()) {
            model.addElement(n);
        }
        JComboBox comboBox = new JComboBox(model);
        panel.add(comboBox);

        int result = JOptionPane.showConfirmDialog(null, panel, "Destination", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
        switch (result) {
            case JOptionPane.OK_OPTION:
                return ((VMInstance) comboBox.getSelectedItem());
        }
        return null;
    }

    public String selectServerPortInstance(RelationshipInstance bi) {
        JPanel panel = new JPanel();
        panel.add(new JLabel("Please make a selection:"));
        DefaultComboBoxModel model = new DefaultComboBoxModel();
        for (ComponentInstance ai : dm.getComponentInstances()) {
            Iterator<ProvidedPortInstance> it = ai.getProvidedPorts().iterator();
            while (it.hasNext()) {//TODO: check issue with foreach on list<ProvidedPortInstances>
                ProvidedPortInstance ci = it.next();
                if (ci.getType().equals(bi.getType().getProvidedEnd())) {
                    model.addElement(ci);
                }
            }
        }
        JComboBox comboBox = new JComboBox(model);
        panel.add(comboBox);

        int result = JOptionPane.showConfirmDialog(null, panel, "ProvidedPort", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
        switch (result) {
            case JOptionPane.OK_OPTION:
                bi.setProvidedEnd((ProvidedPortInstance) comboBox.getSelectedItem());
                return ((ProvidedPortInstance) comboBox.getSelectedItem()).getOwner().getName();
        }
        return "";
    }
}