import java.text.NumberFormat;
import java.text.ParseException;

public class GoogleTile {
	public static final int TILE_WIDTH = 256;
	public static final int TILE_HEIGHT = 256;
	private static final double PI = 3.1415926535;
	private int tileSize = 256;
	private static final double[] pixelsPerLonDegree = new double[18];
	private static final double[] pixelsPerLonRadian = new double[18];
	private static final double[] numTiles = new double[18];
	private static final Coordinate[] bitmapOrigo = new Coordinate[18];
	// Note: These variable names are based on the variables names found in the
	// Google maps.*.js code.
	private static int c = 256;
	private static final double bc = 2 * PI;
	private static final double Wa = PI / 180;

	// The point (x,y) for this tile
	private Coordinate googleCoordinates;
	// The coord (lat,lon) for this tile
	private Coordinate mapCoordinates;
	// Zoom level for this tile
	private int zoom;

	static {
		for (int d = 17; d >= 0; --d) {
			pixelsPerLonDegree[d] = c / 360;
			pixelsPerLonRadian[d] = c / bc;
			double e = c / 2;
			bitmapOrigo[d] = new Coordinate(e, e);
			numTiles[d] = c / 256;
			c *= 2;
		}
	}

	public GoogleTile() {
		super();
	}

	public static void main (String args[]) {
		NumberFormat nf = NumberFormat.getInstance();
		nf.setMaximumFractionDigits(0);
		nf.setGroupingUsed(false);

		double  lat, lon;
		int zoom;
		double sx=0;
		double sy=0;
		try {
			lat=nf.parse(args[0]).doubleValue();
			lon=nf.parse(args[1]).doubleValue();
			zoom=nf.parse(args[2]).intValue();
			try {
			if (args[3]!=null)
				sx=nf.parse(args[3]).doubleValue();
			if (args[4]!=null)
				sy=nf.parse(args[4]).doubleValue();
			} catch (Exception ex) {};
			
		} catch (ParseException pe) {
			System.out.println("error=could not parse input");
			return;
		}
		GoogleTile gt=new GoogleTile(lat ,lon,zoom);
		Coordinate c = gt.getTileCoord();	
        	System.out.println("x="+nf.format(c.getX()));
        	System.out.println("y="+nf.format(c.getY()));
		//System.out.println("url=\"http://mt.google.com/mt?v=w2.5&x=" + nf.format(c.getX()) + "&y=" + nf.format(c.getY()) + "&zoom=" + zoom+"\";");
        	//System.out.println("keyhole="+gt.getKeyholeString()+";");
        	//System.out.println("http://kh1.google.com/kh?n=404&v=7&t="+gt.getKeyholeString());
		c = gt.getTileLatLong();	
        	System.out.println("tileLat="+c.getY()+";");
        	System.out.println("tileLon="+c.getX()+";");

		c = gt.getTileOffset(lat ,lon,zoom);

		System.out.println("offsetX:="+nf.format(c.getX()));
                System.out.println("offsetY:="+nf.format(c.getY()));

		if (sx!=0 && sy!=0) {
        		System.out.println("keyhole="+gt.getKeyholeString(sx,sy));
		}
			
    	}


	public GoogleTile(double latitude, double longitude, int zoomLevel) {
		super();
		zoom = zoomLevel;
		googleCoordinates = getTileCoordinate(latitude, longitude, zoomLevel);
		mapCoordinates = getLatLong(latitude, longitude, zoomLevel);
	}

	private Coordinate getBitmapCoordinate(double latitude, double longitude,
			int zoomLevel) {
		Coordinate d = new Coordinate(0, 0);

		d.x = Math.floor(bitmapOrigo[zoomLevel].x + longitude
				* pixelsPerLonDegree[zoomLevel]);
		double e = Math.sin(latitude * Wa);

		if (e > 0.9999) {
			e = 0.9999;
		}

		if (e < -0.9999) {
			e = -0.9999;
		}

		d.y = Math.floor(bitmapOrigo[zoomLevel].y + 0.5
				* Math.log((1 + e) / (1 - e)) * -1
				* (pixelsPerLonRadian[zoomLevel]));
		return d;
	}

	private Coordinate getTileCoordinate(double latitude, double longitude,
			int zoomLevel) {
		Coordinate d = getBitmapCoordinate(latitude, longitude, zoomLevel);
		d.x = Math.floor(d.x / tileSize);
		d.y = Math.floor(d.y / tileSize);
		return d;
	}

	public Coordinate getTileOffset(double latitude, double longitude,
			int zoomLevel) {
		Coordinate d = getBitmapCoordinate(latitude, longitude, zoomLevel);
		d.x = Math.floor(d.x % tileSize);
		d.y = Math.floor(d.y % tileSize);
		return d;
	}

	private Coordinate getLatLong(double latitude, double longitude,
			int zoomLevel) {
		Coordinate d = new Coordinate(0, 0);
		Coordinate e = getBitmapCoordinate(latitude, longitude, zoomLevel);
		latitude = e.x;
		longitude = e.y;

		d.x = (latitude - bitmapOrigo[zoomLevel].x)
				/ pixelsPerLonDegree[zoomLevel];
		double e1 = (longitude - bitmapOrigo[zoomLevel].y)
				/ (-1 * pixelsPerLonRadian[zoomLevel]);
		d.y = (2 * Math.atan(Math.exp(e1)) - PI / 2) / Wa;
		return d;
	}

	public Coordinate getTileCoord() {
		return googleCoordinates;
	}

	public Coordinate getTileLatLong() {
		return mapCoordinates;
	}

	public String getKeyholeString() {
		String s = "";
		double myX = googleCoordinates.x;
		double myY = googleCoordinates.y;

		for (int i = 17; i > zoom; i--) {
			double rx = (fmod(myX, 2));
			myX = Math.floor(myX / 2);
			double ry = (fmod(myY, 2));
			myY = Math.floor(myY / 2);
			s = getKeyholeDirection(rx, ry) + s;
		}
		return 't' + s;
	}

	public String getKeyholeString(double x, double y) {
		String s = "";
		double myX = x;
		double myY = y;

		for (int i = 17; i > zoom; i--) {
			double rx = (fmod(myX, 2));
			myX = Math.floor(myX / 2);
			double ry = (fmod(myY, 2));
			myY = Math.floor(myY / 2);
			s = getKeyholeDirection(rx, ry) + s;
		}
		return 't' + s;
	}

	private double fmod(double myX, int i) {
		return myX % i;
	}

	public String getKeyholeDirection(double x, double y) {
		if (x == 1) {
			if (y == 1) {
				return "s";
			} else if (y == 0) {
				return "r";
			}
		} else if (x == 0) {
			if (y == 1) {
				return "t";
			} else if (y == 0) {
				return "q";
			}
		}

		return "";
	}
}


