// Copyright 2001-2005, Danger, Inc.  All Rights Reserved.
// This file is subject to the Danger, Inc. Sample Code License,
// which is provided in the file SAMPLE_CODE_LICENSE.
// Copies are also available from http://developer.danger.com/

package com.example.DataStoreDemo;

import java.util.Vector;

import danger.app.Application;
import danger.app.Event;
import danger.app.EventType;
import danger.ui.Menu;
import danger.ui.MenuItem;
import danger.ui.ScreenWindow;
import danger.ui.ScrollView;
import danger.ui.StaticText;
import danger.ui.Table;
import danger.ui.TableCell;
import danger.util.DEBUG;

/**
 * Implements the main window class. It is created from a Screen resource. The
 * layout starts with the ScrollView, and the StaticText column headers.
 */
public class MainWindow extends ScreenWindow implements Resources, Commands {

	private static final int CELL_HEIGHTS = 10; // bo3b: based on fonts?

	/**
	 * These are hard coded width constants, which is generally undesirable. The
	 * Table is not resourceable though, so this is a compromise. Under 2.0
	 * resource files, the Integer format could be used, but this needs to work
	 * under 1.1 as well.
	 */
	private static final int NAME_WIDTH = 97; // column pixel width.

	private static final int SCORE_WIDTH = 25;

	private static final int LEVEL_WIDTH = 42;

	private static final int DATE_WIDTH = 67;

	/**
	 * Local reference for the application, to avoid the need for coercion.
	 */
	private static DataStoreDemo sApplication;

	/**
	 * The main scrolling view of HighScores. It is created from the resources,
	 * but is populated at run time. If there aren't enough scores to fill the
	 * screen, the scrollbar is invisible.
	 */
	private ScrollView mScrollView;

	/**
	 * The Table View object that shows the HighScores in a table format. 4
	 * columns across for each row that is a HighScore.
	 */
	private Table mHighScoreTable;

	// ------------------------------------------------------------------------
	/**
	 * This is called after the window has been created from resources. The
	 * reason to override this routine is to allow access to the screen Table,
	 * so it can be updated when the Scores change.
	 */
	public void onDecoded() {
		// Get a coerced reference to the current App for later use.
		sApplication = (DataStoreDemo) Application.getCurrentApp();

		// Find the ScrollView descendant that the Table will be stuffed into.
		// It also must be the main focus, so that it will scroll when needed.
		mScrollView = (ScrollView) getDescendantWithID(ID_SCROLL_VIEW);
		setFocusedChild(mScrollView);

		// Get the SettingsDB that specifies how the user left sorting, last
		// time around. If the parameter doesn't yet exist, it will default
		// to the programmatic by Score. Setup the UI to match that setting.
		changeSorting(Settings.getSortBy());

		super.onDecoded();
	}

	/**
	 * To handle a private shortcut, this routine overrides the Containers'
	 * routine, and looks for the Menu-X command. It's not user visible, and in
	 * this case is expected to only be useful for debugging purposes. It will
	 * reset a used DB back to the first run empty state. The Application does
	 * the work.
	 */
	public boolean eventShortcut(char shortcut, Event event) {
		if (shortcut == 'X') {
			sApplication.deleteAllRequest();
			DEBUG.p("MainWindow: EVENT_DELETE_ALL_SETTINGS");
			return true;
		}

		// The super must be called for unhandled shortcut events, otherwise
		// they will be dropped.
		return super.eventShortcut(shortcut, event);
	}

	/**
	 * Handles device buttons, specifically the Jump, Cancel, and Done buttons.
	 */
	public boolean eventWidgetUp(int inWhichWidget, Event inEvent) {

		switch (inWhichWidget) {
		// This demonstrates how to use a control from hiptop2 in a way that
		// is still compatible with original hiptop devices. The
		// DEVICE_BUTTON_CANCEL event is defined in the Hiptop2Events.java
		// file. On original hiptop devices, this event is never sent. On
		// hiptop2 devices, this event will be received and handled here.
		// Using the 2.0 APIs, this could be "Event.DEVICE_BUTTON_CANCEL"
		// instead.
		case Hiptop2Events.DEVICE_BUTTON_CANCEL:
			sApplication.returnToLauncher();
			return true;

		// For both the Cancel or Back/Done button from the MainWindow, it
		// will jump back to the launcher screen to provide a better
		// experience.
		case EventType.DEVICE_BUTTON_BACK:
			sApplication.returnToLauncher();
			return true;

		}

		// Always make sure to call super.eventWidgetUp for other events so the
		// system can execute the correct behavior (e.g. returning to the Jump
		// screen with phone selected).
		return (super.eventWidgetUp(inWhichWidget, inEvent));
	}

	/**
	 * Handles events. Called automatically whenever this window receives an
	 * event. The only events handled are related to the menu. All other events
	 * are handled automatically by the window, by calling super. 
	 */
	public boolean receiveEvent(Event e) {

		switch (e.type) {
		case EVENT_NEW_SCORE:
			HighScore fabScore = HighScore.fabricateHighScore(sApplication
					.getStringArray(ID_WINNER_NAMES));

			// Add the fabScore, and recreate the list so it shows up sorted.
			Scores.addScore(fabScore);
			createScoreTable();

			DEBUG.p("MainWindow: EVENT_NEW_SCORE  " + fabScore.getScore());
			return true;

		// For each of these events, the new menu choice forces a resort of the
		// DataStore, as well as changing the user-level setting for sorting
		// choice.
		case EVENT_SORT_SCORE:
			changeSorting(ID_MENU_SORT_SCORE);

			DEBUG.p("MainWindow: EVENT_SORT_SCORE");
			return true;

		case EVENT_SORT_NAME:
			changeSorting(ID_MENU_SORT_NAME);

			DEBUG.p("MainWindow: EVENT_SORT_NAME");
			return true;

		case EVENT_SORT_LEVEL:
			changeSorting(ID_MENU_SORT_LEVEL);

			DEBUG.p("MainWindow: EVENT_SORT_LEVEL");
			return true;

		case EVENT_SORT_DATE:
			changeSorting(ID_MENU_SORT_DATE);

			DEBUG.p("MainWindow: EVENT_SORT_DATE");
			return true;
		}

		return super.receiveEvent(e);
	}

	/**
	 * New sort choice. This is in response to user choice, changing the
	 * sorting. Thus, writing the "settings" is appropriate, unlike at startup.
	 */
	public void changeSorting(int choice) {
		setSortMenu(choice);
		Settings.setSortBy(choice);

		// User choice saved, resort the list, then update window.
		Scores.sort();
		createScoreTable();
	}

	/**
	 * Set the sort menu to the chosen item. Disable the item, and mark it to
	 * show it is the sorting item.
	 */
	public void setSortMenu(int sortBy) {
		Menu sortMenu;
		MenuItem theItem;
		int currentSortBy = Settings.getSortBy();

		// Get the submenu of the main menu which holds all sorting items.
		sortMenu = getActionMenu().getItemWithID(ID_MENU_SORT).getSubMenu();

		// Re-enable the current selection.
		theItem = sortMenu.getItemWithID(currentSortBy);
		theItem.enable();
		theItem.unmark();

		// Mark and disable the new choice.
		theItem = sortMenu.getItemWithID(sortBy);
		theItem.disable();
		theItem.mark();
	}

	/**
	 * Add a new high score record to the data list being displayed.
	 */
	public void addScoreToTable(HighScore score) {
		Vector aVector = new Vector(4); // four columns in each row
		TableCell aCell;

		// Winner Name
		aCell = new TableCell(mHighScoreTable);
		aCell.setElementToString(score.getName());
		aCell.setAlignment(StaticText.HALIGN_LEFT);
		aCell.setSize(NAME_WIDTH, CELL_HEIGHTS);
		aVector.addElement(aCell);

		// High Scores
		aCell = new TableCell(mHighScoreTable);
		aCell.setElementToInt(score.getScore());
		aCell.setAlignment(StaticText.HALIGN_RIGHT);
		aCell.setSize(SCORE_WIDTH, CELL_HEIGHTS);
		aVector.addElement(aCell);

		// High Level
		aCell = new TableCell(mHighScoreTable);
		aCell.setElementToInt(score.getLevel());
		aCell.setAlignment(StaticText.HALIGN_RIGHT);
		aCell.setSize(LEVEL_WIDTH, CELL_HEIGHTS);
		aVector.addElement(aCell);

		// Date, from Version specific string handling.
		aCell = new TableCell(mHighScoreTable);
		aCell.setElementToString(Version.getDateString(score.getDate()));
		aCell.setAlignment(StaticText.HALIGN_RIGHT);
		aCell.setSize(DATE_WIDTH, CELL_HEIGHTS);
		aVector.addElement(aCell);

		// The Vector is fully filled in, add it to the Table.
		mHighScoreTable.addRow(aVector);

		// AutoSize and CalcBottom must be called after table additions,
		// so that the owning ScrollView can properly account for size.
		mHighScoreTable.autoSize();
		mScrollView.calcBottom();
	}

	/**
	 * Rebuild this window's TableCell view to match the current state of the
	 * Scores. This can be called at any time, to rebuild the list.
	 */
	public void createScoreTable() {

		// Create the new Table view to show HighScores. There is no resource
		// definition for Table, so this is created here at runtime.
		mHighScoreTable = new Table();
		mHighScoreTable.show();

		// Remove all views from ScrollView, in case there already was a Table.
		mScrollView.removeAllChildren();

		// Add this new Table view as a child of the scroller.
		mScrollView.addChild(mHighScoreTable);
		mScrollView.setScrollDelta(CELL_HEIGHTS);

		// Add a new row to the Table for every HighScore value that is in
		// the DataStore. They were inserted into the DataStore sorted, so
		// they will be retrieved sorted.
		for (int i = 0; i < Scores.getRecordCount(); i++) {
			HighScore testScore = Scores.getScoreAt(i);
			DEBUG.p("score: " + testScore.getScore());

			// If the score = 0, this is a record type that this version of the
			// program does not understand. It could be a newer version of the
			// record, that this older version of software is reading. Skip
			// them.
			if (testScore.getScore() != 0) {
				addScoreToTable(testScore);
			}
		}
	}
}
