/*
 * TopologyGUI.java
 *
 * Created on October 24, 2007, 6:22 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package sidnet.core.gui;

import sidnet.core.misc.NCS_Location2D;
import sidnet.core.misc.Node;
import sidnet.core.misc.LocationContext;
import sidnet.core.gui.*;
import sidnet.core.gui.Arrow;
import java.awt.Color;
import java.awt.Graphics;
import java.util.LinkedList;
import javax.swing.JPanel;
import sidnet.core.misc.*;
import sidnet.core.interfaces.SIDnetDrawableInterface;
import sidnet.core.interfaces.SIDnetMenuInterface;
import sidnet.core.interfaces.SIDnetRegistrable;
import java.awt.event.ActionEvent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

/**
 *
 * @author Oliver
 *
 * Helper class, designed to integrate with SIDnet simulator, maintains and draws a list of links (lines) on the screen
 * Can be used to maintain a visual representation of a topology (routing structure, for example)
 *
 */
public final class TopologyGUI extends JPanel implements SIDnetDrawableInterface, SIDnetMenuInterface, SIDnetRegistrable{
    private Node[] nodeList = null;
    private LinkedList<TopologyGroup> topologyGroupList;
    
    /** Since this is a GUI, it is important to know the dimension information of the area in which we draw to be able to convert location information from NCS */
    private LocationContext locationContext;
     
    /* Toggle graphics on/off */
    private boolean show = true;
    
    /* Menu */
    private JMenuItem menuItemShowPhysics;
    
    /**
     * Creates a new instance of TopologyGUI
     */
    public TopologyGUI() {
        this.locationContext = locationContext;   
        topologyGroupList = new LinkedList<TopologyGroup>();
        
        // add the default group
        topologyGroupList.add(new TopologyGroup(-1, Color.BLACK));
    }
    
    public void setNodeList(Node[] nodeList)
    {
        this.nodeList = nodeList;
    }
    
    /** 
     * Adds a link to the topology viewer
     * <p>
     * @param fromNodeWithID     the id (NOT IP) of the node from which to draw the link (arrow)
     * @param toNodeWithID       the id (NOT IP) of the node to which do draw the link (tip of the arrow)
     * @param color         the color of the link. It may be use as a mean of identification if two arrows share the same locations
     */
    public void addLink(int fromNodeWithID, int toNodeWithID, int groupId, Color groupColor)
    {
        addLink(nodeList[fromNodeWithID].getNCS_Location2D(), nodeList[toNodeWithID].getNCS_Location2D(), groupId, groupColor);
    }
    
    public void addLink(NCS_Location2D fromPoint, NCS_Location2D toPoint, int groupId, Color groupColor)
    {
        TopologyGroup foundTopologyGroup = getTopologyGroup(groupId);
        
        // If group already defined
        if (foundTopologyGroup != null)
            foundTopologyGroup.addLink(fromPoint, toPoint);
        else
        {
            TopologyGroup newTopologyGroup = new TopologyGroup(groupId, groupColor);
            newTopologyGroup.addLink(fromPoint, toPoint);
            topologyGroupList.add(newTopologyGroup);
        }
    }
    
    public void removeLink(NCS_Location2D fromPoint, NCS_Location2D toPoint, int groupId)
    {
         TopologyGroup foundTopologyGroup = getTopologyGroup(groupId);
         
         foundTopologyGroup.removeLink(fromPoint, toPoint);
    }
    
     public void removeLink(int fromNodeWithID, int toNodeWithID, int groupId)
     {
         removeLink(nodeList[fromNodeWithID].getNCS_Location2D(), nodeList[toNodeWithID].getNCS_Location2D(), groupId);
     }
    
    private TopologyGroup getTopologyGroup(int groupId)
    {
        // Retrieve the group specified by groupId, if any
        for (TopologyGroup topologyGroup: topologyGroupList)
            if (topologyGroup.getGroupId() == groupId)
               return topologyGroup;
        
        return null;
    }
    
    
    /* ********************** *   
     * SIDnetDrawableInterface *
     * ********************** */

    /** Gives the panel handle on which the data will be plot */
    public void configureGUI(JPanel hostPanel)
    {
        if (hostPanel == null)
             return;
         
        this.setOpaque(false);
        this.setBackground(Color.black);
        hostPanel.setLocation(0,0);
        hostPanel.add(this);

        this.setBounds(hostPanel.getBounds());

        this.setSize(hostPanel.getSize());

        /* We choose to enable this by default */
        this.setVisible(false); 

        locationContext = new LocationContext(hostPanel.getWidth(), hostPanel.getHeight());
    }
    
    /** Force the screen to redraw */
    public void repaintGUI() { /*if (show) repaint();*/}
    
    /** Turns the display on/off */
    public void setVisibleGUI(boolean visible)
    {
        if (show)
            this.setVisible(visible);
    }
    
    /* ****************** *
     * SIDnetMenuInterface *
     * ****************** */
    
    public void configureMenu(JPopupMenu hostPopupMenu)
    {
        menuItemShowPhysics = new JMenuItem("Show/Hide Topology Visualization");
        menuItemShowPhysics.addActionListener(this);
        hostPopupMenu.add(menuItemShowPhysics);
    }
    
    public void disableUI() { /* do nothing */ }
    
    public void enableUI() { /* do nothing */ }
    
    public void passMenuActionEvent(ActionEvent e) { /* do nothing */ }
       
    public void actionPerformed(ActionEvent e)
    {
         /* Toggle this visual tool graphics on/off */
         if (e.getActionCommand() == "Show/Hide TreeVisualization")
         {
             System.out.println("SHOW");
             show = !show;
             this.setVisible(show);
             
             
         }
    }
    
    /* ************************* *
     * SIDnetRegistrableInterface *
     * ************************* */
    public void terminate() { /* do nothing */ }
    
   
    
     public synchronized void paint(Graphics g) {
       // super.paint(g);
        for (TopologyGroup topologyGroup:topologyGroupList)
            topologyGroup.repaint(g);
    }
     
    protected void clear(Graphics g) {
       // super.paintComponent(g);
    }  
    
    private class TopologyGroup
    {
        private int groupId;
        private Color groupColor;
        private LinkedList<Arrow> arrowSet = null;   /** Links are represented as a set of Arrows */
        private Graphics g = null;
        
        public TopologyGroup(int groupId)
        {
            this.groupId = groupId;
            arrowSet = new LinkedList<Arrow>();
        }
                
        public TopologyGroup(int groupId, Color groupColor)
        {
            this.groupId = groupId;
            arrowSet = new LinkedList<Arrow>();
            if (groupColor != null)
                this.groupColor = groupColor;
            else
            {
                // randomly generate a RGB color
                int r = ((0xC0 & groupId) >> 6) * 64;
               	int g = ((0x38 & groupId) >> 3) * 32;
              	int b = (0x7   & groupId) * 32;

                this.groupColor = new Color(r, g, b);
            }
        }
        
        public int getGroupId()
        {
            return groupId;
        }

        public void setGroupColor(Color groupColor)
        {
            this.groupColor = groupColor;
        }       
        
        public synchronized void repaint(Graphics g)
        {  
            this.g = g;
            if (arrowSet != null)
                for(Arrow arrow: arrowSet)
                     ArrowDrawingTool.drawArrow(g, arrow);
        }
        
         /** 
         * Adds a link to the topology viewer
         * <p>
         * @param fromPoint     the origin of the link given as NCS
         * @param toPoint       the end-point of the link as NCS
         * @param color         the color of the link. It may be use as a mean of identification if two arrows share the same locations
         */
        public synchronized void addLink(NCS_Location2D fromPoint, NCS_Location2D toPoint)
        {
            /* if an arrow with same location parameters and same color exists already, we do nothing and return */
            if (containsLink(fromPoint, toPoint))
                return;

            arrowSet.add(new Arrow(fromPoint.fromNCS(locationContext), toPoint.fromNCS(locationContext), groupColor));       
            
            if (g != null)
                repaint(g);
        }

       
        
        /** 
         * Test the existence of a link with the same location and color
         * <p>
         * @param fromPoint     the origin of the link given as NCS
         * @param toPoint       the end-point of the link as NCS
         * @param color         the color (seen as identification to distinguish between arrows that share the same locations)
         * @return              TRUE if found; FALSE otherwise
         * 
         */
        public synchronized boolean containsLink(NCS_Location2D fromPoint, NCS_Location2D toPoint)
        {
            for (Arrow arrow: arrowSet)
                if (arrow.getFromPoint() == fromPoint.fromNCS(locationContext) &&
                    arrow.getToPoint() == toPoint.fromNCS(locationContext))
                        return true;

            return false;
        }
        
        /** 
         * Remove a link from the topological view
         * <p>
         * @param fromPoint     the origin of the link given as NCS
         * @param toPoint       the end-point of the link as NCS
         * @param color         the color (seen as identification to distinguish between arrows that share the same locations)
         * 
         */
        public synchronized void removeLink(NCS_Location2D fromPoint, NCS_Location2D toPoint)
        {
            for (Arrow arrow: arrowSet)
            {
                if (arrow.getFromPoint() == fromPoint.fromNCS(locationContext) &&
                    arrow.getToPoint() == toPoint.fromNCS(locationContext))
                    {
                        arrowSet.remove(arrow);
                        break;
                    } 
            }
            if (g != null)
                repaint(g);
        }

       
    }
    
}
