/*
 * Copyright (c) 2003, The JUNG Authors
 * All rights reserved.
 * 
 * This software is open-source under the BSD license; see either "license.txt"
 * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
 * 
 */
package edu.uci.ics.jung.samples;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.swing.BorderFactory;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JRadioButton;

import com.google.common.base.Supplier;

import edu.uci.ics.jung.algorithms.layout.PolarPoint;
import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout;
import edu.uci.ics.jung.algorithms.layout.TreeLayout;
import edu.uci.ics.jung.graph.DelegateForest;
import edu.uci.ics.jung.graph.DelegateTree;
import edu.uci.ics.jung.graph.DirectedGraph;
import edu.uci.ics.jung.graph.DirectedSparseGraph;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.Tree;
import edu.uci.ics.jung.visualization.DefaultVisualizationModel;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.VisualizationModel;
import edu.uci.ics.jung.visualization.VisualizationServer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalLensGraphMouse;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer;
import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.picking.PickedState;
import edu.uci.ics.jung.visualization.transform.LensSupport;
import edu.uci.ics.jung.visualization.transform.shape.HyperbolicShapeTransformer;
import edu.uci.ics.jung.visualization.transform.shape.ViewLensSupport;

/**
 * Shows a RadialTreeLayout view of a Forest.
 * A hyperbolic projection lens may also be applied
 * to the view
 * 
 * @author Tom Nelson
 * 
 */
@SuppressWarnings("serial")
public class RadialTreeLensDemo extends JApplet {
	
	Forest<String,Integer> graph;

	Supplier<DirectedGraph<String,Integer>> graphFactory = 
		new Supplier<DirectedGraph<String,Integer>>() {

		public DirectedGraph<String, Integer> get() {
			return new DirectedSparseGraph<String,Integer>();
		}
	};

	Supplier<Tree<String,Integer>> treeFactory =
		new Supplier<Tree<String,Integer>> () {

		public Tree<String, Integer> get() {
			return new DelegateTree<String,Integer>(graphFactory);
		}
	};
	Supplier<Integer> edgeFactory = new Supplier<Integer>() {
		int i=0;
		public Integer get() {
			return i++;
		}
	};

	Supplier<String> vertexFactory = new Supplier<String>() {
		int i=0;
		public String get() {
			return "V"+i++;
		}
	};

	VisualizationServer.Paintable rings;

	String root;

	TreeLayout<String,Integer> layout;

	RadialTreeLayout<String,Integer> radialLayout;

	/**
	 * the visual component and renderer for the graph
	 */
	VisualizationViewer<String,Integer> vv;

    /**
     * provides a Hyperbolic lens for the view
     */
    LensSupport hyperbolicViewSupport;
    
    ScalingControl scaler;
    
    /**
     * create an instance of a simple graph with controls to
     * demo the zoomand hyperbolic features.
     * 
     */
    public RadialTreeLensDemo() {
        
        // create a simple graph for the demo
        // create a simple graph for the demo
        graph = new DelegateForest<String,Integer>();

        createTree();
        
        layout = new TreeLayout<String,Integer>(graph);
        radialLayout = new RadialTreeLayout<String,Integer>(graph);
        radialLayout.setSize(new Dimension(600,600));

        Dimension preferredSize = new Dimension(600,600);
        
        final VisualizationModel<String,Integer> visualizationModel = 
            new DefaultVisualizationModel<String,Integer>(radialLayout, preferredSize);
        vv =  new VisualizationViewer<String,Integer>(visualizationModel, preferredSize);

        PickedState<String> ps = vv.getPickedVertexState();
        PickedState<Integer> pes = vv.getPickedEdgeState();
        vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer<String>(ps, Color.red, Color.yellow));
        vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<Integer>(pes, Color.black, Color.cyan));
        vv.setBackground(Color.white);
        
        vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
        vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph));
        
        // add a listener for ToolTips
        vv.setVertexToolTipTransformer(new ToStringLabeller());
        
        Container content = getContentPane();
        GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv);
        content.add(gzsp);
        
        final DefaultModalGraphMouse<String,Integer> graphMouse
        	= new DefaultModalGraphMouse<String,Integer>();

        vv.setGraphMouse(graphMouse);
        vv.addKeyListener(graphMouse.getModeKeyListener());
        rings = new Rings();
		vv.addPreRenderPaintable(rings);

        hyperbolicViewSupport = 
            new ViewLensSupport<String,Integer>(vv, new HyperbolicShapeTransformer(vv, 
            		vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), 
                    new ModalLensGraphMouse());
        
        final ScalingControl scaler = new CrossoverScalingControl();

        JButton plus = new JButton("+");
        plus.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                scaler.scale(vv, 1.1f, vv.getCenter());
            }
        });
        JButton minus = new JButton("-");
        minus.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                scaler.scale(vv, 1/1.1f, vv.getCenter());
            }
        });
        
        final JRadioButton hyperView = new JRadioButton("Hyperbolic View");
        hyperView.addItemListener(new ItemListener(){
            public void itemStateChanged(ItemEvent e) {
                hyperbolicViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
            }
        });

        graphMouse.addItemListener(hyperbolicViewSupport.getGraphMouse().getModeListener());
        
        JMenuBar menubar = new JMenuBar();
        menubar.add(graphMouse.getModeMenu());
        gzsp.setCorner(menubar);

        JPanel controls = new JPanel();
        JPanel zoomControls = new JPanel(new GridLayout(2,1));
        zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom"));
        JPanel hyperControls = new JPanel(new GridLayout(3,2));
        hyperControls.setBorder(BorderFactory.createTitledBorder("Examiner Lens"));
        zoomControls.add(plus);
        zoomControls.add(minus);
        JPanel modeControls = new JPanel(new BorderLayout());
        modeControls.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
        modeControls.add(graphMouse.getModeComboBox());
        hyperControls.add(hyperView);
        
        controls.add(zoomControls);
        controls.add(hyperControls);
        controls.add(modeControls);
        content.add(controls, BorderLayout.SOUTH);
    }

    private void createTree() {
    	graph.addVertex("V0");
    	graph.addEdge(edgeFactory.get(), "V0", "V1");
    	graph.addEdge(edgeFactory.get(), "V0", "V2");
    	graph.addEdge(edgeFactory.get(), "V1", "V4");
    	graph.addEdge(edgeFactory.get(), "V2", "V3");
    	graph.addEdge(edgeFactory.get(), "V2", "V5");
    	graph.addEdge(edgeFactory.get(), "V4", "V6");
    	graph.addEdge(edgeFactory.get(), "V4", "V7");
    	graph.addEdge(edgeFactory.get(), "V3", "V8");
    	graph.addEdge(edgeFactory.get(), "V6", "V9");
    	graph.addEdge(edgeFactory.get(), "V4", "V10");
    	
       	graph.addVertex("A0");
       	graph.addEdge(edgeFactory.get(), "A0", "A1");
       	graph.addEdge(edgeFactory.get(), "A0", "A2");
       	graph.addEdge(edgeFactory.get(), "A0", "A3");
       	
       	graph.addVertex("B0");
    	graph.addEdge(edgeFactory.get(), "B0", "B1");
    	graph.addEdge(edgeFactory.get(), "B0", "B2");
    	graph.addEdge(edgeFactory.get(), "B1", "B4");
    	graph.addEdge(edgeFactory.get(), "B2", "B3");
    	graph.addEdge(edgeFactory.get(), "B2", "B5");
    	graph.addEdge(edgeFactory.get(), "B4", "B6");
    	graph.addEdge(edgeFactory.get(), "B4", "B7");
    	graph.addEdge(edgeFactory.get(), "B3", "B8");
    	graph.addEdge(edgeFactory.get(), "B6", "B9");
       	
    }

    class Rings implements VisualizationServer.Paintable {
    	
    	Collection<Double> depths;
    	
    	public Rings() {
    		depths = getDepths();
    	}
    	
    	private Collection<Double> getDepths() {
    		Set<Double> depths = new HashSet<Double>();
    		Map<String,PolarPoint> polarLocations = radialLayout.getPolarLocations();
    		for(String v : graph.getVertices()) {
    			PolarPoint pp = polarLocations.get(v);
    			depths.add(pp.getRadius());
    		}
    		return depths;
    	}

		public void paint(Graphics g) {
			g.setColor(Color.gray);
			Graphics2D g2d = (Graphics2D)g;
			Point2D center = radialLayout.getCenter();

			Ellipse2D ellipse = new Ellipse2D.Double();
			for(double d : depths) {
				ellipse.setFrameFromDiagonal(center.getX()-d, center.getY()-d, 
						center.getX()+d, center.getY()+d);
				Shape shape = 
					vv.getRenderContext().getMultiLayerTransformer().transform(ellipse);
				g2d.draw(shape);
			}
		}

		public boolean useTransform() {
			return true;
		}
    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new RadialTreeLensDemo());
        f.pack();
        f.setVisible(true);
    }
}
