; docformat = 'rst'
;+
; Class responsible for storing and retrieving preferences. Preferences are 
; persistent across IDL sessions on the same computer.
;
; :Examples:
;    The main-level program at the end of this file has an example use of 
;    `MGffPrefs`. To run it, type::
;
;       IDL> .run mgffprefs__define
;
;    The code creates an `MFffPrefs` object and uses the `set` and `get` 
;    methods to set and retrieve a preference.
;
;    Creating the `MGffPrefs` requires setting the `AUTHOR_NAME` and 
;    `APP_NAME` properties so that the `APP_DIRECTORY`, where the prefence
;    value files are stored, can be specified::
;
;       authorName = 'mgffprefs_demo'
;       appName = 'mgffprefs_demo'
;       prefs = obj_new('mgffprefs', author_name=authorName, app_name=appName)
;
;    We can now set a preference value, remember the preference name is 
;    case-insensitive, but the value is stored exactly::
;
;       prefs->set, 'name', 'Michael'
;
;    The preferences object can now be destroyed to show the preferences are
;    persistent between IDL sessions::
;
;       obj_destroy, prefs
;       
;    A new `MGffPrefs` object is created with the same `AUTHOR_NAME` and
;    `APP_NAME` values::
;
;       prefs = obj_new('mgffprefs', author_name=authorName, app_name=appName)
;
;    The "name" preference can now be retrieved::
;
;       name = prefs->get('name', found=found)
;
;    We can retrieve the `APP_DIRECTORY` property to know where the preference
;    files are stored::
;
;       prefs->getProperty, app_directory=appdir
;
;    We are done with the `MGffPrefs` object::
;
;       obj_destroy, prefs
;
;    Print the preference value::
;       
;       print, name, format='(%"Preference value for name: %s")'
;
;    We can manually clean out the entire directory for our preferences::
;       
;       file_delete, file_dirname(appdir), /recursive, /allow_nonexistent, /quiet
;
;    Individual preferences can be cleared with the `clear` method.
;
; :Properties:
;    author_name
;       short name of the author
;    app_name
;       short name of the application
;    author_description
;       full name of the author
;    app_description
;       full name of the application
;    app_directory
;       location of the directory for the application using these preferences
;-
;+
; Save the value of a preference.
; 
; :Params:
;    prefname : in, required, type=string
;       case-insensitive name of preference to retrieve
;    prefvalue : in, required, type=any
;       value of the preference
;-
pro mgffprefs::set, prefname, prefvalue
  compile_opt strictarr
  on_error, 2
  
  filename = filepath(self->_prefnameToFilename(prefname), root=self.appdir)
  save, prefvalue, filename=filename
end
;+
; Clear the value of a preference.
;
; :Params:
;    prefname : in, required, type=string
;       case-insensitive name of preference to retrieve
;-
pro mgffprefs::clear, prefname
  compile_opt strictarr
  
  filename = filepath(self->_prefnameToFilename(prefname), root=self.appdir)
  if (file_test(filename)) then file_delete, filename
end
;+
; Retrieve the value of a preference.
; 
; :Returns:
;    preference value
;
; :Params:
;    prefname : in, required, type=string
;       case-insensitive name of preference to retrieve
;
; :Keywords:
;    found : out, optional, type=boolean
;       set to a named variable to return whether the preference was found
;    default : in, optional, type=any
;       default value to use if no preference value is found for the given
;       preference name
;    names : in, optional, type=boolean
;       set to return a list of the preference names instead of a value; names
;       may not agree exactly with the names given in the set method because
;       they have been modified to make valid filename
;-
function mgffprefs::get, prefname, found=found, default=default, names=names
  compile_opt strictarr
  
  catch, error
  if (error ne 0L) then begin
    catch, /cancel
    return, n_elements(default) gt 0L ? default : -1L
  endif
  if (keyword_set(names)) then begin
    searchPattern = filepath('*.sav', root=self.appdir)
    files = file_search(searchPattern, count=count)
    found = count gt 0L
    return, found ? file_basename(files, '.sav') : -1L
  endif
    
  found = 0B
  if (n_elements(prefname) eq 0L) then message, 'preference name required'
  
  filename = filepath(self->_prefnameToFilename(prefname), root=self.appdir)
  if (~file_test(filename)) then return, n_elements(default) gt 0L ? default : -1L
  restore, filename=filename
  
  found = 1B
  return, prefvalue
end
;+
; Converts a preference name to a valid save filename.
;
; :Private:
;
; :Returns:
;    string
; 
; :Params:
;    prefname : in, required, type=string
;       name of preference
;-
function mgffprefs::_prefnameToFilename, prefname
  compile_opt strictarr
  
  return, strlowcase(idl_validname(prefname, /convert_all)) + '.sav'
end
;+
; Returns directory for application data.
; 
; :Private:
;
; :Returns:
;    string
;
; :Params:
;    authorName : in, required, type=string
;       short name of the author
;    appName : in, required, type=string
;       short application name
;
; :Keywords:
;    author_description : in, optional, type=string
;       full name of the author
;    app_description : in, optional, type=string
;       full name of the application
;-
function mgffprefs::_getAppDir, authorName, appName, $
                                author_description=authorDescription, $
                                app_description=appDescription
  compile_opt strictarr
  readmeVersion = 1
  
  _authorDescription = n_elements(authorDescription) eq 0L $
                         ? authorName $
                         : authorDescription
  _appDescription = n_elements(appDescription) eq 0L $
                      ? appName $
                      : appDescription
  
  readmeText = ['This is the user configuration directory for ' + _appDescription, $
                'by ' + _authorDescription + '.']
                  
  configDir = app_user_dir(authorName, _authorDescription, $
                           appName, _appDescription, $
                           readmeText, readmeVersion)
                           
  return, configDir  
end
;+
; Get properties.
;-
pro mgffprefs::getProperty, app_directory=appDirectory
  compile_opt strictarr
  
  if (arg_present(appDirectory)) then appDirectory = self.appdir
end
;+
; Free resources.
;-
pro mgffprefs::cleanup
  compile_opt strictarr
  
end
;+
; Initialize a prefs object.
;
; :Returns:
;    1 for success, 0 for failure
;
; :Keywords:
;    author_name : in, required, type=string
;       short name of the author
;    app_name : in, required, type=string
;       short name of the application
;    author_description : in, optional, type=string
;       full name of the author
;    app_description : in, optional, type=string
;       full name of the application
;-
function mgffprefs::init, author_name=authorName, app_name=appName, $
                          author_description=authorDescription, $
                          app_description=appDescription
  compile_opt strictarr
  on_error, 2
  
  if (n_elements(authorName) eq 0L || n_elements(appName) eq 0L) then begin
    message, 'Author and application name required'
  endif
  
  self.appdir = self->_getAppDir(authorName, appName, $
                                 author_description=authorDescription, $
                                 app_description=appDescription)
  
  return, 1
end
;+
; Define instance variables.
;
; :Fields:
;    appdir
;       directory to place preference files
;-
pro mgffprefs__define
  compile_opt strictarr
  define = { MGffPrefs, $
             appdir: '' $
           }
end
; main-level example program
authorName = 'mgffprefs_demo'
appName = 'mgffprefs_demo'
prefs = obj_new('mgffprefs', author_name=authorName, app_name=appName)
prefs->set, 'name', 'Michael'
obj_destroy, prefs
prefs = obj_new('mgffprefs', author_name=authorName, app_name=appName)
name = prefs->get('name', found=found)
prefs->getProperty, app_directory=appdir
obj_destroy, prefs
print, name, format='(%"Preference value for name: %s")'
file_delete, file_dirname(appdir), /recursive, /allow_nonexistent, /quiet
                 
end