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

import danger.app.SettingsDB;
import danger.app.SettingsDBException;
import danger.audio.RingToneObject;
import danger.util.DEBUG;

import java.lang.String;

/**
 * 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().
 */
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 our programmatic high score.
     */
    static private int sHighScore;

    /**
     * The parameters to be saved from the Settings:General window. These are
     * the local copies that are the final word as to state. They are loaded
     * either from programmatic defaults or from the DB entries when they exist.
     */
    static private int sCheckBox1;
    static private int sCheckBox2;
    static private int sCheckBox3;
    static private int sCheckBox4;

    static private int sRadioButton;
    static private RingToneObject sRingTone;
    static private String sEditText;

    // ------------------------------------------------------------------------
    /**
     * Constructor for Settings.
     * 
     * Most of the defaults are stored in the resource file, as resource
     * 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 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.
        sHighScore = -1;

        sCheckBox1 = -1;
        sCheckBox2 = -1;
        sCheckBox3 = -1;
        sCheckBox4 = -1;

        sRadioButton = -1;
        sRingTone = null;
        sEditText = null;
    }

    /**
     * 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.
     */
    public int getCheckBox1() {
        try {
            sCheckBox1 = sSettingsDB.getIntValue("checkbox-1");
        } catch (SettingsDBException ex) {
        }
        return sCheckBox1;
    }

    public int getCheckBox2() {
        try {
            sCheckBox2 = sSettingsDB.getIntValue("checkbox-2");
        } catch (SettingsDBException ex) {
        }
        return sCheckBox2;
    }

    public int getCheckBox3() {
        try {
            sCheckBox3 = sSettingsDB.getIntValue("checkbox-3");
        } catch (SettingsDBException ex) {
        }
        return sCheckBox3;
    }

    public int getCheckBox4() {
        try {
            sCheckBox4 = sSettingsDB.getIntValue("checkbox-4");
        } catch (SettingsDBException ex) {
        }
        return sCheckBox4;
    }

    public int getRadioButton() {
        try {
            sRadioButton = sSettingsDB.getIntValue("radio-button");
        } catch (SettingsDBException ex) {
        }
        return sRadioButton;
    }

    public RingToneObject getRingTone() {
        try {
            sSettingsDB.getObject("ring-tone", sRingTone);
        } catch (SettingsDBException ex) {
        }
        return sRingTone;
    }

    /**
     * The getStringValue can return null, rather than an Exception.
     */
    public String getEditText() {
        String theString;

        theString = sSettingsDB.getStringValue("edit-text");
        if (theString != null) {
            sEditText = theString;
        }
        return sEditText;
    }

    public int getHighScore() {
        try {
            sHighScore = sSettingsDB.getIntValue("high-score");
            DEBUG.p("getHighScore: " + sHighScore);
        } catch (SettingsDBException ex) {
        }
        return sHighScore;
    }

    /**
     * Assorted Setter functions for the various UI General: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.
     */
    public void setCheckBox1(int newCheckBox) {
        // if it's initialized and different from saved value, save it to the DB
        if ((sCheckBox1 != -1) && (newCheckBox != sCheckBox1)) {
            sSettingsDB.setIntValue("checkbox-1", newCheckBox);
        }
        sCheckBox1 = newCheckBox;
    }

    public void setCheckBox2(int newCheckBox) {
        if ((sCheckBox2 != -1) && (newCheckBox != sCheckBox2)) {
            sSettingsDB.setIntValue("checkbox-2", newCheckBox);
        }
        sCheckBox2 = newCheckBox;
    }

    public void setCheckBox3(int newCheckBox) {
        if ((sCheckBox3 != -1) && (newCheckBox != sCheckBox3)) {
            sSettingsDB.setIntValue("checkbox-3", newCheckBox);
        }
        sCheckBox3 = newCheckBox;
    }

    public void setCheckBox4(int newCheckBox) {
        if ((sCheckBox4 != -1) && (newCheckBox != sCheckBox4)) {
            sSettingsDB.setIntValue("checkbox-4", newCheckBox);
        }
        sCheckBox4 = newCheckBox;
    }

    public void setRadioButton(int newRadioButton) {
        // if it's initialized and different from saved value, save it to the DB
        if ((sRadioButton != -1) && (newRadioButton != sRadioButton)) {
            sSettingsDB.setIntValue("radio-button", newRadioButton);
        }
        sRadioButton = newRadioButton;
    }

    /**
     * This setter uses the setObject routine to save the ring tone to the DB.
     * This works for system objects like RingToneObjects, but cannot be used
     * for third-party objects because the routine is private and not
     * extensible.
     */
    public void setRingTone(RingToneObject newRingTone) {
        if (sRingTone == null) {
            sRingTone = newRingTone;               // if it was invalid, set it.
        } else if (!newRingTone.equals(sRingTone)) // use !equals for complex
        {                                          // objects, not !=
            sRingTone = newRingTone;
            sSettingsDB.setObject("ring-tone", newRingTone);
        }
    }

    public void setEditText(String newEditText) {
        if (sEditText == null) {
            sEditText = newEditText;               // if it was invalid, set it.
        } else if (!newEditText.equals(sEditText)) // use !equals for complex
        {                                          // objects, not !=
            sEditText = newEditText;
            sSettingsDB.setStringValue("edit-text", newEditText);
        }
    }

    /**
     * Set the high score.
     */
    public void setHighScore(int newHS) {
        if (sHighScore == -1) {
            sHighScore = newHS;             // if it was invalid, set it.
        } else if (newHS != sHighScore) {
            sHighScore = newHS;             // if it's different, save new one.
            sSettingsDB.setIntValue("high-score", newHS);
            DEBUG.p("setHighScore: " + sHighScore);
        }
    }

    /**
     * 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();
    }
}
