# -*- coding: utf-8 -*-

# Copyright (c) 2009, 2010, 2011 Jack Kaliko <efrim@azylum.org>
#
#  This file is part of MPD_sima
#
#  MPD_sima is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  MPD_sima is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with MPD_sima.  If not, see <http://www.gnu.org/licenses/>.
#
#

"""
Deal with configuration and data files.
Parse configuration file and set defaults for missing options.
"""

# IMPORTS 
import ConfigParser
import sys

from ConfigParser import (MissingSectionHeaderError, ParsingError)
from os import (makedirs, environ, stat, chmod)
from os.path import (join, isdir, isfile, expanduser)
from stat import (S_IMODE, ST_MODE, S_IRWXO, S_IRWXG)

import utils

# DEFAULTS
DIRNAME = 'mpd_sima'
CONF_FILE = 'mpd_sima.cfg'

DEFAULT_CONF = {
        'MPD': {
            'host': "localhost",
            'password': "false",
            'port': "6600"},
        'sima': {
            'similarity': "15",
            'dynamic': "10",
            'queue_mode': "track", #TODO control values
            'user_db': "false",
            'history_duration': "8",
            'queue_length': "1",
            'track_to_add': "1",
            'album_to_add': "1",
            'consume': "0",
            'single_album': "false",
            'check_new_version':"false",},
        'daemon':{
            'daemon': "false",
            'pidfile': "/tmp/mpd-sima.pid",},
        'log': {
            'verbosity': "info"}}
#


class ConfMan(object):#CONFIG MANAGER CLASS
    """
    Configuration manager.
    Default configuration is stored in DEFAULT_CONF dictionnary.
    First init_config() run to get config from file.
    Then control_conf() is run and retrieve configuration from defaults if not
    set in conf files.
    These settings are then updated with command line options with
    supersedes_config_with_cmd_line_options().

    Order of priority for the origin of an option is then (lowest to highest):
        * DEFAULT_CONF
        * Env. Var for host and port
        * configuration file (override previous)
        * command line options (override previous)
    """

    def __init__(self, logger, options=None):
        # options settings priority:
        # defauts < conf. file < command line
        self.conf_file = options.get('conf_file')
        self.config = None
        self.defaults = dict(DEFAULT_CONF)
        self.startopt = options
        ## Sima sqlite DB
        self.userdb_file = None

        self.log = logger
        ## INIT CALLS
        self.use_envar()
        self.init_config()
        self.control_conf()
        self.supersedes_config_with_cmd_line_options()

    def get_pw(self):
        try:
            self.config.getboolean('MPD', 'password')
            self.log.debug(u'No password set, proceeding without ' +
                          u'authentication...')
            return None
        except ValueError:
            # ValueError if password not a boolean, hence an actual password.
            pw = self.config.get('MPD', 'password')
            if not pw:
                self.log.debug(u'Password set as an empty string.')
                return None
            return pw

    def control_mod(self):
        """
        Controls conf file permissions.
        """
        mode = S_IMODE(stat(self.conf_file)[ST_MODE])
        self.log.debug(u'file permision is: %o' % mode)
        if mode & S_IRWXO or mode & S_IRWXG:
            self.log.warning(u'File is readable by "other" and/or' +
                             u' "group" (actual permission %o octal).' %
                             mode)
            self.log.warning(u'Consider setting permissions' +
                             u' to 600 octal.')

    def supersedes_config_with_cmd_line_options(self):
        """Updates defaults settings with command line options"""
        for sec in self.config.sections():
            for opt in self.config.options(sec):
                if opt in self.startopt.keys():
                    self.config.set(sec, opt, str(self.startopt.get(opt)))

    def use_envar(self):
        """Use MPD en.var. to set defaults"""
        mpd_host, mpd_port, passwd = utils.get_mpd_environ()
        if mpd_host:
            self.log.info(u'Env. variable MPD_HOST set to "%s"' % mpd_host)
            self.defaults['MPD']['host'] = mpd_host
        if passwd:
            self.log.info(u'Env. variable MPD_HOST contains password.')
            self.defaults['MPD']['password'] = passwd
        if mpd_port:
            self.log.info(u'Env. variable MPD_PORT set to "%s".'
                                  % mpd_port)
            self.defaults['MPD']['port'] = mpd_port

    def control_conf(self):
        """Get through options/values and set defaults if not in conf file."""
        # Control presence of obsolete settings
        for option in ['history', 'history_length', 'top_tracks']:
            if self.config.has_option('sima', option):
                self.log.warning('Obsolete setting found in conf file: "%s"'
                        % option)
        # Setting default if not specified
        for section in DEFAULT_CONF.iterkeys():
            if section not in self.config.sections():
                self.log.debug(u'[%s] NOT in conf file' % section)
                self.config.add_section(section)
                for option in self.defaults[section]:
                    self.config.set(section,
                            option,
                            self.defaults[section][option])
                    self.log.debug(
                            u'Setting option with default value: %s = %s' %
                            (option, self.defaults[section][option]))
            elif section in self.config.sections():
                self.log.debug(u'[%s] present in conf file' % section)
                for option in self.defaults[section]:
                    if self.config.has_option(section, option):
                        #self.log.debug(u'option "%s" set to "%s" in conf. file' %
                        #              (option, self.config.get(section, option)))
                        pass
                    else:
                        self.log.debug(
                                u'Option "%s" missing in section "%s"' %
                                (option, section))
                        self.log.debug(u'=> setting default "%s" (may not suit you…)' %
                                       self.defaults[section][option])
                        self.config.set(section, option,
                                        self.defaults[section][option])

    def init_config(self):
        """
        Use XDG directory standard if exists
        else use "HOME/(.config|.local/share)/mpd_sima/"
        http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
        """

        homedir = expanduser('~')

        if environ.get('XDG_DATA_HOME'):
            data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
        else:
            data_dir = join(homedir, '.local', 'share', DIRNAME)

        # If var folder is provided via CLI set data_dir accordingly
        if self.startopt.get('var_dir'):
            data_dir = join(self.startopt.get('var_dir'))

        if not isdir(data_dir):
            makedirs(data_dir)
            chmod(data_dir, 0700)

        if environ.get('XDG_CONFIG_HOME'):
            conf_dir = join(environ.get('XDG_CONFIG_HOME'), DIRNAME)
        else:
            conf_dir = join(homedir, '.config', DIRNAME)

        self.userdb_file = join(data_dir, 'sima.db')

        # If no conf_file is provided via CLI set default path to XDG
        if not self.startopt.get('conf_file'):
            # Create conf_dir if necessary
            if not isdir(conf_dir):
                makedirs(conf_dir)
                chmod(conf_dir, 0700)
            self.conf_file = join(conf_dir, CONF_FILE)

        config = ConfigParser.SafeConfigParser()

        # If no conf file present, uses defaults
        if not isfile(self.conf_file):
            self.config = config
            return

        self.log.info(u'Loading configuration from:  %s' % self.conf_file)
        self.control_mod()

        try:
            config.read(self.conf_file)
        except (MissingSectionHeaderError, ParsingError), err_conf:
            self.log.error(u'error loading %s:\n%s' %
                           (self.conf_file, err_conf))
            sys.exit(1)

        self.config = config


if __name__ == '__main__':
    pass

# VIM MODLINE
# vim: ai ts=4 sw=4 sts=4 expandtab
