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

from __future__ import with_statement
from time import sleep
from os.path import exists
from gettext import gettext

from module.utils.fs import chmod

CONF_VERSION = 2

from converter import from_string
from new_collections import namedtuple, OrderedDict
from default import make_config

SectionTuple = namedtuple("SectionTuple", "name description long_desc config")
ConfigData = namedtuple("ConfigData", "name type description default")

class ConfigParser:
    """
    Holds and manage the configuration + meta data.
    Actually only the values are read from disk, all meta data have to be provided first via addConfigSection.
    """

    CONFIG = "pyload.conf"
    PLUGIN = "plugin.conf"

    def __init__(self):
        """Constructor"""

        # core config sections from pyload
        self.baseSections = []

        # Meta data information
        self.config = OrderedDict()
        # The actual config values
        self.values = {}

        self.changeCB = None # callback when config value was changed

        self.checkVersion()

        self.loadDefault()
        self.parseValues(self.CONFIG)

    def loadDefault(self):
        make_config(self)

    def checkVersion(self):
        """Determines if config need to be deleted"""
        e = None
        # workaround conflict, with GUI (which also access the config) so try read in 3 times
        for i in range(0, 3):
            try:
                for conf in (self.CONFIG, self.PLUGIN):
                    if exists(conf):
                        f = open(conf, "rb")
                        v = f.readline()
                        f.close()
                        v = v[v.find(":") + 1:].strip()

                        if not v or int(v) < CONF_VERSION:
                            f = open(conf, "wb")
                            f.write("version: " + str(CONF_VERSION))
                            f.close()
                            print "Old version of %s deleted" % conf
                    else:
                        f = open(conf, "wb")
                        f.write("version:" + str(CONF_VERSION))
                        f.close()

            except Exception, ex:
                e = ex
                sleep(0.3)
        if e: raise e


    def parseValues(self, filename):
        """read config values from file"""
        f = open(filename, "rb")
        config = f.readlines()[1:]

        # save the current section
        section = ""

        for line in config:
            line = line.strip()

            # comment line, different variants
            if not line or line.startswith("#") or line.startswith("//") or line.startswith(";"): continue

            if line.startswith("["):
                section = line.replace("[", "").replace("]", "")

                if section not in self.config:
                    print "Unrecognzied section", section
                    section = ""

            else:
                name, non, value = line.rpartition("=")
                name = name.strip()
                value = value.strip()

                if not section:
                    print "Value without section", name
                    continue

                if name in self.config[section].config:
                    self.set(section, name, value, sync=False)
                else:
                    print "Unrecognized option", section, name


    def save(self):
        """saves config to filename"""

        # seperate pyload and plugin conf
        configs = []
        for c in (self.CONFIG, self.PLUGIN):
            f = open(c, "wb")
            configs.append(f)
            chmod(c, 0600)
            f.write("version: %i\n\n" % CONF_VERSION)


        # write on 2 files
        for section, data in self.config.iteritems():
            f = configs[0] if section in self.baseSections else configs[1]

            f.write("[%s]\n" % section)

            for option, data in data.config.iteritems():
                value = self.get(section, option)
                if type(value) == unicode: value = value.encode("utf8")
                else: value = str(value)

                f.write('%s = %s\n' % (option, value))

            f.write("\n")

        [f.close() for f in configs]

    def __getitem__(self, section):
        """provides dictonary like access: c['section']['option']"""
        return Section(self, section)

    def get(self, section, option):
        """get value"""
        if option in self.values[section]:
            return self.values[section][option]
        else:
            return self.config[section].config[option].default

    def set(self, section, option, value, sync=True):
        """set value"""

        data = self.config[section].config[option]
        value = from_string(value, data.type)

        # only save when different to defaul values
        if value != data.default or (option in self.values[section] and value != self.values[section][option]):
            self.values[section][option] = value
            if sync:
                if self.changeCB: self.changeCB(section, option, value)
                self.save()

    def getPlugin(self, *args):
        """gets a value for a plugin"""
        ret = self.get(*args)
        print "Deprecated method getPlugin%s -> %s" % (str(args), ret)
        return ret

    def setPlugin(self, *args):
        """sets a value for a plugin"""
        print "Deprecated method setPlugin%s" % str(args)
        self.set(*args)

    def getMetaData(self, section, option):
        """ get all config data for an option """
        return self.config[section].config[option]

    def getBaseSections(self):
        for section, data in self.config.iteritems():
            if section in self.baseSections:
                yield section, data
        return

    def getPluginSections(self):
        for section, data in self.config.iteritems():
            if section not in self.baseSections:
                yield section, data
        return

    def addConfigSection(self, section, name, desc, long_desc, config, base=False):
        """Adds a section to the config. `config` is a list of config tuples as used in plugin api definied as:
        Either (name, type, verbose_name, default_value) or
                (name, type, verbose_name, short_description, default_value)
        The ordner of the config elements are preserved with OrdererDict
        """
        d = OrderedDict()

        for entry in config:
            if len(entry) == 5:
                conf_name, type, conf_desc, conf_verbose, default = entry
            else: # config options without tooltip / description
                conf_name, type, conf_desc, default = entry
                conf_verbose = ""

            d[conf_name] = ConfigData(gettext(conf_desc), type, gettext(conf_verbose), from_string(default, type))

        if base:
            if section not in self.baseSections: self.baseSections.append(section)
        elif section in self.baseSections:
            return # would overwrite base section

        data = SectionTuple(gettext(name), gettext(desc), gettext(long_desc), d)
        self.config[section] = data

        if section not in self.values:
            self.values[section] = {}

class Section:
    """provides dictionary like access for configparser"""

    def __init__(self, parser, section):
        """Constructor"""
        self.parser = parser
        self.section = section

    def __getitem__(self, item):
        """getitem"""
        return self.parser.get(self.section, item)

    def __setitem__(self, item, value):
        """setitem"""
        self.parser.set(self.section, item, value)


if __name__ == "__main__":
    pypath = ""

    from time import time

    a = time()

    c = ConfigParser()

    b = time()

    print "sec", b - a

    print c.config

    c.saveConfig(c.config, "user.conf")