package com.isa.jump.plugin;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.List;

import javax.swing.JButton;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.util.Assert;
import com.vividsolutions.jump.workbench.WorkbenchContext;
import com.vividsolutions.jump.workbench.model.LayerManager;
import com.vividsolutions.jump.workbench.model.WMSLayer;
import com.vividsolutions.jump.workbench.plugin.PlugInContext;
import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
import com.vividsolutions.jump.workbench.ui.Viewport;

public class GoogleLayer extends WMSLayer 
{
	private static final double zoom0extentX = 360.0;
	private static final double zoom0extentY = 360.0;

	public GoogleLayer() 
	{

	}

	public GoogleLayer(PlugInContext context, List layerNames) throws IOException
	{  
		//this code copied from WMSLayer and AbstractLayerable constructors
		LayerManager layerManager = context.getLayerManager();
		String name = "Google Layer";
		Assert.isTrue(layerManager != null);

		setLayerManager(layerManager);  

		//Can't fire events because this Layerable hasn't been added to the
		//LayerManager yet. [Jon Aquino]
		boolean firingEvents = layerManager.isFiringEvents();
		layerManager.setFiringEvents(false);

		try 
		{
			setName(layerManager.uniqueLayerName(name));
		} 
		finally 
		{
			layerManager.setFiringEvents(firingEvents);
		}    

	}

	private int round(double num)
	{
		return (int)Math.round(num); 
	}

	public Image createImage(LayerViewPanel panel) throws IOException
	{        
		//view and panel refer to the workbench portion with which the user is interacting
		//raster refers to the visible portion of the JPG file drawn onto the view panel
		//image refers to the created image onto which is drawn the raster extracted from the JPG file
		totalBounds = new Envelope();
		BufferedImage newImage = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_ARGB);

		Graphics2D g = (Graphics2D)newImage.getGraphics();
		g.setColor(new Color(0,0,0,0)); //alpha channel = 0 for transparent background
		g.fillRect(0, 0, panel.getWidth(), panel.getHeight()); 

		Image image = null;

		Viewport viewport = panel.getViewport();
		Envelope vpEnvelope = viewport.getEnvelopeInModelCoordinates();       	

		int w = panel.getWidth();
		int h = panel.getHeight();
		int urlX = 512;
		int urlY = 512;
		int zoom = 0;

		if((h > 512) || (w > 512))
		{
			if(w > h)
			{
				urlX = 512;
				urlY = round(512 * ((double)h / (double)w));
			}
			else
			{
				urlY = 512;
				urlX = round(512 * ((double)w / (double)h));
			}
		}		

		Coordinate center = vpEnvelope.centre();   
		//DecimalFormat urlformatter = new DecimalFormat("###.######");
		zoom = CalculateZoomLevel(vpEnvelope);
		// The following variables will store the values halved when zoomed in both the x and y axis
		double imageExtentX = 2 * (zoom0extentX / (1 << zoom ));
		double imageExtentY = 2 * (zoom0extentY / (1 << zoom ));
		String url = "http://maps.google.com/staticmap?center=" + (center.y) + "," + (center.x) + "&markers=" + (center.y) + "," + (center.x);
		url += "&zoom=" + zoom + "&size=" + urlX  + "x" + urlY + "&key=";  //add your Google Maps key after the =

		image = Toolkit.getDefaultToolkit().getImage(new java.net.URL (url));

		MediaTracker mt = new MediaTracker(new JButton());
		mt.addImage(image, 0);

		try {
			mt.waitForID(0);
		} catch (InterruptedException e) {
			Assert.shouldNeverReachHere();
		}

		int jpgPixelWidth = image.getWidth(panel);
		int jpgPixelHeight = image.getHeight(panel);
		double jpg_xres = imageExtentX / jpgPixelWidth; // units are degrees per/pixel
		resolution = jpg_xres;
		double jpg_ulx = center.x - imageExtentX / 2.0; //realworld coords
		double jpg_uly = center.y + imageExtentY / 2.0; //realworld coords

		int image_x = 0; //x position of raster in final image in pixels
		int image_y = 0; //y position of raster in final image in pixels
		int image_w = panel.getWidth(); //width of raster in final image in pixels
		int image_h = panel.getHeight(); //height of raster in final image in pixels


		double view_res = 1 / viewport.getScale(); //panel resolution
		double rwViewLeft = vpEnvelope.getMinX();
		double rwViewRight = vpEnvelope.getMaxX();
		double rwViewTop = vpEnvelope.getMaxY();
		double rwViewBot = vpEnvelope.getMinY();

		//Here calculate the real world jpg edges.
		//NOTE: world file coordinates are center of pixels
		double halfPixel = 0.5 * jpg_xres;
		double rwJpgFileLeftEdge = jpg_ulx - halfPixel;
		double rwJpgFileRightEdge = rwJpgFileLeftEdge + (jpgPixelWidth * jpg_xres);
		double rwJpgFileTopEdge = jpg_uly + halfPixel;
		double rwJpgFileBotEdge = rwJpgFileTopEdge - (jpgPixelHeight * jpg_xres);

		totalBounds.expandToInclude(rwJpgFileLeftEdge, rwJpgFileBotEdge);
		totalBounds.expandToInclude(rwJpgFileRightEdge, rwJpgFileTopEdge);

		double rwRasterLeft = Math.max(rwViewLeft, rwJpgFileLeftEdge);
		double rwRasterRight = Math.min(rwViewRight, rwJpgFileRightEdge);
		double rwRasterTop = Math.min(rwViewTop, rwJpgFileTopEdge);
		double rwRasterBot = Math.max(rwViewBot, rwJpgFileBotEdge);
		
		//calculate which pixels in the jpg file fit inside the view
		int jpgLeftPixel = (int)((rwRasterLeft - rwJpgFileLeftEdge) / jpg_xres); //trunc
		int jpgRightPixel = (int)((rwRasterRight - rwJpgFileLeftEdge) / jpg_xres); //trunc
		if (jpgRightPixel == jpgPixelWidth) jpgRightPixel = jpgPixelWidth - 1;
		int jpgTopPixel = (int)((rwJpgFileTopEdge - rwRasterTop) / jpg_xres); //trunc
		int jpgBotPixel = (int)((rwJpgFileTopEdge - rwRasterBot) / jpg_xres); //trunc
		if (jpgBotPixel == jpgPixelHeight) jpgBotPixel = jpgPixelHeight - 1;

		//calculate the real world coords of the included pixels
		double rwJpgLeft = rwJpgFileLeftEdge + (jpgLeftPixel * jpg_xres);
		double rwJpgRight = rwJpgFileLeftEdge + (jpgRightPixel * jpg_xres) + jpg_xres;
		double rwJpgTop = rwJpgFileTopEdge - (jpgTopPixel * jpg_xres);
		double rwJpgBot = rwJpgFileTopEdge - (jpgBotPixel * jpg_xres) - jpg_xres;

		//calculate the pixel offset on the panel of the included portion of the jpg file
		int leftOffset = round((rwRasterLeft - rwJpgLeft) / view_res);
		int rightOffset = round((rwJpgRight - rwRasterRight) / view_res);
		int topOffset = round((rwJpgTop - rwRasterTop) / view_res);
		int botOffset = round((rwRasterBot - rwJpgBot) / view_res);

		image_x = round(rwRasterLeft / view_res) - round(rwViewLeft / view_res);
		image_w = round(rwRasterRight / view_res) - round(rwRasterLeft / view_res);
		if (image_w <= 0) image_w = 1;

		image_y = round(rwViewTop / view_res) - round(rwRasterTop / view_res);
		image_h = round(rwRasterTop / view_res) - round(rwRasterBot / view_res);
		if (image_h <= 0) image_h = 1;

		image_x -= leftOffset;
		image_y -= topOffset;
		image_w += (leftOffset + rightOffset);
		image_h += (topOffset + botOffset);

		g.setComposite(AlphaComposite.Src);
		//parameters: destination corners then source corners
		//source corners are defined in terms of infinitely thin coordinates
		//which define the edges of the pixel space so that we have
		//to add 1 to the right bottom coordinate of the source rectangle
		//since jpgRightPixel & jpgBotPixel are defined in terms of array element position
		//any questions, see Java documentation for Graphics object
		g.drawImage(image, image_x, image_y, image_x + image_w, image_y + image_h, jpgLeftPixel, jpgTopPixel, jpgRightPixel + 1, jpgBotPixel + 1, panel);
		//g.drawImage(image,0, 0, panel.getWidth(), panel.getHeight(), panel);


		return newImage;
	}

	public int CalculateZoomLevel(Envelope e)
	{
		int zoomLevel = 0;

		if(e.getWidth() > e.getHeight())
		{
			double zoomExtentX = zoom0extentX;

			while((zoomExtentX >= e.getWidth()) || (zoomLevel == 19))
			{
				zoomLevel++;
				zoomExtentX /= 2.0;
			}
		}
		else
		{
			double zoomExtentY = zoom0extentY;

			while((zoomExtentY >= e.getHeight()) || (zoomLevel == 19))
			{
				zoomLevel++;
				zoomExtentY /= 2.0;
			}
		}		

		return zoomLevel == 0 ? 0 : zoomLevel - 1; 
	}
}


