// Copyright 2001-2004, 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 danger.app.SettingsDB;

/**
 * Manager for settings. This object is the ultimate arbiter of the settings for
 * the program. Before settings are displayed, they should be pulled from here
 * to get their most recent and correct state. The main reason to store them
 * here is for simplicity. There are lots of places where they are used, but it
 * helps to have one source for definitive information on their states. Here.
 * 
 * Also, this is more self-contained, making it more flexible to be used as
 * template code for other applications.
 * 
 * The Settings object is created as a singleton, because there should only ever
 * be one object that contains the settings states, and a second copy can only
 * lead to skinned knees and tears. It is referenced by getting the single
 * instance using Settings.getInstance().
 * 
 * In this case, there is only a single setting for the program, the SortBy
 * field. This code was copied from the more complete SettingsDemo.
 * 
 * 
 * This needs to be in an object by itself, to avoid an interesting bug when the
 * Datastore is restored. If the object sSettingsDB is recreated during that
 * event, it will create a new DataStore and produce an infinite loop of
 * DATASTORE RESTORED events. This avoids that by having a single instance of
 * Settings, and thus a single reference to sSettingsDB.
 * 
 * There has been some question as to whether the DataStore and SettingsDB can
 * use the same backing DataStore name. While this is presently true, it is poor
 * form to use the same name for both, as it may change in the future.
 * Therefore, this uses "settings" for the sort parameter and "high-score" for
 * the actual score data.
 */
public class Settings {
	static private boolean REQUEST_AUTO_SYNC = true;

	/**
	 * The sSettings is the single reference to the single instance of this
	 * singleton object. It is private and static to prevent any other code from
	 * accessing it accidentally. It is initialized upon the first call to
	 * getInstance().
	 */
	static private Settings sSettings = null;

	/**
	 * Always non-null; datastore for all the prefs.
	 */
	static private SettingsDB sSettingsDB;

	/**
	 * The local instance of the Sorting choice. The final arbiter for how
	 * things are to be sorted.
	 */
	static private int sSortBy;

	// ------------------------------------------------------------------------
	/**
	 * Constructor for Settings.
	 * 
	 * When created, this object must be the arbiter of all settings. So, to
	 * properly reflect the default no-DataBase case, the instance variables are
	 * initially set to invalid values. The App will then initialize them from
	 * the SettingsScreen resource based defaults, and finally update each of
	 * them to reflect any DB based settings. This way, any non-DB items are
	 * left properly set to reflect the resource or programmatic defaults.
	 * 
	 * This constructor is private, so that it cannot be instantiated by any
	 * other code. This makes it a singleton.
	 */
	private Settings() {
		// Create the object in any case. If nothing is saved, the DB will still
		// not exist, and there will be no network traffic. The referenced
		// object is always needed.
		sSettingsDB = new SettingsDB("settings", REQUEST_AUTO_SYNC);

		// For debugging purposes, dump the contents of the DB to the console.
		dumpDB();

		// All instance variables are default initialized to invalid values, so
		// that they can be properly set to programmatic or resource based
		// defaults.
		sSortBy = -1;
	}

	/**
	 * getInstance() The routine returns the singleton reference to 'this'.
	 * Because of the private constructor, and getInstance(), there will only be
	 * one instance of this object.
	 */
	static public Settings getInstance() {
		if (sSettings == null) {
			sSettings = new Settings();
		}
		return (sSettings);
	}

	/**
	 * Getters for the various fields to be saved to the service.
	 * 
	 * Each Getter handles the possible Exception, because a specific record may
	 * not have been written. If an exception is thrown, that means the
	 * programmatic default must still be used. In that case, the current
	 * variable is the right choice, as it will have been initialized to a
	 * programmatic default.
	 * 
	 * The generic Exception is used instead of the SettingsDBException, to
	 * handle possible I/O errors, or other transient events.
	 */
	public static int getSortBy() {
		try {
			sSortBy = sSettingsDB.getIntValue("sort-by");
		} catch (Exception ex) {
		}
		return sSortBy;
	}

	/**
	 * Assorted Setter functions for the various UI Settings. The philosophy for
	 * these setters is to update the DB immediately when the setter is called.
	 * Settings are done one at a time, and if a setting has not changed, it
	 * won't be rewritten. In fact, if it has not changed from its original
	 * programmatic default, it won't even be created, to save DB space.
	 * 
	 * The instance variables are initialized to an invalid state (-1 or null)
	 * to allow for them to be set to programmatic defaults, without having to
	 * create DB entries.
	 */

	/**
	 * Set the Sorting parameter.
	 */
	public static void setSortBy(int newSortBy) {
		if (sSortBy == -1) {
			sSortBy = newSortBy; // if it was invalid, set it.
		} else if (newSortBy != sSortBy) {
			sSortBy = newSortBy; // if it's different, save new one.
			sSettingsDB.setIntValue("sort-by", newSortBy);
		}
	}

	/**
	 * Clear all the settings from the DB saved on the network. This is
	 * something that should be done rarely, because it causes a lot of work for
	 * the service. This routine is primarily here to allow for special cases,
	 * like resetting the settings back to their programmatic defaults, with no
	 * settings saved. Or for handling something terrible like a corrupted DB.
	 * 
	 * In this program, it is called from a shortcut operation to reset the
	 * program back to first states, in order to demonstrate how the program and
	 * resource defaults are used before the DB is created.
	 * 
	 * After the operation, the DB will be cleared, but the UI widget states
	 * will be left as they were, and won't match the usual programmatic
	 * defaults.
	 * 
	 * There has been some confusion as to whether clearAll() actually works or
	 * not. It does work as expected, but requires the usual 5 minute timeout
	 * for DB changes to take effect. Rebooting the device is not necessary.
	 */
	public void clearAllSettingsDB() {
		sSettingsDB.clearAll();
	}

	/**
	 * For debugging purposes, it is useful to be able to specifically see what
	 * is in the DB at a given moment. .dump() does not work under 1.1 or 2.0,
	 * but gives full information under 2.3
	 */
	public void dumpDB() {
		sSettingsDB.dump();
	}
}
