import java.awt.*;
import java.text.*;
import java.io.*;
import myutil.*;
import ccj.*;

class Zip
{
    int zip;
    double lat;
    double lon;
    double x;    // x-coordinate in azimuthal projection
    double y;    // y-coordinate in azimuthal projection
}

public class ZipFinder2 extends Frame
{
    static int n;
    static Zip[] zips;
    static Zip myzip = null;

    public static synchronized void main(String[] args)
    {
        int i, zip, j, junk;

        /************************************************
         * Read in data. 
         ***********************************************/
    
	BufferedReader in;
	try {
	  in = new BufferedReader(new FileReader("zlla0.dat"));
	}
	catch (FileNotFoundException fe) {
		System.err.println("File not found");
		return;
	}

	n = FileIO.readInt(in);

	zips = new Zip[n];
	for (i=0; i<n; i++) {
	    if (i%500 == 0) {System.out.println(i);}
	    zips[i] = new Zip();
	    zips[i].zip = FileIO.readInt(in);
	    zips[i].lat = FileIO.readDouble(in);
	    zips[i].lon = -FileIO.readDouble(in); 
	    junk        = FileIO.readInt(in);
	}
    
        azimuthal();   // compute (x,y) coords in azimuthal proj
    
        /************************************************
         * Open graphics window
         ***********************************************/
    
	ZipFinder2 bs = new ZipFinder2();
	bs.resize(600,600);
	bs.show();
    
        /************************************************
         * Prompt user for a zip and then highlight it on the
         * screen
         ***********************************************/

        while (true) {
	    zip = Console.in.readInt("Enter a zip: ");
	    if (zip < 0) break;
    
            /* pick one */
            j = binsearch( zip );
            // j = brutesearch( zip );

            if ( j >= 0 ) {                             
		myzip = zips[j];
                Console.out.println("Found it "+myzip.lat+" "+
			myzip.lon); 
            } else {
                Console.out.println("Not found");       
		myzip = zips[-j];
                Console.out.println("How about "+myzip.zip+": "
			+myzip.lat+" "+ myzip.lon+"?"); 
            }
	    bs.paint(bs.getGraphics());
        }
    }

    public synchronized void paint (Graphics g)
    {
        double minx=1000, maxx=-1000, miny=1000, maxy=-1000;
	double mid;
        int i, zip, j;

        for (i=0; i<n; i++) {
            if (zips[i].y > maxy) maxy = zips[i].y;
            if (zips[i].y < miny) miny = zips[i].y;
        }
        for (i=0; i<n; i++) {
            if (zips[i].x > maxx) maxx = zips[i].x;
            if (zips[i].x < minx) minx = zips[i].x;
        }
    	maxx = maxx + 0.05*(maxx - minx);
    	minx = minx - 0.05*(maxx - minx);
    	maxy = maxy + 0.05*(maxy - miny);
    	miny = miny - 0.05*(maxy - miny);
    
    	if (maxx - minx > maxy - miny) {
    	    mid = (miny + maxy)/2;
    	    maxy = mid + (maxx - minx)/2;
    	    miny = mid - (maxx - minx)/2;
    	} else {
    	    mid = (minx + maxx)/2;
    	    maxx = mid + (maxy - miny)/2;
    	    minx = mid - (maxy - miny)/2;
    	}
    
	GL.ginit(g, size().width, size().height, this, false);
	// GL.ginit(g, size().width, size().height, this);
	GL.ortho2(minx, maxx, miny, maxy);

        /************************************************
         * Show all the points in red
         ***********************************************/
    
        GL.color(Color.black);
        GL.clear();
    
        for (i=0; i<n; i++) {
            GL.color(Color.red);
            GL.pnt2(zips[i].x, zips[i].y);
        }

	try { Thread.sleep(1000); } catch(InterruptedException ie){};

	if (myzip != null) {
	    GL.color(Color.yellow);
	    GL.pnt2(myzip.x, myzip.y);          
	    GL.drawString("HERE", myzip.x, myzip.y, "CENTER", "BOTTOM");
	}

	// GL.swapbuffers();
    }

    /****************************************************
     * binsearch(): find w in zips[0] <= zips[1] <= ... <= zips[n-1].
     *              negative if not found.
     ***************************************************/
    
    private static int binsearch(
        int   w         // item sought 
    )
    {
        int low, high, mid;
    
        low = 0; high = n-1;
        while ( low <= high ) {
            mid = (low+high)/2;
            if ( w < zips[mid].zip ) {
                high = mid - 1;            // in lower half
            } else if ( w > zips[mid].zip ) {
                low  = mid + 1;            // in upper half
            } else {
                return mid;                // found it
            }
        }
	if (high < 1) { high = 1; }
        return -high;                      // not found
    }

    /****************************************************
     * brutesearch(): find w in zips[0] <= zips[1] <= ... <= zips[n-1].
     *              negative if not found.
     ***************************************************/

    private static int brutesearch(
        int   w         // item sought
    )
        {
        int i;

        for (i=0; i<n; i++) {
            if ( w == zips[i].zip ) {
                return i;           // found it
            }
        }
        return -1;                  // not found
    }
    
    static void azimuthal()
    {
        int i;
        double xbar, ybar, zbar;
        double r, rho, theta, phi;
        double x1, y1, z1, x2, y2, z2;

        /****************************************************
         * Compute barycenter (in Euclidean coordinates).
         ***************************************************/
    
        xbar = 0;
        ybar = 0;
        zbar = 0;
        for (i=0; i<n; i++) {
            phi   = 2*Math.PI*(90-zips[i].lat)/360;
            theta = 2*Math.PI*zips[i].lon/360;
    
            xbar += Math.sin(phi)*Math.cos(theta);
            ybar += Math.sin(phi)*Math.sin(theta);
            zbar += Math.cos(phi);
        }
        xbar /= n;
        ybar /= n;
        zbar /= n;
    
        /****************************************************
         * Compute spherical coordinates of barycenter.
         ***************************************************/
    
        r = Math.sqrt(xbar*xbar + ybar*ybar);
        theta = Math.atan2(ybar, xbar);
    
        rho = Math.sqrt(xbar*xbar + ybar*ybar + zbar*zbar);
        phi = Math.atan2(r, zbar);
    
        /****************************************************
         * Compute unit tangent vector parallel to lat line.
         ***************************************************/
    
        x1 = -Math.sin(theta);
        y1 =  Math.cos(theta);
        z1 =  0;
    
        /****************************************************
         * Compute unit tangent vector parallel to lon line.
         ***************************************************/
    
        x2 = -Math.cos(phi)*Math.cos(theta);
        y2 = -Math.cos(phi)*Math.sin(theta);
        z2 =  Math.sin(phi);
    
        /****************************************************
         * The azimuthal coords are simply the inner products
         * of the cartesian coords with these unit vectors.
         ***************************************************/
    
        for (i=0; i<n; i++) {
            phi   = 2*Math.PI*(90-zips[i].lat)/360;
            theta = 2*Math.PI*zips[i].lon/360;
    
            xbar = Math.sin(phi)*Math.cos(theta);
            ybar = Math.sin(phi)*Math.sin(theta);
            zbar = Math.cos(phi);
    
            zips[i].x = xbar*x1 + ybar*y1 + zbar*z1;
            zips[i].y = xbar*x2 + ybar*y2 + zbar*z2;
        }
    }
}

