package todo;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

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.PositionHelper;

/**
 * 
 * @author finsprings
 *
 */
public class MyApplication {	
	private static final String NEAREST = "NEAREST";
	private static final long PERIOD = 3000;
	private LatLon currentLocation;
	private Frame frame;
	private Label currentTodoName;
	private Label currentTodoDistance;
	private final Timer timer = new Timer();
	private IModuleDisplay display;
	private IPositionProvider position;
	private final java.util.List todos = new ArrayList();
	private TodoList currentTodoList;
	private TodoList lastTodoList;
	private final DecimalFormat distanceFormat = new DecimalFormat("##0.00;-##0.00");
	private java.awt.List items;
	private boolean chooseNearest = true;
	
	public MyApplication() {
		loadTodos();
	}
	
	private void loadTodos() {
		// TODO: get todo lists from file
		
		{
			// Canned Home todo list
			final LatLon location = new LatLon();
			location.latitude = 40.744194444444446;
			location.longitude = -73.99397222222223;
			final TodoList todo = new TodoList( "home", location );
			todo.addItem( "clean up garage" );
			todo.addItem( "clear leaves" );
			todo.addItem( "wash the dishes" );
			todos.add( todo );
		}
		
		{
			// Canned Work todo list
			final LatLon location = new LatLon();
			location.latitude = 40.744638888888886;
			location.longitude = -73.99536111111111;
			final TodoList todo = new TodoList( "work", location );
			todo.addItem( "read email" );
			todo.addItem( "surf the web" );
			todo.addItem( "research cake" );
			todos.add( todo );
			
		}
		
		{
			// Canned food todo list
			final LatLon location = new LatLon();
			location.latitude = 40.743611111111115;
			location.longitude = -73.99591666666667;
			final TodoList todo = new TodoList( "supermarket", location );
			todo.addItem( "eggs" );
			todo.addItem( "milk" );
			todo.addItem( "bread" );
			todo.addItem( "cake" );
			todo.addItem( "more cake" );
			todos.add( todo );
			
			// make this the default list for now
			currentTodoList = todo;
		}
	}
	
	private void saveTodos() {
		// TODO: save todo lists to file (put them in a serializable object? or be clean and use XML?)
	}
	

	/**
	 * Start ourselves up.
	 * 
	 * @param display handle to the display service so we can make our UI
	 * @param position handle to the position service (GPS)
	 */
	public void start(IModuleDisplay display, IPositionProvider position) {
		this.display = display;
		this.position = position;
		System.out.println( "todo: app starting" );
		
		createUI();
		update();
		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( "todo: app started" );
	}
	
	/**
	 * Shut ourselves down.
	 */
	public void stop() {
		System.out.println( "todo: app stopping" );
		
		// make sure we don't try and update again
		timer.cancel();
		
		saveTodos();
		
		// get rid of our UI
		frame.dispose();
		
		System.out.println( "todo: app stopped" );
	}

	/**
	 * Build our ugly UI.
	 */
	private void createUI() {
		frame = display.getFrame();
		frame.removeAll();
		
		// build menu
		final Menu menu = new Menu( "Choose a Todo List" );
		
		// special item that means the todo list for the nearest location
		final MenuItem nearest = new MenuItem( NEAREST );
		nearest.addActionListener(new ActionListener() {
			public void actionPerformed( ActionEvent e ) {
				MyApplication.this.chooseNearest = true;
			}
		});
		menu.add( nearest );
		menu.addSeparator();
		
		final Iterator iter = todos.iterator();
		while ( iter.hasNext() ) {
			final TodoList todoList = (TodoList) iter.next();
			final MenuItem menuItem = new MenuItem( todoList.getName() );
			menuItem.addActionListener(new ActionListener() {
				public void actionPerformed( ActionEvent e ) {
					MyApplication.this.setTodoList( ( (MenuItem) e.getSource() ).getLabel() );
				}
			});
			menu.add( menuItem );
		}
		final MenuBar menuBar = new MenuBar();
		menuBar.add( menu );
		frame.setMenuBar( menuBar );
		
		frame.setLayout( new BorderLayout() );
		
		final Panel info = new Panel();
		info.setLayout( new BorderLayout() );

		currentTodoName = new Label();
		info.add( currentTodoName, BorderLayout.CENTER );
		
		currentTodoDistance = new Label();
		info.add( currentTodoDistance, BorderLayout.EAST );
		
		frame.add( info, BorderLayout.NORTH );
		items = new java.awt.List();
		frame.add( items, BorderLayout.CENTER );
	}
	
	// can be called from timer or by user choosing menu item, so sync it
	private synchronized void updateUI() {
		
		if ( lastTodoList != currentTodoList ) {
			System.out.println( "Updating UI for new list " + 
					currentTodoList.getName() + " with " +
					currentTodoList.getItems().size() + " items." );
			
			// we've just switched lists, so update the list contents too
			items.removeAll();
			final Iterator iter = currentTodoList.getItems().iterator();
			while ( iter.hasNext() ) {
				final String item = (String) iter.next();
				items.add( item );
			}
			
			lastTodoList = currentTodoList;
		}
		
		// always update the name (in case the user switched to/from nearest
		// but ended up on the same todo list, in which case we want to add/remove
		// the " (Nearest)"
		currentTodoName.setText( currentTodoList.getName() +
				( chooseNearest ? " (Nearest)" : "" ));
		
		// always update the distance
		final String distance;
		if (currentLocation != null) {
			distance = distanceFormat.format(
					PositionHelper.kilometersToMiles(
							PositionHelper.calculateDistance(
									currentTodoList.getLocation(),
									currentLocation )));			
		} else {
			distance = "?";
		}
		
		currentTodoDistance.setText( distance + " miles");
		System.out.println("Distance now " + distance);
	}
	

	/**
	 * Our callback when our update timer fires. We
	 * need to get our current position and update our UI.
	 */
	private void update() {
		System.out.println( "todo: updating" );
		
		// save where we are now
		currentLocation = position.getLatitudeLongitude();
		
		if (currentLocation != null) {
			System.out.println( "current lat:" + currentLocation.latitude );
			System.out.println( "current lon:" + currentLocation.longitude );
			
			if ( chooseNearest ) {
				updateCurrentTodoList();
			}
			
			updateUI();			
		}
	}
	
	private void updateCurrentTodoList() {
		if (currentLocation != null) {
			lastTodoList = currentTodoList;
			
			// save how far we are from the current todo list
			double currentDistance = PositionHelper.calculateDistance(
					currentTodoList.getLocation(),
					currentLocation);
			
			// update closest todo
			final Iterator iter = todos.iterator();
			while ( iter.hasNext() ) {
				final TodoList todoList = (TodoList) iter.next();
				final double distance = PositionHelper.calculateDistance(
						todoList.getLocation(),
						currentLocation);
				
				System.out.println("Distance to " + todoList.getName() + ": " + distance);
				if ( distance < currentDistance ) {
					System.out.println( "Switching to " + todoList.getName() );
					currentDistance = distance;
					currentTodoList = todoList;
				}
			}
		}
	}
	
	private TodoList findTodoList( final String name ) {
		final Iterator iter = todos.iterator();
		while ( iter.hasNext() ) {
			final TodoList todoList = (TodoList) iter.next();
			if ( todoList.getName().equals( name ) ) {
				return todoList;
			}
		}
		
		return null;
	}
	
	private void setTodoList( final String name ) {
		final TodoList todoList = findTodoList( name );
		if ( todoList != null ) {
			System.out.println( "Manually switching to " + name );
			chooseNearest = false;
			currentTodoList = todoList;
			updateUI();
		}
	}
}