import java.awt.*;
import java.awt.image.*;
import java.applet.*;
import java.util.*;
import java.lang.*;
import java.awt.event.*;
import java.io.*;

// mostly main stuff
public class pointMapper extends Frame
{
		
	public static void main(String args[])
	{
		String srcFilename="";
		String destFilename="";
		String pointFilename="";
		_pointMapper PM;
		
		try
		{
		srcFilename=args[0];
		destFilename=args[1];
		pointFilename=args[2];
		} catch (Exception e){ };

		if (srcFilename.equals(""))  srcFilename="src.jpg";
		if (destFilename.equals("")) destFilename="dest.jpg";
		if (pointFilename.equals("")) pointFilename="points.dat";
		
		PM=new _pointMapper(srcFilename,destFilename,pointFilename);
		
	}	

}

class _pointMapper extends Frame
{
	String srcFilename;
	String destFilename;
	String pointFilename;
	Image src,dest;
	int W,H;
	int[][] points; //Format for point n = point[n]=(xs,ys,xd,yd); points=[N][4]
	int N;
	int active; 	// either -1 or in range [0,N-1]
	int prevActive;
	int top;		// in range [0,N]
	
	boolean pressed;	// true is mouse button is pressed inside an image window.
	boolean srcActive;	// true if editing is being done on the source.
	boolean destActive;	// true if editing is being done on the destination.
	Button writePointfile = new Button("Write Pointfile");
	Button removePoint = new Button("Remove Point");
	Button exit = new Button("Exit");

	_pointMapper(String srcFilename,String destFilename,String pointFilename)
	{
		this.srcFilename=srcFilename;
		this.destFilename=destFilename;
		this.pointFilename=pointFilename;
		try
		{
			Toolkit toolkit=Toolkit.getDefaultToolkit();
			this.src=toolkit.getImage(srcFilename);
			this.dest=toolkit.getImage(destFilename);
		
			MediaTracker t=new MediaTracker(this);
			t.addImage(this.src,0);
			t.addImage(this.dest,1);
			t.waitForID(0);
			t.waitForID(1);
			this.W=this.src.getWidth(null);
			this.H=this.src.getHeight(null);
			
		} catch (InterruptedException e){ };
		
		Panel S= new Panel();
		setLayout (new BorderLayout());
		
		add("South",S);
		S.add(removePoint);
		S.add(writePointfile);
		S.add(exit);

		this.setBackground(CONSTANTS.background);
		this.setForeground(CONSTANTS.foreground);
		this.setTitle("Point mapper.");
		this.resize(10+2*W+10+10,30+H+50);
		this.show();

		N=200;		//Set max no. of points here.
		points=new int[N][4];
		active=-1;
		top=0;
	}

	public boolean mouseDrag(Event evtobj,int x,int y)
	{
		if (pressed)
		{
			if (inSrc(x,y))
			{
				// active point to be moved.
				points[active][0]=x;
				points[active][1]=y;
				repaintActiveArea();
				return true;
			}
			if (inDest(x,y))
			{
				// active point to be moved.
				points[active][2]=x;
				points[active][3]=y;
				repaintActiveArea();
				return true;
			}
		}
		

		return true;
	}


	public boolean mouseUp(Event evtobj,int x,int y)
	{
		pressed=false;

		return true;
	}

	
	public boolean mouseDown(Event evtobj,int x,int y)
	{
		if (inSrc(x,y))
		{
			srcActive=true;
			destActive=false;
			
			// check weather user is trying to edit an old point.
			int i=getIndexInSrc(x,y);
			if (i!=-1)
			{
				//user wants to select an existing point
				setActive(i);
				repaintActiveArea();
				pressed=true;
				return true;
			}
			// user wants to specify a new point.
			setActive(addPointInSrc(x,y));
			repaintActiveArea();

			return true;
		}
		
		if (inDest(x,y))
		{
			destActive=true;
			srcActive=false;
			
			// check weather user is trying to edit an old point.
			int i=getIndexInDest(x,y);
			if (i!=-1)
			{
				//user wants to select an existing point
				setActive(i);
				repaintActiveArea();
				pressed=true;
				return true;
			}
			// user wants to specify a new point.
			setActive(addPointInDest(x,y));
			repaintActiveArea();

			return true;
		}
		return true;
	}

	
	// Adds a point in the src and corresponding point in the dest.
	// Returns the array index of the point added. 
	int addPointInSrc(int x,int y)
	{
		if (top==N) return active;	//Number of points exceeded.
		
		points[top][0]=x;
		points[top][1]=y;
		points[top][2]=x+W+10;
		points[top][3]=y;	
		top++;
		return top-1;
	}
	
	int addPointInDest(int x,int y)
	{
		if (top==N) return active;	//Number of points exceeded.

		points[top][2]=x;
		points[top][3]=y;
		points[top][0]=x-W-10;
		points[top][1]=y;	
		top++;
		return top-1;
	}

	// Gets the index of the point in src, if present, otherwise returns -1.
	int getIndexInSrc(int x,int y)
	{
		int xs,ys;
		for (int n=0;n<top;n++)
		{
			xs=points[n][0];
			ys=points[n][1];
			if ((x>=xs)&&(x<=xs+5)&&(y>=ys)&&(y<=ys+5)) return n;
		}
		return -1;
	}

	int getIndexInDest(int x,int y)
	{
		int xs,ys;
		for (int n=0;n<top;n++)
		{
			xs=points[n][2];
			ys=points[n][3];
			if ((x>=xs)&&(x<=xs+5)&&(y>=ys)&&(y<=ys+5)) return n;
		}
		return -1;
	}

	// returns true if x,y are within source area.
	boolean inSrc(int x,int y)
	{
		return (x>10)&&(x<10+W)&&(y>30)&&(y<30+H);
	}
	
	// returns true if x,y are within destination area.
	boolean inDest(int x,int y)
	{
		return (x>10+W+10)&&(x<10+2*W+10)&&(y>30)&&(y<30+H);
	}

	
	public void paint(Graphics g)
	{
		g.drawImage(src,10,30,null);
		g.drawImage(dest,10+W+10,30,null);
		g.drawRect(10,30,W,H);
		g.drawRect(10+W+10,30,W,H);
		
		for (int n=0;n<top;n++)
		{
			g.fillRect(points[n][0],points[n][1],5,5);
			g.fillRect(points[n][2],points[n][3],5,5);
		}
	
		if (active != -1)
		{
			int n=active;
			g.setColor(Color.yellow);
			g.fillRect(points[n][0],points[n][1],5,5);
			g.fillRect(points[n][2],points[n][3],5,5);
			g.setColor(Color.black);
			g.drawRect(points[n][0],points[n][1],5,5);
			g.drawRect(points[n][2],points[n][3],5,5);
		}
	}
	
	void setActive(int i)
	{
		prevActive=active;
		if (active==-1) prevActive=i;
		active=i;
	}

	void repaintActiveArea()
	{
		if (active==-1) 
		{
			repaint();
			return;
		}
		repaint(points[active][0]-10,points[active][1]-10,25,25);
		repaint(points[active][2]-10,points[active][3]-10,25,25);
		repaint(points[prevActive][0]-10,points[prevActive][1]-10,25,25);
		repaint(points[prevActive][2]-10,points[prevActive][3]-10,25,25);
	}

	public void update(Graphics g)
	{
		paint(g);
	}

	public boolean action(Event evtObj,Object arg)
	{
		if (evtObj.target instanceof Button)
		{
			if (arg.equals("Write Pointfile"))
			{
				writePointFile();
			}

			if (arg.equals("Remove Point"))
			{
				if (active != -1)
				{
					for (int n=active;n<top-1 ;n++ )
					{
						points[n][0]=points[n+1][0];
						points[n][1]=points[n+1][1];
						points[n][2]=points[n+1][2];
						points[n][3]=points[n+1][3];
					}
					top--;
					setActive(-1);
					repaint();
				}
			}

			if (arg.equals("Exit"))
			{
				System.exit(0);
			}
		}

		
		return false;
	}

	void writePointFile()
	{
		// First construct the entire file as a string.
		String F="\n"+top+"\n";
		FileOutputStream fout;

		
		for (int n=0;n<top;n++)
		{
			F=F+" "+(points[n][0]-10)+" "+(points[n][1]-25)+"\t"+(points[n][2]-10-W-10)+" "+(points[n][3]-25)+"\n";
		}
				
		//Now write this to the file.
		try{
			fout = new FileOutputStream(pointFilename);
		
			for (int n=0;n<F.length();n++)
			{
				fout.write(F.charAt(n));
			}
			fout.close();
		} catch (IOException e){ };
	}

	
}

class CONSTANTS
{
	static Color background=new Color((0xff000000 | (99)));
	//static Color foreground=new Color((0xff000000 | (247<<16) | (247<<8)));
	static Color foreground=new Color((0xff000000 | (150<<16) | (150<<8)));
};
