//
// Visuzalization module for TETDM 
// DrawNodes.java Version 0.30
// Copyright(C) 2011 WATARU SUNAYAMA ALL RIGHTS RESERVED.
// This program is provided under the license.
// You should read the README file.
//


package module.VisualizationModules.DrawNodes;		// Replace ALL [VisualizationStyle] with your [module name]

import source.*;
import source.TextData.*;
//import source.Utility.*;


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

import java.lang.Runtime;


public class DrawNodes extends VisualizationModule implements ActionListener, MouseListener, MouseMotionListener, Runnable
{
	JPanel layer;
	JButton spring1,keyseg,keysen,segkey,senkey;
	
	
	//For objects operation
	int mousex, mousey;			//Coordinates for a mouse
	int old_mousex, old_mousey;	//Last Coordinates for a mosue
	int dx, dy;					//Distance for mouse moving
	
	int touch_num;				//Node Number of being touched
	
	boolean moving;			//Flag for node moving
	boolean dragging;		//Flag for field moving	
	
	boolean springmodel1;
	Thread th;
	
	int node_keyidlist[];
	int topic_num;
	
	////For spring model
	double bane_k;		//Spring constant value	K 0.2
	double minus;		//Repulsion rate against the gravity 0.8	
	
	DecimalFormat df_a;
	
	NewMapData2 map;	
	
	
	public DrawNodes()
	{
		setModuleID(1017);			// Set your module ID after you have got it
	}
	
	public void initializePanel()
	{
		//Create operation pane
		layer = new JPanel();
        layer.setLayout(new GridLayout(0,5));
        layer.setBackground(Color.CYAN);
		add("South",layer);
		
		spring1 = new JButton("Spring");
        spring1.setBackground(Color.YELLOW);
        spring1.addActionListener(this);
		layer.add(spring1);
		
		keyseg = new JButton("Keyseg");
        keyseg.setBackground(Color.YELLOW);
        keyseg.addActionListener(this);
		layer.add(keyseg);
		
		keysen = new JButton("Keysen");
        keysen.setBackground(Color.YELLOW);
        keysen.addActionListener(this);
		layer.add(keysen);
		
		segkey = new JButton("Segkey");
        segkey.setBackground(Color.YELLOW);
        segkey.addActionListener(this);
		layer.add(segkey);
		
		senkey = new JButton("Senkey");
        senkey.setBackground(Color.YELLOW);
        senkey.addActionListener(this);
		layer.add(senkey);		
		
		addMouseListener(this);			//MouseListener
		addMouseMotionListener(this);	//MouseMotionListener	
	}
	
	public void initializeData()
	{
		touch_num = -1;
		
		moving = false;
		dragging = false;
		
		topic_num = 0;
		
		////For spring model
		bane_k = 0.2;		//Spring constant value	K 0.2
		minus = 0.8;		//Repulsion rate against the gravity 0.8
		
		df_a = new DecimalFormat("0.00");		
	}
	
	public void firstData()
	{
		map = new NewMapData2(text.keywordRelationBySegment.number);
		
		boolean active[] = new boolean[text.keywordNumber];
		for(int i=0;i<text.keywordNumber;i++)
			if(text.keyword[i].segmentFrequency < 2)
				active[i] = false;
			else
				active[i] = true;
		
		text.keywordRelationBySegment.optimizeLink(text.keywordRelationBySegment.cos, 0.5, active);		
		
		map.set_relation_data(text.keywordRelationBySegment);	
	}
	
	public void displayOperations(int optionNumber)
	{
		switch(optionNumber)
		{
			case 0:
				firstData();
				repaint();
				break;
		}		
	}	
	
	//	public void setFont(){}	
	
//	public void setData(***){}
	
	
	//////////DRAWING
	void draw_background(Graphics2D g2)
	{
		getPanelSize();
		
        g2.setColor(new Color(0,128,255));
        g2.fillRect(0,0, sizeX, sizeY);
		
	}
	
	//Display links
	public void draw_links(Graphics2D g2)
	{
		g2.setColor(Color.white);
		g2.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER));
		for(int i=0;i<map.node_num;i++)
			for(int j=0;j<map.node_num;j++)
				if(map.relation[i][j].link == true)
					g2.drawLine((int)map.node[i].x,(int)map.node[i].y,(int)map.node[j].x,(int)map.node[j].y);
	}
	
	
	//Display nodes
	public void draw_node(Graphics2D g2, int n)
	{
		g2.setColor(map.node[n].color);
		g2.fillOval((int)map.node[n].x - (int)map.node[n].radius, 
					(int)map.node[n].y - (int)map.node[n].radius, (int)map.node[n].radius*2, (int)map.node[n].radius*2);
	}
	
	public void draw_nodes(Graphics2D g2)
	{
		for(int i=0;i<map.node_num;i++)
			draw_node(g2, i);
	}
	
	////Dispaly node names
	public void draw_node_name(Graphics2D g2, int n)
	{
		g2.setFont(new Font("Dialog", Font.BOLD, (int)(24-2*map.node_num/25.0)));
		g2.drawString(map.node[n].name, (int)map.node[n].x+(int)map.node[n].radius, (int)map.node[n].y+(int)map.node[n].radius);
	}
	
	public void draw_node_names(Graphics2D g2)
	{
		for(int i=0;i<map.node_num;i++)
		{
			g2.setColor(Color.black);
			draw_node_name(g2, i);
		}
	}	
	
	public void draw_touch_node(Graphics2D g2)
	{
		if(touch_num < 0)
			return;
		
		draw_node(g2, touch_num);
	}
	
	//Display node names touched by a mouse
	public void draw_touch_nodename(Graphics2D g2)
	{
		if(touch_num < 0)
			return;
		
		g2.setColor(Color.red);
		draw_node_name(g2, touch_num);
	}
	
	//Display parameters
	public void draw_parameters(Graphics2D g2)
	{
		g2.setColor(Color.yellow);
		g2.setFont(new Font("Dialog", Font.BOLD, 20));
		
		g2.drawString("Min_Re="+df_a.format(map.relate_low),5,sizeY-50);
		
		if(springmodel1)
			g2.drawString("Moving Spring",200,sizeY-50);
		
		
		g2.drawString("Nodes="+map.active_node+"/"+map.node_num,200,sizeY-100);
		g2.drawString("Links="+map.link_num/2,200,sizeY-150);
	}	
	
	public void paintComponent(Graphics g)
	{
		Graphics2D g2 = (Graphics2D)g;
		
		draw_background(g2);	//Set background
		draw_links(g2);			//Display links
		draw_nodes(g2);			//Display nodes
		draw_node_names(g2);	//Display node names
		draw_touch_node(g2);	//Display nodes touched by a mouse
		draw_touch_nodename(g2);	//Display node names touched by a mouse
		draw_parameters(g2);	//Display parameters		
	}
	
	public void update(Graphics g)		//Avoid from blinking
	{
		paintComponent(g);
	}
	
	
	
	////Buttons
	//Action Listener
	public void actionPerformed(ActionEvent e)
	{
		if(e.getSource() == spring1)		//Start and Stop Spring model
		{
			if(springmodel1)
				stop_spring1();
			else
			{
				bane_k = 0.2;
				start_spring1();
			}
		}
		
		if(e.getSource() == keyseg)		
		{
			map = new NewMapData2(text.keywordRelationBySegment.number);
			
			boolean active[] = new boolean[text.keywordNumber];
			for(int i=0;i<text.keywordNumber;i++)
				if(text.keyword[i].segmentFrequency < 2)
					active[i] = false;
				else
					active[i] = true;
			
			text.keywordRelationBySegment.optimizeLink(text.keywordRelationBySegment.cos, 0.5, active);		
			
			map.set_relation_data(text.keywordRelationBySegment);
		}
		
		if(e.getSource() == keysen)		
		{
			map = new NewMapData2(text.keywordRelationBySentence.number);
			
			boolean active[] = new boolean[text.keywordNumber];
			for(int i=0;i<text.keywordNumber;i++)
				if(text.keyword[i].sentenceFrequency < 2)
					active[i] = false;
				else
					active[i] = true;
			
			text.keywordRelationBySentence.optimizeLink(text.keywordRelationBySentence.cos, 0.5, active);		
			
			map.set_relation_data(text.keywordRelationBySentence);		
		}
		
		if(e.getSource() == segkey)		
		{
			map = new NewMapData2(text.segmentRelation.number);
			
			text.segmentRelation.optimizeLink(text.segmentRelation.cos, 0.5);		
			
			map.set_relation_data(text.segmentRelation);		
		}
		
		if(e.getSource() == senkey)		
		{
			map = new NewMapData2(text.sentenceRelation.number);
			
			text.sentenceRelation.optimizeLink(text.sentenceRelation.cos, 0.5);		
			
			map.set_relation_data(text.sentenceRelation);		
		}			
		
	}	
	
	
	
	//MouseListener
	public void mousePressed(MouseEvent me)	//Press
	{
		mousex = me.getX();
		mousey = me.getY();
		
		if(touch_num == -1)
		{
			old_mousex = mousex;
			old_mousey = mousey;
			dragging = true;
			return;
		}
		moving = true;
	}
	
	public void mouseReleased(MouseEvent me)	//Release
	{
		if(moving == true)
			moving = false;
		
		if(dragging == true)
			dragging = false;
	}
	
	public void mouseEntered(MouseEvent me){}	//Enter an area
	public void mouseExited(MouseEvent me){}	//Exit an area
	
	
	
	public void mouseClicked(MouseEvent me)	//Click
	{
		int click_num;
		
		mousex = me.getX();
		mousey = me.getY();
		click_num = CatchNode(mousex,mousey);
		
		if(click_num == -1)
		{
			topic_num = 0;
			for(int i=0;i<map.node_num;i++)
				if(map.node[i].y < 30*sizeY/100)
					topic_num++;
			
			node_keyidlist = new int[topic_num];
			
			int c=0;
			for(int i=0;i<map.node_num;i++)
				if(map.node[i].y < 30*sizeY/100)				
					node_keyidlist[c++] = map.node[i].id;
			
			//			pvs1.indica.setSelected(true);
			//		mining.set_data(node_keyidlist);//FEEDBACK
			//		mining.module_options(0);			//
			//		mining.module_options(1);			//
			System.out.println("Clicked!!");			
			//			init_data();
		}
		repaint();
	}	
	
	//MouseMotionListener
	public void mouseDragged(MouseEvent me)	//Dragging
	{
		if(moving == true)		//Move a node
		{
			try{
				mousex = me.getX();
				mousey = me.getY();
				
				map.node[touch_num].x = mousex;
				map.node[touch_num].y = mousey;
				
				repaint();
			}
			catch(StackOverflowError e){
				System.out.println("Stack OVERFLOW");
			}
		}
		
		if(dragging == true)	//Move Field
		{
			mousex = me.getX();
			mousey = me.getY();
			dx = old_mousex - mousex;
			dy = old_mousey - mousey;
			old_mousex = mousex;
			old_mousey = mousey;
			
			for(int i=0;i<map.node_num;i++)
			{
				map.node[i].x -= dx;
				map.node[i].y -= dy;
			}
			repaint();
		}
	}
	
	public void mouseMoved(MouseEvent me)
	{
		mousex = me.getX();
		mousey = me.getY();
		touch_num = CatchNode(mousex,mousey);
		repaint();
	}
	
	public int CatchNode(int mousex, int mousey)	//Investigate number of selected node
	{
		for(int i=0;i<map.node_num;i++)
			if((mousex > map.node[i].x - map.node[i].radius && mousex < map.node[i].x + map.node[i].radius 
				+ map.node[i].name.length()*(24-2*map.node_num/25.0)) && 
			   (mousey > map.node[i].y - map.node[i].radius && mousey < map.node[i].y + map.node[i].radius))
				return i;
		
		return -1;
	}
	
	
	//Execute spring model	
	void start_spring1()
	{
		th = new Thread(this);
		map.calc_idist();//
		springmodel1 = true;
		th.start();
	}
	void stop_spring1()
	{
		springmodel1 = false;
		th.yield();
		repaint();
	}
	

	
	public void run()
	{
		try		//Execute Spring model
		{
			while(springmodel1)
			{
				map.springstep(bane_k, minus);
				map.rearrange();
				repaint();
				bane_k -= 0.001;
				if(bane_k < 0)
					stop_spring1();
				th.sleep(100);
			}
		}
		catch(InterruptedException e){}	
	}
	
	
	
	class NewMapData2
	{
		
		class Node
		{
			String name;
			int id;
			
			double x;
			double y;
			double radius=10;
			
			int link_num;
			
			Color color;
		}
		
		Node node[];
		int node_num;
		int active_node;
		
		
		//Node Relation Class
		class Relation
		{
			boolean link;			//Is exist a link between nodes?
			double value;
			
			double rdist;			// real distance for spring model
			double idist;			// ideal distance for spring model
		}
		
		Relation relation[][];		//Node Relation information	
		int link_num;				//Number of all links
		
		double relate_low;
		
		void init_nodes(int num)
		{
			node_num = num;
			node = new Node[node_num];
			
			for(int i=0;i<node_num;i++)
				node[i] = new Node();
			
			relation = new Relation[node_num][node_num];
			
			for(int i=0;i<node_num;i++)
				for(int j=0;j<node_num;j++)
					relation[i][j] = new Relation();
		}
		
		NewMapData2(int num)
		{
			init_nodes(num);
		}
		
		//Variables for operation
		double curdist = 100;		//Minimum distance between nodes (for spring model)		
		
		void set_relation_data(RelationalData rd)
		{
			Color color[] = new Color[4];
			color[0] = new Color(0xff,0x66,0x00);
			color[1] = new Color(0xa5,0x2a,0x2a);
			color[2] = new Color(0x00,0x00,0xcc);
			color[3] = new Color(0x00,0xdb,0x47);
			
			for(int i=0;i<rd.number;i++)
			{
				map.node[i].id = i;
				map.node[i].name = rd.name[i];
				map.node[i].x = sizeX/2 + sizeX/4 * Math.cos(i);
				map.node[i].y = sizeY/2 + sizeY/4 * Math.sin(i);
				map.node[i].color = color[0];			
			}			
			
			
			for(int i=0;i<node_num;i++)
				for(int j=0;j<node_num;j++)
				{
					relation[i][j].link = rd.link[i][j];
					relation[i][j].value = rd.cos[i][j];
				}	
			
			link_num = rd.totalLinkNumber;
			
			active_node = 0;
			for(int i=0;i<node_num;i++)
				if(rd.linkNumber[i] > 1)
				{
					node[i].link_num = rd.linkNumber[i];
					active_node++;			
				}
			
			relate_low = rd.linkThreshold;
		}
		
		
		//// 3) Spring model
		//Standard values
		//bane_k: Spring constant value 0.2
		//minus: rate of repulsion power against the gravity 0.8
		
		//Ideal distance calculation
		void calc_idist()
		{
			for(int i=0;i<node_num;i++)
				for(int j=0;j<node_num;j++)
				{	
					if(relation[i][j].link)
						relation[i][j].idist = curdist;
					else
						relation[i][j].idist = Math.max(curdist*5/relation[i][j].value, node[i].radius*2 + curdist);
					
					relation[i][j].idist = Math.min(relation[i][j].idist, curdist+sizeX/2);
				}
		}
		
		//Move nodes by spring model, calculated from relation values and ideal distances
		void springstep(double bane_k, double minus)	//Calculate power effect on each node
		{
			double tx,ty,dx,dy,tdx,tdy;
			int i,j;
			double power_limit;
			
			power_limit = 100 - link_num/10;
			if(power_limit < 1)
				power_limit = 1;
			
			for(i=0;i<node_num;i++)
			{
				dx = dy = 0;
				if(node[i].link_num > 0)
					for(j=0;j<node_num;j++)
						if(i != j && node[j].link_num > 0)
						{
							tx = node[i].x-node[j].x;
							ty = node[i].y-node[j].y;
							relation[i][j].rdist = Math.sqrt(Math.pow(tx,2)+Math.pow(ty,2));
							
							if(relation[i][j].rdist ==0)
								continue;
							
							tdx = tx*bane_k*(1.0 - relation[i][j].idist/relation[i][j].rdist);
							tdy = ty*bane_k*(1.0 - relation[i][j].idist/relation[i][j].rdist);
							
							if(tdx > power_limit)
								tdx = power_limit;
							if(tdx < -power_limit)
								tdx = -power_limit;
							if(tdy > power_limit)
								tdy = power_limit;
							if(tdy < -power_limit)
								tdy = -power_limit;
							
							if(relation[i][j].link == true)  //Gravity
							{
								//Power to X direction and to Y direction
								dx += tdx;
								dy += tdy;
							}
							else	//Repulsion
							{
								dx += minus*tdx;
								dy += minus*tdy;
							}
						}
				node[i].x -= dx;
				node[i].y -= dy;
				
				if(node[i].x < 0)
					node[i].x = 0;
				if(node[i].y < 0)
					node[i].y = 0;
				if(node[i].x > sizeX)
					node[i].x = sizeX - node[i].radius*2;
				if(node[i].y > sizeY)
					node[i].y = sizeY - node[i].radius*2;
			}
		}
		
		public void rearrange()	 //Re-arrangement of nodes: Place to the center of the field
		{
			int i,minx=0,maxx=0,miny=0,maxy=0,dx,dy;
			
			//Optimize display position
			for(i=0;i<node_num;i++)//Initial values
				if(node[i].link_num > 0)
				{
					minx = maxx = (int)node[i].x;
					miny = maxy = (int)node[i].y;
					break;
				}
			
			for(;i<node_num;i++)//Measure range of existance nodes
				if(node[i].link_num > 0)
				{
					if(node[i].x < minx)
						minx = (int)node[i].x;
					if(node[i].x > maxx)
						maxx = (int)node[i].x;
					if(node[i].y < miny)
						miny = (int)node[i].y;
					if(node[i].y > maxy)
						maxy = (int)node[i].y;
				}
			
			if(maxx == minx && maxy == miny)
				return;
			
			dx = sizeX/2 - (maxx+minx)/2 - 30;
			dy = sizeY/2 - (maxy+miny)/2 - 30;
			
			for(i=0;i<node_num;i++)
				if(node[i].link_num > 0)
				{
					node[i].x += dx;
					node[i].y += dy;
				}
		}
	}
}
