import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
import Geometry.NVector;

/**
 * A simple two dimensional view of the optimization program in progress. 
 * Written by Chris Studholme
 * Licenced for redistribution under the GNU General Public Licence
 */
class GraphicalView extends Canvas {

    /** Initial width of canvas.
     */
    public static final int INITAL_WIDTH=500;
    /** Initial height of canvas.
     */
    public static final int INITAL_HEIGHT=500;

    /** Current best (lowest error) parameter set.
     */
    PointParameters params;

    /** Observed data set.
     */
    ObservedPoints data;

    /** Array of colors to use for surface points and lines.
     */
    Color[] colors;
    /** Number of valid colors in colors array.
     */
    int ncolors;

    /**
     * Initialize canvas and colors array.
     */
    public GraphicalView() {
	super();
	setBackground(Color.white);
	setForeground(Color.black);
	setSize(INITAL_WIDTH,INITAL_HEIGHT);
	ncolors=24;
	colors = new Color[ncolors];
        int pos=0;
        for (int i=0; i<3; ++i)
            for (int j=0; j<3; ++j)
                for (int k=0; k<3; ++k)
                    if ((i!=j)||(j!=k))
                        colors[pos++] = new Color(0.25f*i,0.25f*j,0.25f*k);
    }

    /**
     * Signal that the display should be updated with a new best (lowest error)
     * parameter set.
     * @param params new parameter set
     */
    public void update(PointParameters params) {
	this.params = params;
	repaint(100);
    }

    /**
     * Paint the canvas.
     * @param g graphics object to use
     */
    public void paint(Graphics g) {
	super.paint(g);
	if (params==null)
	    return;

	int W = getWidth();
	int H = getHeight();

	// compute point array
	NVector[] points = params.points;
	int npoints = 0;
	while ((npoints<points.length)&&(points[npoints]!=null))
	    ++npoints;
	
	double minx=params.photos[0].pos.component[0];
	double maxx=params.photos[0].pos.component[0];
	double miny=params.photos[0].pos.component[1];
	double maxy=params.photos[0].pos.component[1];
	for (int i=1; i<params.nphotos; ++i) {
	    if (params.photos[i].pos.component[0]<minx) 
		minx=params.photos[i].pos.component[0];
	    if (params.photos[i].pos.component[0]>maxx) 
		maxx=params.photos[i].pos.component[0];
	    if (params.photos[i].pos.component[1]<miny) 
		miny=params.photos[i].pos.component[1];
	    if (params.photos[i].pos.component[1]>maxy) 
		maxy=params.photos[i].pos.component[1];
	}
	for (int i=0; (i<npoints); ++i) {
	    if (points[i].component[0]<minx) minx=points[i].component[0];
	    if (points[i].component[0]>maxx) maxx=points[i].component[0];
	    if (points[i].component[1]<miny) miny=points[i].component[1];
	    if (points[i].component[1]>maxy) maxy=points[i].component[1];
	}

	double wx = W/(maxx-minx);
	double wy = H/(maxy-miny);
	double xofs = 0;
	double yofs = 0;

	if (wx<wy) {
	    wy=wx;
	    yofs=(H-wy*(maxy-miny))/2;
	}
	else {
	    wx=wy;
	    xofs=(W-wx*(maxx-minx))/2;
	}

	// draw points
	for (int i=0; i<npoints; ++i) {
	    g.setColor(colors[i%ncolors]);
	    int x = (int)(xofs+(points[i].component[0]-minx)*wx);
	    int y = (int)(yofs+(maxy-points[i].component[1])*wy);
	    g.drawOval(x-3,y-3,6,6);
	}

	// draw cameras
	for (int i=0; i<params.nphotos; ++i) {
	    int x = (int)(xofs+(params.photos[i].pos.component[0]-minx)*wx);
	    int y = (int)(yofs+(maxy-params.photos[i].pos.component[1])*wy);
	    g.setColor(Color.black);
	    g.drawOval(x-5,y-5,10,10);
	    for (int j=0; j<npoints; ++j) {
		g.setColor(colors[j%ncolors]);
		// find pixel in data.photos[i]'s list
		for (int k=0; k<data.photos[i].npoints; ++k)
		    if (data.points[j].
			equals(data.photos[i].points[k].point)) {
			// project point
			NVector px = data.photos[i].points[k].pixel;
			NVector p2 =
			    params.photos[i].
			    projectPixel(px.scale(params.getWidth()));
			double dist =
			    points[j].subtract(params.photos[i].pos).
			    innerProduct(p2);
			p2 = p2.scale(dist).add(params.photos[i].pos);
			int xp = (int)(xofs+(p2.component[0]-minx)*wx);
			int yp = (int)(yofs+(maxy-p2.component[1])*wy);
			g.drawLine(x,y,xp,yp);
			break;
		    }
	    }
	}
	
    }
}
