//
// Visuzalization module for TETDM 
// DrawNodes2.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.DrawNodes2;

import source.*;
import source.TextData.*;

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 DrawNodes2 extends VisualizationModule implements MouseListener, MouseMotionListener, Runnable
{
	//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;
	int oldTouchNumber;
	
	DecimalFormat df_a;
	
	NewMapData2 map;
	
	public DrawNodes2()
	{
		setModuleID(1018);		
	}

	public void initializePanel()
	{
		addMouseListener(this);			//MouseListener
		addMouseMotionListener(this);	//MouseMotionListener	
	}	
	
	public void initializeData()
	{
		df_a = new DecimalFormat("0.00");
		
		touch_num = -1;
		
		moving = false;
		dragging = false;
		
		topic_num = 0;
	}

	public void firstData()
	{
		map = new NewMapData2(text.segmentRelation.number);
		
		boolean active[] = new boolean[text.segmentNumber];
		for(int i=0;i<text.segmentNumber;i++)
			active[i] = true;
		
		text.segmentRelation.createStrongestLink(text.segmentRelation.cos);
//		text.segmentRelation.optimize_link(text.segmentRelation.cos, 0.5);		
		
		map.set_relation_data(text.segmentRelation);		
	}
	
	public void displayOperations(int optionNumber)
	{
		switch(optionNumber)
		{
			case 4501:	
			case 0:
				firstData();
				repaint();
				break;
		}		
	}	
	
	
	//////////DRAWING
	
	/*	
	void calcu_coordinate(int sx, int sy)
	{
		double x_resize = 1.0, y_resize = 1.0;
		
		if(sizeX == 0 || sizeY == 0)
		{
			sizeX = getWidth();
			sizeY = getHeight();
			map.set_relation_data(text.segmentRelation);			
		}
		else
		{
			x_resize = (double)sx/(double)sizeX;
			y_resize = (double)sy/(double)sizeY;			
		}

		for(int i=0;i<text.segmentNumber;i++)
		{
			map.node[i].x *= x_resize;
			map.node[i].y *= y_resize;
		}
	}*/
	
	void calcu_coordinate()
	{		
		for(int i=0;i<text.segmentNumber;i++)
		{
			map.node[i].x *= changeX;
			map.node[i].y *= changeY;
		}
	}
	
	void draw_background(Graphics2D g2)
	{
//		sizeX = getWidth();
//		sizeY = getHeight();
		
        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)(text.fontSize+10-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(text.focus.mainFocusSegment < 0)
			return;
		
		int i=text.focus.mainFocusSegment, j=text.segmentRelation.rank[text.focus.mainFocusSegment][1];
		
		g2.setColor(Color.white);
		g2.setStroke(new BasicStroke(5, BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER));
		g2.drawLine((int)map.node[i].x,(int)map.node[i].y,(int)map.node[j].x,(int)map.node[j].y);		
		
		
		g2.setColor(Color.red);
		draw_node_name(g2, i);
		draw_node_name(g2, j);
	}

	//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.linkNumber/2,200,sizeY-150);
	}	
	
	void draw_for_linkage(Graphics2D g2)
	{
		for(int i=0;i<map.node_num;i++)
			if(text.focus.focusKeywords[i])			
			{
				g2.setColor(Color.white);
				draw_node_name(g2, i);
			}
		
		if(text.focus.mainFocusSentence >= 0)
		{
			g2.setColor(Color.red);
			draw_node_name(g2, text.focus.mainFocusSentence);
		}	
	}	
	
	public void paintComponent(Graphics g)
	{
		
		Graphics2D g2 = (Graphics2D)g;
		
//		int sx, sy;
//		sx = getWidth();
//		sy = getHeight();
		
//		if(sx != sizeX || sy != sizeY)
//			calcu_coordinate(sx,sy);
		
		boolean change = getPanelSize();
		
		if(oldSizeX == 0 || oldSizeY == 0)
			map.set_relation_data(text.segmentRelation);		
		
		if(change)
			calcu_coordinate();
		
//		System.out.println(map.node[0].x+":"+map.node[0].y);
		
		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
		
		draw_for_linkage(g2);
	}
	
	public void update(Graphics g)		//Avoid from blinking
	{
		paintComponent(g);
	}
	
	//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);

		if(touch_num >= 0)
		{
			text.focus.mainFocusSegment = touch_num;
			text.focus.subFocusSegment = text.segmentRelation.rank[touch_num][1];//

			if(touch_num != oldTouchNumber)
			{
				repaintOthersByTouch();
				oldTouchNumber = touch_num;
			}
		}
		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();
	}

	////For spring model
	double bane_k = 0.2;		//Spring constant value	K 0.2
	double minus = 0.8;			//Repulsion rate against the gravity 0.8
	
	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 linkNumber;
		
			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 linkNumber;				//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++)
			{
				node[i].id = i;
				node[i].name = rd.name[i];
				node[i].x = sizeX/2 + (sizeX/2-50) * Math.cos(i);
				node[i].y = sizeY/2 + (sizeY/2-50) * Math.sin(i);				
				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];
				}	
			
			linkNumber = rd.totalLinkNumber;
			
			active_node = 0;
			for(int i=0;i<node_num;i++)
				if(rd.linkNumber[i] > 1)
				{
					node[i].linkNumber = 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 - linkNumber/10;
			if(power_limit < 1)
				power_limit = 1;
			
			for(i=0;i<node_num;i++)
			{
				dx = dy = 0;
				if(node[i].linkNumber > 0)
					for(j=0;j<node_num;j++)
						if(i != j && node[j].linkNumber > 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].linkNumber > 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].linkNumber > 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].linkNumber > 0)
				{
					node[i].x += dx;
					node[i].y += dy;
				}
		}
	}
}
