/*
 * +================================================================================+
 *      __  ___  ______________  __  __  ______________   ____
 *     / / / / |/ /  _/_  __/\ \/ / / / / /_  __/  _/ /  / __/
 *    / /_/ /    // /  / /    \  / / /_/ / / / _/ // /___\ \
 *    \____/_/|_/___/ /_/     /_/  \____/ /_/ /___/____/___/
 *
 * +================================================================================+
 *
 *    "THE BEER-WARE LICENSE"
 *    -----------------------
 *
 *    As long as you retain this notice you can do whatever you want with this
 *    stuff. However it is expressly forbidden to sell or commercially distribute
 *    Unity Utils or parts of it apart from your games.
 *    If you meet any of the authors some day, and you think this stuff is
 *    worth it, you can buy them a beer in return.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *    THE SOFTWARE.
 *
 * +================================================================================+
 *
 *    Authors:
 *    David Krummenacher (dave@timbookedtwo.com)
 *
 */

using System;
using UnityEngine;

namespace Utils.Settings {

	/// <summary>
	/// Setting base class.
	/// Derive from this class to create your own settings.
	/// </summary>
	public abstract class Setting {

		#region Events

		/// <summary>
		/// Delegate for events.
		/// </summary>
		public delegate void SettingEventHandler(Setting sender);
		/// <summary>
		/// Occurs when setting was initialized.
		/// </summary>
		public event SettingEventHandler OnInitialized;
		/// <summary>
		/// Occurs when setting was loaded.
		/// </summary>
		public event SettingEventHandler OnLoaded;
		/// <summary>
		/// Occurs when setting was saved.
		/// </summary>
		public event SettingEventHandler OnSaved;
		/// <summary>
		/// Occurs when setting was applied.
		/// </summary>
		public event SettingEventHandler OnApplied;
		/// <summary>
		/// Occurs when a setting was changed.
		/// </summary>
		public event SettingEventHandler OnValueChanged;
		/// <summary>
		/// Raises the value changed event.
		/// Use this in the derived settings classes on their properties.
		/// </summary>
		/// <param name="sender">Sender.</param>
		protected void RaiseOnValueChanged(Setting sender) {
			// Don't raise event if no one is subscribed
			SettingEventHandler handler = OnValueChanged;
			if (handler != null) handler(sender);
		}

		#endregion

		#region Convenience methods

		/// <summary>
		/// Indicates wheter a specified key can be found in the player preferences.
		/// </summary>
		/// <returns><c>true</c> if the key was found; otherwise, <c>false</c>.</returns>
		/// <param name="key">The key.</param>
		public static bool HasKey(string key) { return PlayerPrefs.HasKey(key); }
		/// <summary>
		/// Sets the specified key to the specifided value in the player preferences.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="value">The value.</param>
		public static void SetInt(string key, int value) { PlayerPrefs.SetInt(key, value); }
		/// <summary>
		/// Gets the value of the specified key in the player preferences.
		/// </summary>
		/// <returns>The value.</returns>
		/// <param name="key">The key.</param>
		public static int GetInt(string key) { return PlayerPrefs.GetInt(key); }
		/// <summary>
		/// Sets the specified key to the specifided value in the player preferences.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="value">The value.</param>
		public static void SetFloat(string key, float value) { PlayerPrefs.SetFloat(key, value); }
		/// <summary>
		/// Gets the value of the specified key in the player preferences.
		/// </summary>
		/// <returns>The value.</returns>
		/// <param name="key">The key.</param>
		public static float GetFloat(string key) { return PlayerPrefs.GetFloat(key); }
		/// <summary>
		/// Sets the specified key to the specifided value in the player preferences.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="value">The value.</param>
		public static void SetBool(string key, bool value) { PlayerPrefs.SetInt(key, value ? 1 : 0); }
		/// <summary>
		/// Gets the value of the specified key in the player preferences.
		/// </summary>
		/// <returns>The value.</returns>
		/// <param name="key">The key.</param>
		public static bool GetBool(string key) { return PlayerPrefs.GetInt(key) > 0; }
		/// <summary>
		/// Sets the specified key to the specifided value in the player preferences.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="value">The value.</param>
		public static void SetString(string key, string value) { PlayerPrefs.SetString(key, value); }
		/// <summary>
		/// Gets the value of the specified key in the player preferences.
		/// </summary>
		/// <returns>The value.</returns>
		/// <param name="key">The key.</param>
		public static string GetString(string key) { return PlayerPrefs.GetString(key); }

		#endregion

		/// <summary>
		/// Initializes the setting.
		/// </summary>
		protected virtual void InitSetting() {}
		/// <summary>
		/// Loads the setting.
		/// </summary>
		protected virtual void LoadSetting() {}
		/// <summary>
		/// Saves the setting.
		/// </summary>
		protected virtual void SaveSetting() {}
		/// <summary>
		/// Applies the setting.
		/// </summary>
		protected virtual void ApplySetting() {}

		/// <summary>
		/// Initializes a new instance of the <see cref="Utils.Settings.Setting"/> class.
		/// </summary>
		public Setting() {
			Init();
		}

		/// <summary>
		/// Initializes this setting.
		/// </summary>
		public void Init() {
			InitSetting();

			// Don't raise event if no one is subscribed
			SettingEventHandler handler = OnInitialized;
			if (handler != null) handler(this);
		}

		/// <summary>
		/// Loads this setting from the player preferences.
		/// Use this to discard changes on a setting.
		/// </summary>
		/// <param name="apply">If set to <c>true</c> applies the setting as well.</param>
		public void Load(bool apply = false) {
			LoadSetting();

			// Don't raise event if no one is subscribed
			SettingEventHandler handler = OnLoaded;
			if (handler != null) handler(this);

			if (apply) Apply();
		}

		/// <summary>
		/// Saves this setting to the player preferences.
		/// </summary>
		/// <param name="apply">If set to <c>true</c> applies the setting as well.</param>
		public void Save(bool apply = false) {
			SaveSetting();

			// Don't raise event if no one is subscribed
			SettingEventHandler handler = OnSaved;
			if (handler != null) handler(this);

			if (apply) Apply();
		}

		/// <summary>
		/// Applies this setting.
		/// </summary>
		public void Apply() {
			ApplySetting();

			// Don't raise event if no one is subscribed
			SettingEventHandler handler = OnApplied;
			if (handler != null) handler(this);
		}
	}
}
