package gpslogger;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.Toolkit;
import java.awt.Image;
import java.text.DecimalFormat;
import java.util.Timer;
import java.util.TimerTask;
import java.net.MalformedURLException;
import java.net.URL;

import com.buglabs.bug.module.gps.pub.IPositionProvider;
import com.buglabs.bug.module.gps.pub.LatLon;
import com.buglabs.bug.module.lcd.pub.IModuleDisplay;

import util.GoogleTile;
import util.GoogleTileURL;
import util.ImageCanvas;
import util.PositionHelper;

/**
 * This is the meat of our application. It will be only be created
 * once the services it uses exist.
 * 
 * @author finsprings
 *
 */
public class MyApplication {	
	/**
	 * The time, in milliseconds, between updates of our position.
	 */
	private static final long PERIOD = 3000;
	
	/**
	 * The zoom level we want for our Google Tiles.
	 */
	private final int TILE_ZOOM_LEVEL = 2;
	
	/**
	 * The starting position of our route.
	 */
	private LatLon startLatLon = null;
	
	/**
	 * Our current position.
	 */
	private LatLon currentLatLon;
	
	/**
	 * The frame into which we'll stuff our UI.
	 */
	private Frame frame;
	
	/**
	 * The panel where we'll stick our positions and distance traveled.
	 */
	private Panel topPanel;
	
	/**
	 * The panel where we'll stick the Google Tile.
	 */
	private Panel bottomPanel;
	
	/**
	 * The field we'll display our start position in.
	 */
	private TextField startPosition;
	
	/**
	 * The field we'll display our current position in.
	 */
	private TextField currentPosition;

	/**
	 * The field we'll display the distance traveled in.
	 */
	private TextField distance;
	
	/**
	 * A helper to let us get the Google Tile onto our UI.
	 */
	private Toolkit tk;
	
	/**
	 * A canvas to stick the Google Tile image on.
	 */
	private ImageCanvas canvas;
	
	/**
	 * The format for the distance we display so we don't show tedious
	 * numbers of digits after the decimal point.
	 */
	private final DecimalFormat distanceFormat = new DecimalFormat("##0.00;-##0.00");
	
	/**
	 * The timer that will ping us every time we want to update the UI.
	 */
	private final Timer timer = new Timer();
	
	/**
	 * Our access to the display.
	 */
	private final IModuleDisplay display;
	
	/**
	 * Our access to our current position from the GPS.
	 */
	private final IPositionProvider position;

	/**
	 * Our constructor. We just save the service handles.
	 *  
	 * @param display handle to the display service so we can make our UI
	 * @param position handle to the position service (GPS)
	 */
	public MyApplication(IModuleDisplay display, IPositionProvider position) {
		this.display = display;
		this.position = position;
	}

	/**
	 * Start ourselves up.
	 */
	public void start() {
		System.out.println( "GpsLogger: app starting" );
		
		createUI();
		
		frame.show();
		
		// Set up our timer to fire every PERIOD ms and call our update method.
		timer.schedule( new TimerTask() {
			public void run() {
				MyApplication.this.update();
			}
		}, 0, PERIOD );
		
		System.out.println( "GpsLogger: app started" );
	}
	
	/**
	 * Shut ourselves down.
	 */
	public void stop() {
		System.out.println( "GpsLogger: app stopping" );
		
		// make sure we don't try and update again
		timer.cancel();
		
		// get rid of our UI
		frame.dispose();
		
		System.out.println( "GpsLogger: app stopped" );
	}

	/**
	 * Build our ugly UI.
	 */
	private void createUI() {
		frame = display.getFrame();
		frame.setTitle("GpsLogger");
		frame.setLayout( new BorderLayout() );
		
		topPanel = new Panel();
		topPanel.setLayout( new GridLayout( 3, 2) );

		topPanel.add( new Label( "Start Position:" ));
		startPosition = new TextField();
		topPanel.add( startPosition );
		
		topPanel.add(new Label( "Current Position:" ));
		currentPosition = new TextField();
		topPanel.add( currentPosition );
		
		topPanel.add(new Label( "Distance Travelled:" ));
		distance = new TextField();
		topPanel.add( distance );
		
		frame.add( topPanel, BorderLayout.NORTH );

		tk = Toolkit.getDefaultToolkit();
		canvas = new ImageCanvas();

		bottomPanel = new Panel();
		bottomPanel.setLayout( new BorderLayout() );
		bottomPanel.add(canvas, BorderLayout.CENTER );
		
		// I can't remember any AWT stuff so this looks
		// pretty bad on-screen.
		frame.add( bottomPanel, BorderLayout.CENTER );
	}

	/**
	 * Our callback when our update timer fires. We
	 * need to get our current position and update our UI.
	 */
	private void update() {
		System.out.println( "GpsLogger: updating position" );
		try {
			// update where we are
			currentLatLon = position.getLatitudeLongitude();
			if (currentLatLon == null) { 
				System.err.println("Unable to update position: got null position back.");
				return;
			}
		} catch (Exception e) {
			System.err.println("Unable to get position because of:");
			e.printStackTrace();
			return;
			
		}
		
		currentPosition.setText( PositionHelper.latLonToString( currentLatLon ) );
		
		// if this is the first time we've gotten a fix then
		// set our start position as well
		if (startPosition.getText().length() == 0) {
			System.out.println("Setting start info (" + startLatLon +
					" / " + startPosition);
			startLatLon = currentLatLon;
			startPosition.setText(currentPosition.getText());
		}
		
		// update how far we've come
		final double d = PositionHelper.calculateDistance( startLatLon, currentLatLon );
		distance.setText( "" + distanceFormat.format( d ) + " km" );
		
		// update the tile for where we are
		final GoogleTile tile = new GoogleTile(
				currentLatLon.latitude, currentLatLon.longitude, TILE_ZOOM_LEVEL );

		try {
			final Image img = tk.createImage(new URL( GoogleTileURL.url( tile ) ) );
			canvas.setImage( img );
		}
		catch ( MalformedURLException e ) {
			System.err.println( "Failed to update Google Tile: " + e.getMessage() );
		}
	}
}