// 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.SettingsDemo;

/**
 This sample program has been developed specifically to demonstrate the
 use of the SettingsDB class.  It will perform other operations related to
 that goal, like having a UI SettingsScreen, but will not use or demonstrate
 full Application functionality.  This will keep the sample shorter and simpler,
 and more focused upon the interesting piece, the Settings object.

 For a more complete Application example, please refer to the HelloWorld/Template app.

 The code base is specifically designed to start with the 1.1 source as the default.
 It is forward compatible with all other versions, including 2.0, 2.1 and 2.3.  
 Because the goal is to keep one simplified code base, the program does not use
 any 2.0 or greater APIs.
 Newer versions are compiled using build variables (e.g. -DLIB_OS_VERSION="2.3" 
 -DBUILD_NUMBER="90526")

 This sample demonstrates the following SettingsDB techniques:
 1) Creation of SettingsDB object, without having to save to the service.
 2) Programmatic defaults, so that defaults are not saved to the service.
 3) Proper set/get operations for each item saved in the SettingsDB.
 4) Proper DATASTORE_RESTORED event handling, for hard-resets.
 5) General:Settings dialog, to save user-level settings.
 6) Settings clearAll() to release all DB use.
 7) RingTonePicker state saving and restoring, including 2.0 compatible version.


 The sample saves two distinct kinds of data: 
 1) User level settings data derived from a General:Settings dialog
 2) Programmatic data, in this case a 'high-score' value.
 Both are set and used throughout to show the slightly different handling.

 There is a notable bug in the 2.3 version of the simulator.  When Save Datastores
 is enabled, the settings will be saved as expected, simulating the battery backed
 up RAM of a normal SettingsDB.  (If Save Datastores is unchecked, no settings will
 be saved over reboots.)  When Save Datastores is enabled, the datastores will be 
 saved in the working directory (usually the sdk root), and the 2.3 version will 
 cause the 2.0 or 1.1 simulator to crash with an out of memory error.  Delete the 
 datastores folder to fix the problem.

 There are a couple of scenarios that are helpful to demonstrate how the 
 sample and SettingsDB works.

 Normal case:
 1) Run SettingsDemo, on a device, or the simulator.
 2) At the MainWindow, choose the menu operation for Settings:General
 3) Change some user-level settings, click Done/Back.
 4) Note that the settings have changed.
 5) Quit simulator or reboot device.
 6) Run SettingsDemo.
 7) Note that the settings were retained over the reboot.

 Hard-Reset case (simulator):
 1) For the simulator, find the datastores folder in the working directory.
 2) Delete the datastores folder.  
 3) Run SettingsDemo.
 4) Allow the network to connect. 
 5) Note that the settings are updated to reflect saved settings on the service.

 Hard-Reset case (device):
 1) Hard reset with paperclip and power, or use poweroff in htconsole.
 2) Start device, then run SettingsDemo.
 3) After network connects, note that settings are restored.

 Data-sync case:
 0) Set High-score to 1000.
 1) Make sure that setting data exists on the service (wait 5 min).
 2) Hard-reset by deleting the datastores folder.
 3) Set com.danger.network.disabled=true in sysinfo.properties.
 4) This simulates off network, hard-reset.
 5) Add data, by changing defaults.  Change High Score to 2000.
 6) Set network.disabled=false, and rerun.
 7) Note that high score starts at 2000, and stays. 
 8) Most recent record preempts the restore from network.
 
  
 Please note that at some point, the device or the simulator will need to idle
 for 5 minutes, to allow the save to network operation to run.  Hard-resets
 before the 5 minute timeout will not be restored, as the data never made it
 to the service.  The network has saved the data when the message:
 [java] Srvc: send 'danger.io.ServiceObjects.Pong'
 appears in the console.

 A possible simplified way to think of SettingsDB is that it is persistent
 data storage that happens to be automatically backed up.  It can appear to 
 do more than it actually does, which is to be a safe backup.  One primary
 service provided by SettingsDB is to automatically restore and synchronize
 records on the device.  If data on the device is newer than on the service,
 it will not be overwritten (new data after a hard-reset, before network).

 In general, there is no way to determine the difference between a first-run
 of the application, and a hard-reset case.  Generally speaking, this should
 never be a concern.  If the app does not find any settings upon startup, it
 should assume it is first-run and use programmatic defaults, but switch back 
 to normal operation upon receiving DATASTORE_RESTORED.
 */

import danger.app.Application;
import danger.app.EventType;
import danger.ui.ScreenWindow;
import danger.util.DEBUG;
import danger.app.Event;

// ------------------------------------------------------------------------

/**
 * Implements the main application class.
 */
public class SettingsDemo extends Application implements Resources, Commands {

	/**
	 * All instance variables are private, unless there is a good reason not to
	 * be. Good programming habits help to avoid bugs. Good habits are more
	 * powerful than good brains. for reference: "Writing Solid Code" by Steve
	 * Maguire. Also highly recommended: "Code Complete" by Steve McConnell
	 */
	private MainWindow mWindow;

	private SettingsScreen mScreener;

	// ------------------------------------------------------------------------
	/**
	 * Creates the main application class.
	 */
	public SettingsDemo() {
		// All DEBUG.p are simply debugging comments sent to the console.
		DEBUG.p("............................Settings Demo startup");
		createWindows();
	}

	/**
	 * Create the window objects, which will read default values from the
	 * resource file. This ensures the Settings object correctly reflects those
	 * defaults.
	 */
	private void createWindows() {
		Settings theSettings;

		// Create our application's main window
		mWindow = (MainWindow) getResources().getScreen(ID_MAIN_SCREEN);
		mWindow.show();

		// Create the SettingsWindow, for use in setting preferences.
		// This needs to be done before the Settings object, so that the default
		// resource specifications can be used. Upon completion this is
		// actually a SettingsScreen, not merely a ScreenWindow, hence the cast.
		mScreener = (SettingsScreen) getResources().getScreen(
				ID_SETTINGS_SCREEN);

		// First reference to getInstance, will create the Settings object
		// itself, for storing preferences.
		theSettings = Settings.getInstance();

		// Make the Settings object and SettingsScreen objects be in sync.
		// By calling saveAllUISettings first, all the presently invalid
		// unitialized Settings fields will be set to the resource based
		// programmatic defaults.
		mScreener.saveAllUISettings();

		// Now, doing the restoreAllUISettings will try to read any settings
		// from the network that exist, overwriting defaults, then updating the
		// UI to match.
		mScreener.restoreAllUISettings();

		// Fix up the high score the same way. Programmatic default:
		theSettings.setHighScore(0);

		// Get DB version if there is one, to overwrite default.
		theSettings.getHighScore();

		// Make main window match the newly improved Settings state.
		mWindow.setAllStates();

		// Make the windows display the correct states.
		refreshWindows();
	}

	/**
	 * Called by the Window classes to have the screen redrawn. This makes it so
	 * that the windows don't need to have references to the other windows, and
	 * can refresh all states when necessary, like after the Done button in the
	 * SettingsScreen.
	 */
	public void refreshWindows() {
		mScreener.restoreAllUISettings();
		mScreener.invalidate();

		mWindow.setAllStates();
		mWindow.invalidate();
	}

	/**
	 * ScreenWindow get function, so that the mScreener variable can be private,
	 * and hence less prone to bugs.
	 */
	public ScreenWindow getScreenWindow() {
		return (mScreener);
	}

	/**
	 * When the hidden menu option is chosen, come here to put up an Alert, to
	 * decide if the settings are to be deleted. 'this' is the Application
	 * Listener receiveEvent, which will receive the EVENT_DELETE_ALL_OK. Or, it
	 * might receive the Back/Cancel event, in which case nothing is done.
	 */
	public void deleteAllRequest() {
		showAlert(ID_DELETE_ALL_ALERT, this);
	}

	/**
	 * The Delete All dialog will send the Delete_All_OK event here, when the
	 * user makes that choice. The Back/Done is ignored as it dismisses the
	 * dialog and acts as a Cancel button.
	 * 
	 * The App also handles the Datastore_Restored event. It is sent when a
	 * device hard reset has caused the battery backed datastores to be lost,
	 * and they have been reloaded from the network. In this event, the Settings
	 * object is refreshed to reflect the most accurate settings. The UI is also
	 * updated to match that reload.
	 * 
	 * Lastly the Auto_Sync_Done event is received here as well. This event is
	 * of limited value, and is usually seen at the same time as the
	 * Datastore_Restored. Unless the application has cross-datastore
	 * requirements, this event can be safely ignored. It is not necessary to
	 * call registerAutoSyncBundleListener() as the Application bundle is
	 * registered by default.
	 */
	public boolean receiveEvent(Event e) {
		switch (e.type) {

		// Called in response to the Delete All click in the Confirmation
		// Dialog. The App object was specified as the Listener when the
		// dialog was created.
		case EVENT_DELETE_ALL_OK:
			Settings.getInstance().clearAllSettingsDB();

			// Now the window values don't match the DB values, so recreate
			// both windows to restore the programmatic defaults.
			createWindows();
			DEBUG.p("SettingsDemo: EVENT_DELETE_ALL_OK");
			return true;

		// This event does not occur in the normal case of launching or
		// rebooting the device. This only occurs when the device is
		// hard-reset, and data is restored from the network.
		case EventType.EVENT_DATASTORE_RESTORED:
			refreshWindows();

			Settings.getInstance().dumpDB(); // see what was just restored in
			// the console.
			DEBUG.p("SettingsDemo: EVENT_DATASTORE_RESTORED");
			return true;

		// This notifies the app that the *device* as a whole has been
		// fully synchronized with the service, not just the app.
		// In this app, this event is unused, because there are no cross-app
		// needs. Most apps can safely ignore this event.
		// An example of potential cross-application communication might be
		// the gallery, if the application needed access to the photos.
		case EventType.EVENT_AUTO_SYNC_DONE:
			DEBUG.p("SettingsDemo: EVENT_AUTO_SYNC_DONE");
			return true;
		}

		// If the event wasn't handled above, send it back to the superclass.
		return super.receiveEvent(e);
	}

}
