/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 * This file is part of CMUcamGUI, a java program that helps     *
 * interface with the CMUcam Vision Board.                       *
 * Contact cmucam@cs.cmu.edu, or see                             *
 * http://www.cs.cmu.edu/~cmucam for more information.           *
 *                                                               *
 * Copyright 2001 Anthony Rowe                                   *
 *                                                               *
 * This program is free software; you can redistribute it and/or *
 * modify it under the terms of the GNU General Public License   *
 * as published by the Free Software Foundation - version 2.     *
 *                                                               *
 * This program is distributed in the hope that it will be       *
 * useful, but WITHOUT ANY WARRANTY; without even the implied    *
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       *
 * PURPOSE.  See the GNU General Public License in file COPYING  *
 * for more details.                                             *
 *                                                               *
 * You should have received a copy of the GNU General Public     *
 * License along with this program; if not, write to the Free    *
 * Software Foundation, Inc., 59 Temple Place - Suite 330,       *
 * Boston, MA 02111, USA.                                        *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.io.*;
import java.lang.Object;
import java.util.*;

/*
This is where most of the good stuff happens.  I'm personally not a 
big fan of oop, so don't blame me for the mess...(java is just nice for 
rapid GUI prototyping)

This object gets called from mainWindow when menu events happen.  
This then holds the current on screen image and does the actual
double buffered drawing to the window. 
It handles the following:
1) Track Color box
2) Middle Mass point
3) Bitmap for track color
4) lines for lm mean
5) Dump Frame
*/
public class CameraImage extends Canvas implements Serializable
{
    Frame picker_f;
    boolean pickerStat;
    public int mm;
    int mmx,mmy,mmoldx,mmoldy;
    char bmask[][];
    int lm,lmIndex,tc;
    int width,height;
    int pixels[];
    int col,row;
    int mx,my,xcoord,ycoord,zcoord,oldz,oldx,oldy,x2coord,y2coord,oldx2,oldy2,conf;
    MemoryImageSource source;
    Image image;
    Image offScreenImage;
    Graphics offScreenGC;
    zoomBox azoomBox;
    Label red_l,green_l,blue_l;
    Panel colorBox;
    int frames,fps;
    long startTime;
    int start;
    long wdTimer;
    int scale;
    colorTrack myTrack;
    outWindow outWin;
    meanWindow myMean;
    rawWindow myRaw;
    channelWindow myChannels;
    setWindow mySW;
    camSettings myCamSettings;
    Frame channel_f;
    serialComm mySerial;
    CameraImage(int x,int y,String commPort)
    {
	mySerial=new serialComm(commPort);
	

	bmask= new char[10][48];
	lmIndex=0;
    	mmoldx=0;
	mmoldy=0;
	mmx=0;
	mmy=0;
	mm=1;
	lm=0;
	scale=0;
	start=1;
	zcoord=0;
	xcoord=0;
	ycoord=0;
	x2coord=0;
	y2coord=0;
	oldx=0;
	oldy=0;
	oldx2=0;
	oldy2=0;
	oldz=0;
	conf=0;
	tc=0;
	pickerStat=false;
	picker_f= new Frame("Color Picker");
	Panel picker_p = new Panel();
	red_l = new Label("0");
	green_l = new Label("0");
	blue_l = new Label("0");
	azoomBox = new zoomBox();
	colorBox = new Panel();
	colorBox.setSize(20,20);
	picker_f.setBackground(Color.lightGray);
	picker_p.setLayout(new GridLayout(2,3));
	picker_p.add(new Label("Red"));
	picker_p.add(new Label("Green"));
	picker_p.add(new Label("Blue"));
	picker_p.add(red_l);
	picker_p.add(green_l);
	picker_p.add(blue_l);
	picker_f.add("North",new Panel());
	picker_f.add("South",new Panel());
	picker_f.add("West",new Panel());
	//picker_f.add("Center",azoomBox);
	picker_f.add("Center",colorBox);
	picker_f.add("East",picker_p);
	picker_f.setSize(200,70);
	picker_f.setLocation(260,0);
	picker_f.show();
	pickerStat=true; 
	setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
	enableEvents(AWTEvent.MOUSE_EVENT_MASK |
		     AWTEvent.MOUSE_MOTION_EVENT_MASK);
	col=0;
	row=0;
	this.setBackground(Color.black);
	width=x;
	height=y;
	pixels=new int[width*height];
	source = new MemoryImageSource(width,height,pixels,0,width);
	source.setAnimated(true);
	image=createImage(source);
	frames=0;
		fps=0;
	Date time = new Date();
		startTime=time.getTime();	
		
      myTrack = new colorTrack();
      outWin = new outWindow();
      myMean = new meanWindow();
      myChannels = new channelWindow(width,height);
      mySW = new setWindow();
      myCamSettings = new camSettings();
      channel_f= new Frame("Image Channels");
      channel_f.add(myChannels);
      channel_f.setBackground(Color.lightGray);
      channel_f.setSize(200,500);
      channel_f.setLocation(670,0);
      channel_f.show();
      //myRaw = new rawWindow();
    }
    public void hideSW() {mySW.hide();}
    public void showSW(){mySW.show();}
    public void hideTrack() {myTrack.hide();}
    public void showTrack(){myTrack.show();}
    public void hideMean() {myMean.hide();}
    public void showMean(){myMean.show();}
    public void processEvent(AWTEvent e)
    {
	mx=((MouseEvent)e).getX();
	my=((MouseEvent)e).getY();
	int tx,ty,pix,tmp;
	tx=mx-(getSize().width-width)/2;
	ty=my-(getSize().height-height)/2;
	if(tx<0 || ty<0 || tx>width-1 || ty>height-1)
	    pix=0;
	else
	    pix=pixels[ty*width+tx];
	Color pixColor= new Color(pix);
	colorBox.setBackground(pixColor);
	tmp=pix & 0xFF;
	blue_l.setText(String.valueOf(tmp));
	tmp=pix>>8 & 0xFF;
	green_l.setText(String.valueOf(tmp));
	tmp=pix>>16 & 0xFF;
	red_l.setText(String.valueOf(tmp));
    }

    private int readNum()
    {
	char one,two,three,four;
	
     
	one=mySerial.readChar();	
	two=mySerial.readChar();
	if( two==' '|| two=='\r' )
	    return((int)(one-'0'));
	three=mySerial.readChar();
	if( three==' ' || three=='\r' )
	    return((int)(one-'0')*10+(two-'0'));
	four=mySerial.readChar();
	return((int)(one-'0')*100+(two-'0')*10+(three-'0'));
     
    }

   
    /*
      Set the window.
      Set the background image correctly for dump frame
    */
    public int sw()
    {
	for(int l=0; l<width; l++ )
	    for(int d=0; d<height; d++ )
		pixels[d*width+l] = 0;
	sendCommand(mySW.sendString());
	source.newPixels(0,0,width,height);
    return 0;
    }
    public int trackColor(int mode)
    {
	int data;
	if(start==1)
	    {
	
		if(mode==0)
		    {
			if(!sendCommand(myTrack.sendString()))return 0;
		    }
		else 
		    {
			if(!sendCommand("tw" + "\r")) return 0;
		    }
	    }
	try{
	    //data=sPortIn.read();
	    data=mySerial.readChar();
	    if(data==0xFE)  // Ignore a line mode mean packet
		{ 
		    data=mySerial.readChar();
		    while(data!=0xFD) data=mySerial.readChar();
		}
	    if(data=='C' || data=='M' || data==0xAA)
		{
		    tc=1;
		   
		    if(data==0xAA)
			lm=1;
		    else
			lm=0;
		    lmIndex=0;
		    if(lm==1)  // Read in the bitmap for lm
			{
				    try{
					data=0;
					int index=0;
					Date time = new Date();
					wdTimer=time.getTime();
					while(data!=0xAA)
					    {
						data=mySerial.readByte(); //(char)sPortIn.read();
						bmask[index][lmIndex]=(char)data; 
						index++;
						if(index==10)
						    {
							index=0;
							lmIndex++;
						    }
							Date time2 = new Date();
							if(time2.getTime()-wdTimer>1000) {System.out.println("<Track> time out"); return 0; }
					    }

					data=mySerial.readByte();//(char)sPortIn.read();
					if(data!=0xAA)
					    {
						System.out.println( "Line Mode Termination Error" );
					    }
				    } catch(Exception e) {}   
				    data=mySerial.readChar();
				    data=mySerial.readChar();
				    if(data=='S')
					return 2;
				    
			}
		    if(data=='M') // If middle mass is on
			mm=1;
		    else 
			mm=0;
		    data=mySerial.readChar();
		    if(mm==1)
			{
			    mmx=readNum();
			    mmy=readNum();
			}
		    xcoord=readNum();
		    ycoord=readNum();
		    x2coord=readNum();
		    y2coord=readNum();
		    zcoord=readNum();
		    conf=readNum();
		    frames++;
		    Date time = new Date();
		    if( time.getTime()-startTime>1000 )
			{
			    fps = frames;
			    frames=0;
			    startTime=time.getTime();
			}
		 
		    /*if(lm==0)*/ outWin.append("x1="+ (new Integer(xcoord)).toString()+" y1="+
				      (new Integer(ycoord)).toString()+" x2="+
				      (new Integer(x2coord)).toString()+" y2="+
				      (new Integer(y2coord)).toString()+" size="+
				      (new Integer(zcoord)).toString() + " fps="+
				       (new Integer(fps)).toString()+" conf="+
				      (new Integer(conf)).toString() );
		
		    //xcoord*=4.4;
		    xcoord*=2;
		    xcoord=160-xcoord;
		    //x2coord*=4.4;
		    x2coord*=2;
		    x2coord=160-x2coord;
		    ycoord=144-ycoord;
		    y2coord=144-y2coord;
		    repaint();
		}
	  }catch(Exception e) { System.out.println(e);}
	
	return 2;
    }
    

    // Set the camera
    public void setCamera()
    {

	sendCommand(myCamSettings.getString()+"\r");
       
    }
    /*
      this reads the current image and breaks
      it up into the channels window
    */
    public void splitChannels()
    {

	myChannels.splitChannels(pixels);
	
    }

public int getMean(int mode)
    {
	int data,cnt,pix;
	if(start==1)
	    {
		if(!sendCommand("gm\r"))return 0;
	    }
      
	cnt=0;
	data=mySerial.readChar();
	if(data==0xFE) // Start of line mode mean flag
	    { 
		int pcnt=0;
		tc=0;
		while(true)
		    {
			int red,green,blue;
			red=mySerial.readChar();
			if(red==0xFD) // Got the end of a packet
			    {
				source.newPixels(0,0,width,height);
				if(pcnt<143) scale=1;
				else scale=0;
				break;
			    }
			green=mySerial.readChar();
			if(green==0xFD) break;
			blue=mySerial.readChar();
			if(blue==0xFD) break;
			pix=0;
			pix+=blue;
			pix+=green<<8;
			pix+=red<<16;
			Color pixColor= new Color(pix);	
			pcnt++;
			// This loop draws the get mean lines on the background image
			for(int col=0; col<160; col++ )
			    {
				if(cnt<143)
				    {
					pixels[cnt*width+col] = pixColor.getRGB();
					if(scale==1) pixels[(cnt+1)*width+col] = pixColor.getRGB(); 
				    }
			
			    }
			
			cnt++;
			if(scale==1) cnt++;
		    }
	      
	    }	
	if(data=='S')  // Mean packet
		{
		    int rm,gm,bm,rd,gd,bd;
		    // Track Color Packet
		    data=mySerial.readChar();
		    rm=readNum();
		    gm=readNum();
		    bm=readNum();
		    rd=readNum();
		    gd=readNum();
		    bd=readNum();
		    myMean.update(rm,gm,bm,rd,gd,bd);
		    frames++;
		    Date time = new Date();
		    if( time.getTime()-startTime>1000 )
			{
			    fps = frames;
			    frames=0;
			    startTime=time.getTime();
			}
		    outWin.append( "C1m= " +rm+" C2m= " +bm+" C3m= "+gm+" deviations= " + rd + "," + gd + "," + bd+ " fps= " + fps );
		}
	
	return 6;
    }



    
    /*
      Flushes all data out of the serial file buffer
    */
    public void flushBuf()
    {
	char a;
	try {
	    while(mySerial.aval()!=0) { a=mySerial.readChar(); }
	    //while(sPortIn.available()!=0) { a=mySerial.readChar(); }
	} catch(Exception e) { System.out.println(e); }
    }

    /*
      Trys to get the camera to settle down and idle
    */
    public boolean idle()
    {
	char a,c,k,r,p;
	try
	    {
	        Date time = new Date();
		wdTimer=time.getTime();	
		mySerial.writeStr("\r");
		//mySerial.writeStr(new String("\r").getBytes());
		//a=(char)sPortIn.read();
		a=mySerial.readChar();
		// BUG! Sometimes readChar doesn't return here!!!!
		while(mySerial.aval()!=0)
		    {	
			a=mySerial.readChar();
			if(a==':' ) 
			    {
				start=1;
				xcoord=0;
				ycoord=0;
				x2coord=0;
				y2coord=0;
				zcoord=0;
				repaint();
				outWin.append("Camera OK and idle..." );
				return true;
			    }	
			Date time2 = new Date();
			if(time2.getTime()-wdTimer>1000) {System.out.println("<idle> time out"); return false; }
		    }
		return false;
		
	    }catch(Exception e) {System.out.println(e);}
	return false;
	
    }
    /*
      Writes the string to the output window
    */
    public void writeText(String str)
    {
	outWin.append( str );
	
    }
    /*
      Sends a command and checks for an ACK
      Returns 1 if ACK else 0
    */
    public boolean sendCommand(String command)
    {
	char a,c,k,r;
	a=0;
	try
	    {
		while(mySerial.aval()!=0 )
		    a=mySerial.readChar();
		    //a=(char)sPortIn.read();
		mySerial.writeStr(command);
		a=mySerial.readChar();
		c=mySerial.readChar();
		k=mySerial.readChar();
		r=mySerial.readChar();
		if(a=='A' && c=='C' && k=='K' && r=='\r' ) 
		    {
			outWin.append( ">" + command + "< confirmed..." );
			start=0;
			return true;
		    }
		else
		    {
			outWin.append( ">" + command +"< failed..." );
			start=1;
			return false;
		    }
		
		
	    }catch(Exception e) {System.out.println(e);}
	return false;
    }
    
    public int dumpFrame()
    {
	char a,c,k,r;
	int data, pix;

	
	if(start==1)
	    if(!sendCommand("DF\r")) return 0;

		     
	try{
	    while(mySerial.aval()!=0)
		{
		data=mySerial.readByte();
		if(data==1)  // New frame
		    {
			int tx,ty,tmp;
		
			tx=mx-(getSize().width-width)/2;
			ty=my-(getSize().height-height)/2;
			if(tx<0 || ty<0 || tx>width-1 || ty>height-1)
			    pix=0;
			else
			    pix=pixels[ty*width+tx];
			Color pixColor= new Color(pix);
			colorBox.setBackground(pixColor);
			tmp=pix & 0xFF;
			blue_l.setText(String.valueOf(tmp));
			tmp=pix>>8 & 0xFF;
			green_l.setText(String.valueOf(tmp));
			tmp=pix>>16 & 0xFF;
			red_l.setText(String.valueOf(tmp));
			col=0;
			row=0;
		    }
		else if(data==2) // New col
		    {
			col+=2;
			row=0;
		    }
		else if(data==3) // End frame
		    {
			outWin.append("Dump frame Complete" );
			 source.newPixels(0,0,width,height);
			start=1;
			row=0;
			col=0;
			return 0;
		    }
		else
		    {  // A pixel!
			int red,green,blue,green2;
			red=data;
			green=mySerial.readByte();
			blue=mySerial.readByte();
			green2=green;
			pix=0;
			pix+=blue;
			pix+=green<<8;
			pix+=red<<16;
		
			Color pixColor= new Color(pix);
			pix=0;
			pix+=blue;
			pix+=green2<<8;
			pix+=red<<16;	
			Color pixColor2= new Color(pix);
			if(row<height && col<width)
			    {
			pixels[row*width+col] = pixColor.getRGB();
			pixels[row*width+col+1] = pixColor2.getRGB();
			row++;
			    }
		    }
		}
	}
	catch(Exception e){
	    System.out.println(e);
	}
		    
	
     source.newPixels(0,0,width,height);
     return 1;
    }
   
    /*
      The buffer that holds the background image.
      The lines for line mode on get mean are drawn
      into the background buffer too.
    */
    public void update(Graphics g)
    {	
	//paint(g);
	if(offScreenImage==null)
	    {
		offScreenImage=createImage(getSize().width,getSize().height);
		offScreenGC=offScreenImage.getGraphics();
	    }
	paint(offScreenGC);
	g.drawImage(offScreenImage,0,0,this);
    }
    /*
      This is where the bitmap and tracking boxes are drawn.
      The actual background is add in update()
    */
    public void paint(Graphics g)
    {

	int w_width,w_height;
	w_width=getSize().width;
	w_height=getSize().height;
	int sx=mySW.rX();
	int sy=mySW.rY();
      g.setColor(Color.black);
      g.fillRect(0,0,w_width,w_height);
      g.drawImage(image,(w_width-width)/2+sx,(w_height-height)/2+sy,this);
     
      // Tracking box
      if(xcoord!=width && ycoord!=height && x2coord!=width && y2coord!=height && zcoord!=0)
    	  { 
	      if(conf>50) g.setColor(Color.green);
	      else
	      if(conf>0) g.setColor(Color.yellow);
	      else g.setColor(Color.red); 
	      g.fillRect(x2coord+(w_width-width)/2,y2coord+(w_height-height)/2, xcoord-x2coord,ycoord-y2coord );
	  }

      // Line mode for bitmap drawing
      if(lm==1 && tc==1)
	  {
	      g.setColor(Color.cyan); 
	      int bx,by;
	      for(int k=0; k<lmIndex; k++ )
		  for(int j=0; j<10; j++ )
		      {
			  
			  for(int l=0; l<8; l++ )
			      {
				  bx=w_width-(w_width-width)/2-((j*8+l)*2);
				  by=(w_height-height)/2+k*3;
				  if( (bmask[j][k]<<l & 128)==128)
				      g.fillRect(bx,by, 2, 3 );
			      }
			  
		      }
	  }

      // Red dot for middle mass mode
      if(mm==1)
	  {
	      if(mmx!=0 || mmy!=0)
		  {
		      g.setColor(Color.red);
		      g.fillRect((width-mmx*2)+(w_width/2)-width/2,(144-mmy)+(w_height/2)-height/2, 5, 5 );
		  }
	  }
      
    }
    
    
    
}
