/* This file is part of GPump, a Pump.io client.
 *
 * GPump (THE SOFTWARE) is made available under the terms and conditions of the
 * GNU Lesser General Public Licence 3.0. A copy of the licence can be read
 * in the file lgpl-3.0.txt in the root of this project.
 */


const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const System = imports.system;

let _default_settings_object = null;

/** @file
 */

/** @function
 * Please use this function when getting the settings data object. It is not
 * good to have multiple instances of the settings data object, it may
 * cause problems down the line.
 *
 * @return The the default @c SettingsData object
 */
function get_settings () {
  if (!_default_settings_object) {
    _default_settings_object = new SettingsData ();
  }
  return _default_settings_object;
}

/** @enum
 */
const QUIT_ON_CLOSE = {
  /** 
   * Default value, will show the close dialogue on clicking the close button
   * in the main window.
   */
  UNKNOWN: -1,
  /** Will not exist the app on close of main window */
  NO: 0,
  /** Will exit on close of main window */
  YES: 1
}

/** @class
 * NO NOT CREATE INSTANCES FROM THIS OBJECT, USE THE get_settings () FUNCTION!
 */
const SettingsData = new Lang.Class ({
  Name: 'SettingsData',
  Extends: GObject.Object,
  Signals: {
    /** @signal change
     * When hooking up to this signal check that the name of the change.
     * 
     * The function signature is <code> function my_callback (String: setting,
     * Value: value) </code> where @c setting is the name of the setting,
     * and @c value is the value to be that it has changed to.
     *
     * @warning Always check the @c value type and the @c setting name.
     *
     * @return void
     */
    'change': {
      param_types: [GObject.TYPE_STRING, GObject.TYPE_INT]
    }
  },
  
  
  /* Member definitions */
  _settings_file_path: String,
  _settings: Array,
  _settings_file: Gio.File,
  
  /**
   * 
   */
  _init: function (params) {
    this.parent (params);
    
    /* First we construct the path for where to store the settings file. */
    this._settings_file_path = "" + 
                              GLib.get_user_config_dir () +
                              "/gpump/gpump.json";
    print ("Config file is: " + this._settings_file_path);
    
    this._settings_file = Gio.File.new_for_path (this._settings_file_path);
    
    /* Then we check that the file exists. If the file does not exists we
     * construct some sane default values.
     */
    if (!GLib.file_test (this._settings_file_path, GLib.FileTest.EXISTS)) {
      /* -1 is undefined, and will be set at construction of the window */
      this._settings = {
        ui: {
          x: -1,
          y: -1,
          h: 500,
          w: 500
        },
        main: {
          privacy: {
            show_full_name: true,
            only_show_avatar: false
          },
          use_dark: false,
          first_run: true,
          quit_on_close: QUIT_ON_CLOSE.UNKNOWN
        }
      };
      // DEBUG:
      print (JSON.stringify (this._settings, null, 2).toString () );
      
      // Check if the folder exists, and if it does not exist, create it.
      let folder_path = "" +
                        GLib.get_user_config_dir () +
                        "/gpump/";
      let folder = Gio.File.new_for_path (folder_path);
      if (!GLib.file_test (folder_path, GLib.FileTest.EXISTS)) {
        folder.make_directory (null);
      }
      
      let file_stream = this._settings_file.create (Gio.FileCreateFlags.PRIVATE,
                                                    null);
      
      file_stream.write_all (JSON.stringify (
          this._settings, null, 2).toString (), null);
      
      file_stream.close (null);
      
    } else {
      /* The file exists, we load the settings data into memory */
      let file_stream = this._settings_file.read (null);
      
      /* See: http://stackoverflow.com/a/21146281
       */
      let file_info = this._settings_file.query_info("standard::size",
                                             Gio.FileQueryInfoFlags.NONE, null);
      let size = file_info.get_size ();
      
      let buffer = file_stream.read_bytes (size, null).get_data ();
      
      this._settings = JSON.parse (buffer);
      
      //DEBUG:
      print (JSON.stringify (this._settings, null, 2).toString () );
      
    }
  },
  
  /** @method
   * Sets a value in the setting object.
   * 
   * @return false on fail
   * @return true when everything is OK.
   */
  set_setting: function (setting, value) {
    let ret_val = true;
    if (typeof setting != "string") {
      print ("ERROR: The \"setting\" parameter must be a string.");
      return false;
    }
    switch (setting) {
      case "ui.x":
        if (typeof value == "number") {
          this._settings.ui.x = value;
        } else {
          print ("The setting \"ui.x\" must be a number.");
          ret_val = false;
        }
        break;
      case "ui.y":
        if (typeof value == "number") {
          this._settings.ui.y = value;
        } else {
          print ("The setting \"ui.y\" must be a number.");
          ret_val = false;
        }
        break;
      case "ui.h":
        if (typeof value == "number") {
          this._settings.ui.h = value;
        } else {
          print ("The setting \"ui.h\" must be a number.");
          ret_val = false;
        }
        break;
      case "ui.w":
        if (typeof value == "number") {
          this._settings.ui.w = value;
        } else {
          print ("The setting \"ui.w\" must be a number.");
          ret_val = false;
        }
        break;
      case "main.use_dark":
        if (typeof value == "boolean") {
          this._settings.main.use_dark = value;
        } else {
          print ("The setting \"main.use_dark\" must be a boolean.");
          ret_val = false;
        }
        break;
      case "main.first_run":
        if (typeof value == "boolean") {
          this._settings.main.first_run = value;
        } else {
          print ("The setting \"main.first_run\" must be a boolean.");
          ret_val = false;
        }
        break;
      case "main.quit_on_close":
        if (typeof value == "number") {
          this._settings.main.quit_on_close = value;
        } else {
          print (
            "The setting \"main.quit_on_close\" must be a number.");
          ret_val = false;
        }
        break;
      case "main.privacy.show_full_name":
        if (typeof value == "boolean") {
          this._settings.main.privacy.show_full_name = value;
        } else {
          print (
            "The setting \"main.privacy.show_full_name\" must be a boolean.");
          ret_val = false;
        }
        break;
      case "main.privacy.only_show_avatar":
        if (typeof value == "boolean") {
          this._settings.main.privacy.only_show_avatar = value;
        } else {
          print (
            "The setting \"main.privacy.only_show_avatar\" must be a boolean.");
          ret_val = false;
        }
        break;
      default:
        
        print ("ERROR: The setting \"" + setting + "\" does not exist.");
        return false;
        break;
    }
    
    
    /* Because, reasons. */
    if (ret_val) {
      this.emit ("change", setting, value);
    }
    
    return ret_val;
  },
  
  /** @method
   * Gets a value from the settings object.
   *
   * @param setting the @c String that corrosponds to the setting to get.
   *
   * @return a <code> complex object </code> with two field: @c ok and @c data.
   *
   * If @c ok is false something went wrong and the data field will be undefined.
   *
   * If @c ok is true everything is ok and the data field will be set to the
   * value.
   */
  get_setting: function (setting) {
    let ret_data = {
      data: undefined,
      ok: true
    };
    
    if (typeof setting != "string") {
      print ("ERROR: The \"setting\" parameter must be a string.");
      ret_data.ok = false;
      return ret_data;
    }
    
    switch (setting) {
      case "ui.x":
        ret_data.data = this._settings.ui.x;
        break;
      case "ui.y":
        ret_data.data = this._settings.ui.y;
        break;
      case "ui.h":
        ret_data.data = this._settings.ui.h;
        break;
      case "ui.w":
        ret_data.data = this._settings.ui.w;
        break;
      case "main.use_dark":
        ret_data.data = this._settings.main.use_dark;
        break;
      case "main.first_run":
        ret_data.data = this._settings.main.first_run;
        break;
      case "main.privacy.show_full_name":
        ret_data.data = this._settings.main.privacy.show_full_name;
        break;
      case "main.privacy.only_show_avatar":
        ret_data.data = this._settings.main.privacy.only_show_avatar;
        break;
      case "main.quit_on_close":
        ret_data.data = this._settings.main.quit_on_close;
        break;
      default:
        ret_data.ok = false;
        print ("ERROR: The setting \"" + setting + "\" does not exist.");
        break;
    }
    
    return ret_data;
  },
  
  /** @method
   * Commits changes to the settings object to file.
   */
  commit_to_file: function () {
    print (JSON.stringify (this._settings, null, 2).toString () );
      
      let file_stream = this._settings_file.replace (null,
                                                    false,
                                                    Gio.FileCreateFlags.PRIVATE,
                                                    null);
      file_stream.write_all (JSON.stringify (
          this._settings, null, 2).toString (), null);
      
      file_stream.close (null);
  }
  
  // End of class.
});



