package org.geotools.demo;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.FileNotFoundException;

import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import javax.swing.filechooser.FileFilter;

import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.gce.image.WorldImageReader;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.renderer.lite.gridcoverage2d.GridCoverageRenderer;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.RasterSymbolizerImpl;
import org.opengis.geometry.DirectPosition;

/**
 * This class is my first cut at rendering an image, please
 * use ImageLab2 as a good example.
 * 
 * @author Jody Garnett
 * @depreacted Please use ImageLab2 as an example
 */
public class ImageDisplay extends JFrame {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 5611889483925986034L;
	/** Original coverage we are working on */
	GridCoverage2D coverage;
	CoveragePanel panel;
	Zoom zoom;
	RasterSymbolizer style = new RasterSymbolizerImpl();
	ReferencedEnvelope bbox;
	
	/**
	 * Explore the functionality of the provided GridCoverage (think BufferedImage + CRS).
	 * <p>
	 * A GridCoverage literally a set of features that "covers"
	 * an area without gaps; in the case of grid coverage the area
	 * is covered by an regular grid.
	 * <p>
	 * Coverage work by letting you call a "sample" operation in order
	 * to retrieve a Record of the data at the location. A grid coverage
	 * lets you express the location using row and column.
	 * <p> 
	 * @param coverage
	 */
	public ImageDisplay(GridCoverage2D coverage){
		setResizable(false);
		setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		setTitle("ImageLab");
		this.coverage = coverage;
		this.bbox = new ReferencedEnvelope( coverage.getEnvelope() );
		this.panel = new CoveragePanel();
		add( panel );
		
		this.zoom = new Zoom();

		setGlassPane( zoom );
		zoom.setVisible(true);
		
		pack();
	}
	/**
	 * Take the clicks and update the bbox; implemented as a glass pane
	 * so we do not have to redraw the image.
	 */
	class Zoom extends JComponent implements MouseListener, MouseMotionListener {
		/**
		 * 
		 */
		private static final long serialVersionUID = 7936937113781962010L;
		Point point1;
		Point point2;		
		Zoom(){
			addMouseListener( this );
			addMouseMotionListener( this );
		}
		public void mouseClicked(MouseEvent squeek) {
			if( squeek.getClickCount() == 2 ){
				bbox = new ReferencedEnvelope( coverage.getEnvelope2D() );
				point1 = null;
				point2 = null;
			}
		}
		public void mouseEntered(MouseEvent squeek) {			
		}
		public void mouseExited(MouseEvent squeek) {			
		}
		public void mousePressed(MouseEvent squeek) {
			point1 = squeek.getPoint();
			repaint();
		}
		public void mouseReleased(MouseEvent squeek) {
			if( point1 != null && point2 != null &&
		        point1.distance(point2) > 5 ){
				DirectPosition2D location1 = toLocation( point1 );
				DirectPosition2D location2 = toLocation( point2 );
				if( location1 == null || location2 == null ) return;

				bbox = aspectRatioFix( location1, location2 );
				panel.repaint();
			}
			point1 = null;
			point2 = null;
			repaint();
		}
		
		public void mouseDragged(MouseEvent squeek) {			
			if( point1 != null ){
				point2 = squeek.getPoint();
				repaint();
			}
		}
		public void mouseMoved(MouseEvent squeek) {			
			
		}
		protected void paintComponent(Graphics g) {
			if( point1 != null && point2 != null ){
				Rectangle feedback = new Rectangle( point1 );
				feedback.add( point2 );
				
				g.setColor( Color.BLUE );
				g.drawRect( feedback.x, feedback.y, feedback.width, feedback.height );
				
				g.setColor( new Color( 0.0f, 0.0f, 1.0f,0.5f));
				g.fillRect(feedback.x, feedback.y, feedback.width, feedback.height );
			}			
		}
		public DirectPosition2D toLocation(Point point) {
			double w = (double) getWidth();
			double h = (double) getHeight();			
			double rx = ((double) point.x) / w;
			double ry = (h-(double) point.y) / h;
			
			DirectPosition start = bbox.getLowerCorner();
			DirectPosition2D location = new DirectPosition2D( coverage.getCoordinateReferenceSystem() );
			location.x = start.getOrdinate(0) + rx*bbox.getWidth();
			location.y = start.getOrdinate(1) + ry*bbox.getHeight();
			
			return location;
		}
		public ReferencedEnvelope aspectRatioFix( DirectPosition start, DirectPosition end ){
			ReferencedEnvelope request = new ReferencedEnvelope( coverage.getCoordinateReferenceSystem() );
			request.include( start.getOrdinate(0), start.getOrdinate(1) );
			request.include( end.getOrdinate(0), end.getOrdinate(1) );
			start = request.getLowerCorner();
			end = request.getUpperCorner();
			
			ReferencedEnvelope world = new ReferencedEnvelope( coverage.getEnvelope() );
			double ratio = world.getHeight() / world.getWidth();
			
			double w = request.getWidth();
			double h = request.getWidth() * ratio;
			
			ReferencedEnvelope envelope = new ReferencedEnvelope(coverage.getCoordinateReferenceSystem());
			envelope.include( start.getOrdinate(0), start.getOrdinate(1) );
			envelope.include( start.getOrdinate(0)+w, start.getOrdinate(1)+h );
			return envelope;
		}
	}

	class CoveragePanel extends JPanel {
		/**
		 * 
		 */
		private static final long serialVersionUID = 3956983132547799557L;

		CoveragePanel(){
			setBackground( Color.WHITE );
		}
		public Dimension getPreferredSize() {
			ReferencedEnvelope world = new ReferencedEnvelope( coverage.getEnvelope() );
			double ratio = world.getHeight() / world.getWidth();
			double w = 640.0;
			double h = 640.0 * ratio;
			return new Dimension( (int) w, (int) h );
		}
		
		public void paintComponent(Graphics graphics) {
			super.paintComponents( graphics );			
			Graphics2D g = (Graphics2D) graphics;			
			GridCoverageRenderer renderer;
			try {
				renderer = new GridCoverageRenderer( coverage.getCoordinateReferenceSystem(), bbox, getBounds() );
				renderer.paint( g, coverage, style );
			} catch (Exception e) {
				g.drawString( e.getLocalizedMessage(), 0, getHeight() / 2 );
			}
		}
	}
	
	/**
	 * Prompt the user for a file and open up ImageLab.
	 * @param args filename of image
	 */
	public static void main( String[] args ) throws Exception {
		File file = getImageFile( args );
		WorldImageReader reader = new WorldImageReader( file );
		GridCoverage2D coverage = (GridCoverage2D) reader.read(null);		
		
		ImageDisplay imageDisplay = new ImageDisplay( coverage );
		imageDisplay.setVisible(true);
	}
	
	private static File getImageFile(String[] args) throws FileNotFoundException {
		File file;
		if (args.length == 0) {
			JFileChooser chooser = new JFileChooser();
			chooser.setDialogTitle("Open Image file");
			chooser.setFileFilter(new FileFilter() {
				public boolean accept(File f) {
					return f.isDirectory() || f.getPath().endsWith("jpg")
							|| f.getPath().endsWith("JPG");
				}

				public String getDescription() {
					return "JPEG";
				}
			});
			int returnVal = chooser.showOpenDialog(null);

			if (returnVal != JFileChooser.APPROVE_OPTION) {
				System.exit(0);
			}
			file = chooser.getSelectedFile();

			System.out.println("Opening Image file: " + file.getName());
		} else {
			file = new File(args[0]);
		}
		if (!file.exists()) {
			throw new FileNotFoundException(file.getAbsolutePath());
		}
		return file;
	}
}
