package demoPlugin;

import king.*;
import king.core.*;
import driftwood.gui.ReflectiveAction;

import java.util.*;
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class DemoPlugin extends Plugin implements ActionListener, WindowListener
// This class creates a basic plugin for KiNG.  This plugin generates random data, and asks KiNG 
//    asks KiNG to display it.  This plugin illustrates how to write a plugin for KiNG.
	{
	
		// The following are most of the allowed colours.  This array is not used in this plugin.  
		//    I had created it for a different plugin so that I could rotate through the colours, and 
		//    left it here in case anyone else would find it useful.
		// Colours not included here include white, black, gray, invisible, deadwhite, and deadblack.
		//    See king.core.KPalette for the full list of colours.
	static final String[] colours = {"red", "orange", "gold", "yellow", "lime", "green", "sea", 
			"cyan", "sky", "blue", "purple", "magenta", "hotpink", "pink", "lilac", "peach", 
			"peachtint", "yellowtint", "greentint", "bluetint", "lilactint", "pinktint", "brown"};
	
	DemoFrame frame;	// window that has controls for this plugin
	Kinemage demokin;	// the kinemage which will store our data & be sent to KiNG
	KGroup lineGroup = null, ptGroup = null;	// copies of the groups containing the polylines and balls
	
	
	public DemoPlugin(ToolBox tb)
		{
		super(tb);	// call the Plugin constructor
		
		// put your constructor stuff here.
		}	// end constructor
	
	
	public JMenuItem getToolsMenuItem()
	// Sets up the call-back, so KiNG knows what to put in the menu, and what method to call
	//	when this plugin is selected.
		{
		return new JMenuItem(new ReflectiveAction("Demo Plugin...", null, this, "callDemoPlugin"));
		}	// end method getToolsMenuItem()
	
	
	public void callDemoPlugin(ActionEvent ev)
	// Opens up the Demo Plugin window when the Demo Plugin menu item is selected.
	// This method is the target of reflection... if you change the name, you need to change the 
	//   name in getToolsMenuItem()
		{
		if (frame == null) frame = new DemoFrame("Demo Plugin", this);
		
		frame.setVisible(true);
		frame.toFront();
		}	// end method callDemoPlugin(ActionEvent)
	
	
		/******************** BEGIN ActionListener methods ********************/
	
	public void actionPerformed(ActionEvent e)
	// This method listens for commands from the DemoFrame.  (I.e. the button being clicked.)
	// Alternately, you can have the DemoFrame, or some other class listen for commands.
		{
		String command = e.getActionCommand();
		
		if (command.equals("demo")) drawDemo();
		}	// end method actionPerformed(ActionEvent)
	
		/******************** END ActionListener methods ********************/
	
	
		/******************** BEGIN WindowListener methods ********************/
	
	public void windowOpened(WindowEvent e)
	// Invoked the first time a window is made visible.
		{
		demokin = new Kinemage("Demo Kinemage");
		kMain.getStable().append(Arrays.asList(new Kinemage[] {demokin}));
		}	// end method
	
	public void windowClosing(WindowEvent e)
	// Invoked when the user attempts to close the window from the window's system menu.
		{
		frame.hide();
		frame.dispose();
		frame = null;
		}
	
	public void windowClosed(WindowEvent e) {}	// Invoked when a window has been closed as the result  of calling dispose on the window.
	public void windowIconified(WindowEvent e) {}
	public void windowDeiconified(WindowEvent e) {}
	public void windowActivated(WindowEvent e) {}
	public void windowDeactivated(WindowEvent e) {}
	
		/******************** END WindowListener methods ********************/
	
	public void drawDemo()
		{
		drawSomeLines();
		drawSomeBalls();
		
		// Note:  there are datatypes other than balls and vectors you can use.  See king.core for more types.
		
		recenter(demokin);	// recenter the kinemage view. 
							// Note:  recentering is optional, but should be called if kinemage was empty previously.
		notifyKinChange();	// notify KiNG that the kinemage has changed and should be redrawn.
		}	// end method drawDemo()
	
	
	final void notifyKinChange() { kMain.notifyChange(KingMain.EM_EDIT_GROSS); }
	final void notifyKinChange(int type) { kMain.notifyChange(type); }
	
	
	public void drawSomeLines()
	// generate some random lines.
		{
		KGroup group = new KGroup(demokin, "Lines Group");	// create the group
		KSubgroup subgroup = new KSubgroup(group, "Lines Subgroup");	// create the subgroup
		subgroup.setHasButton(true);	// whether the subgroup has a button displayed
		group.add(subgroup);	// must call to add the subgroup to the group (previously only set parent)
		KList list = createKList(subgroup, KList.VECTOR, "Line List", true, "green", "list master");
		VectorPoint a;
		
		int npolylines = (int)(Math.random() * 10.0);
		for (int i = 0; i < npolylines; i++)
			{
			int nseg = (int)(Math.random() * 10.0) + 1;
			a = createPoint((float)(Math.random() * 100.0), (float)(Math.random() * 100.0), 
					(float)(Math.random() * 100.0), "l:"+i+", p:0", list, null);
			for (int j = 1; j <= nseg; j++)
				a = createPoint((float)(Math.random() * 100.0), (float)(Math.random() * 100.0), 
						(float)(Math.random() * 100.0), "l:"+i+", p:"+j, list, a);
			}	// end for
		
		if (lineGroup == null)	// no preexisting group, add new group to kinemage
			{
			demokin.add(group);
			recenter(demokin);
			}
		else demokin.replace(lineGroup, group);	// replace old group with new
		lineGroup = group;	// remember group
		}	// end method drawSomeLines()
	
	
	public void drawSomeBalls()
	// generate some random balls.  This method is basically the same as drawSomeLines.
		{
		KGroup group = new KGroup(demokin, "Balls Group");
		KSubgroup subgroup = new KSubgroup(group, "Balls Subgroup");
		subgroup.setHasButton(true);
		group.add(subgroup);
		KList list = createKList(subgroup, KList.BALL, "Ball List", true, "red", "list master");
		list.setRadius(1);
		BallPoint point;
		
		int npts = (int)(Math.random() * 10.0);
		for (int j = 0; j < npts; j++)
			{
			point = new BallPoint(list, "b"+j);
			point.setOrigX((float)Math.random() * 100.0);
			point.setOrigY((float)Math.random() * 100.0);
			point.setOrigZ((float)Math.random() * 100.0);
			list.add(point);
			}	// end for
		
		if (ptGroup == null)
			{
			demokin.add(group);
			recenter(demokin);
			}
		else demokin.replace(ptGroup, group);
		ptGroup = group;
		}	// end method drawSomeBalls()
	
	
	private KList createKList(KSubgroup subgroup, String type, String name, boolean hasbutton, String colour, String masterName)
	// This method performs the usual tasks associated with creating a KList.
		{
		KList list = new KList(subgroup, name);
		list.setType(type);
		list.setHasButton(hasbutton);
		list.setColor(demokin.getPaintForName(colour));
		if (masterName!= null) list.addMaster(masterName);
		subgroup.add(list);
		return list;
		}	// end method createKList(KSubGroup, String, boolean, String, String)
	
	
	private VectorPoint createPoint(float x, float y, float z, String id, KList list, VectorPoint prev)
	// This method creates and initializes a VectorPoint.
		{
		VectorPoint a = new VectorPoint(list, id, prev);
		a.setOrigX(x);
		a.setOrigY(y);
		a.setOrigZ(z);
		list.add(a);
		return a;
		}	// end method createPoint()
	
	
	private void recenter(Kinemage kin)
	// recenters the view, and sets the default zoom to 1.
		{
		if (kin == null) return;
		
		kin.calcSize();
		float[] center = kin.getCenter();
		kin.getCurrentView().setCenter(center[0], center[1], center[2]);	// set the center of the view
		kin.getCurrentView().setZoom(1);		// set the zoom
		notifyKinChange(KingMain.EM_NEWVIEW);	// notify KiNG that the view has changed
		}	// end method recenter(Kinemage)
	
	
	public URL getHelpURL()
	// This method is called by KiNG to determine where the help file is.  The help file should 
	//    be in html format, and located in the king/<project>/resource/<package>/ directory.  For example, 
	//    the help file for this plugin is located in the king/demoPlugin/resource/demoPlugin directory, 
	//    and is called demoPlugin_manual.html.  If the help file is contained in a further subdirectory, 
	//    the correct subdirectory should be added to the getResource call below.
		{
		URL url = getClass().getResource("demoPlugin_manual.html");
		String anchor = getHelpAnchor();
		if (url != null && anchor != null)
			{
			try { url = new URL(url, anchor); }
			catch(MalformedURLException e) { e.printStackTrace(System.err); }
			return url;
			}
		else return url;
		}	// end method getHelpURL()
	
		// The following method specifies which anchor the help call should link to in the document
		//   specified by getHelpURL.
	public String getHelpAnchor() { return "#top"; }
	
		// This string will be displayed in the Help menu.
	public String toString() { return "Demo Plugin"; }	// called by getHelpMenuItem
	
	}	// end class DemoPlugin