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

from __future__ import with_statement
from time import sleep
from os.path import exists
from os.path import join
from shutil import copy

from traceback import print_exc
from utils import chmod

IGNORE = ("FreakshareNet", "SpeedManager")
#ignore this plugin configs

CONF_VERSION = 1

class ConfigParser:
    """
    holds and manage the configuration
    
    current dict layout:
    
    {
    
     section : { 
      option : { 
            value:
            type:
            desc:
      }
      desc: 
    
    }
    
    
    """

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        self.config = {} # the config values
        self.plugin = {} # the config for plugins
        self.oldRemoteData = {}
        
        self.checkVersion()
        
        self.readConfig()

        self.deleteOldPlugins()
    
    #----------------------------------------------------------------------
    def checkVersion(self, n=0):
        """determines if config need to be copied"""
        try:
            if not exists("pyload.conf"):
                copy(join(pypath,"module", "config", "default.conf"), "pyload.conf")
                
            if not exists("plugin.conf"):
                f = open("plugin.conf", "wb")
                f.write("version: "+str(CONF_VERSION))
                f.close()
            
            f = open("pyload.conf", "rb")
            v = f.readline()
            f.close()
            v = v[v.find(":")+1:].strip()
            
            if not v or int(v) < CONF_VERSION:
                copy(join(pypath,"module", "config", "default.conf"), "pyload.conf")
                print "Old version of config was replaced"
            
            f = open("plugin.conf", "rb")
            v = f.readline()
            f.close()
            v = v[v.find(":")+1:].strip()
              
            if not v or int(v) < CONF_VERSION:
                f = open("plugin.conf", "wb")
                f.write("version: "+str(CONF_VERSION))
                f.close()
                print "Old version of plugin-config replaced"
        except:
            if n < 3:
                sleep(0.3)
                self.checkVersion(n+1)
            else:
                raise
        
    #----------------------------------------------------------------------
    def readConfig(self):
        """reads the config file"""
        
        self.config = self.parseConfig(join(pypath,"module", "config", "default.conf"))
        self.plugin = self.parseConfig("plugin.conf")
        
        try:
            homeconf = self.parseConfig("pyload.conf")
            if homeconf["remote"].has_key("username"):
                if homeconf["remote"].has_key("password"):
                    self.oldRemoteData = {"username": homeconf["remote"]["username"]["value"], "password": homeconf["remote"]["username"]["value"]}
                    del homeconf["remote"]["password"]
                del homeconf["remote"]["username"]
            self.updateValues(homeconf, self.config)
            
        except Exception, e:
            print "Config Warning"
            print_exc()
        
    #----------------------------------------------------------------------
    def parseConfig(self, config):
        """parses a given configfile"""
        
        f = open(config)
        
        config = f.read()

        config = config.splitlines()[1:]
        
        conf = {}
        
        section, option, value, typ, desc = "","","","",""
        
        listmode = False
        
        for line in config:

            comment = line.rfind("#")
            if line.find(":", comment) < 0 and line.find("=", comment) < 0 and comment > 0:
                line = line.rpartition("#") # removes comments            
                if line[1]:
                    line = line[0]
                else:
                    line = line[2]
            
            line = line.strip()
            
            try:
            
                if line == "":
                    continue
                elif line.endswith(":"):
                    section, none, desc = line[:-1].partition('-')
                    section = section.strip()
                    desc = desc.replace('"', "").strip()
                    conf[section] = { "desc" : desc }
                else:
                    if listmode:
                        
                        if line.endswith("]"):
                            listmode = False
                            line = line.replace("]","")
                            
                        value += [self.cast(typ, x.strip()) for x in line.split(",") if x]
                        
                        if not listmode:
                            conf[section][option] = { "desc" : desc,
                                                      "type" : typ,
                                                      "value" : value} 
                        
                        
                    else:
                        content, none, value = line.partition("=")
                        
                        content, none, desc = content.partition(":")
                        
                        desc = desc.replace('"', "").strip()
    
                        typ, none, option = content.strip().rpartition(" ")

                        value = value.strip()
                        
                        if value.startswith("["):
                            if value.endswith("]"):                            
                                listmode = False
                                value = value[:-1]
                            else:
                                listmode = True
                          
                            value = [self.cast(typ, x.strip()) for x in value[1:].split(",") if x]
                        else:
                            value = self.cast(typ, value)
                        
                        if not listmode:
                            conf[section][option] = { "desc" : desc,
                                                      "type" : typ,
                                                      "value" : value}
                
            except Exception, e:
                print "Config Warning"
                print_exc()

                        
        f.close()
        return conf
        
    
    
    #----------------------------------------------------------------------
    def updateValues(self, config, dest):
        """sets the config values from a parsed config file to values in destination"""
                                
        for section in config.iterkeys():
    
            if dest.has_key(section):
                
                for option in config[section].iterkeys():
                    
                    if option == "desc": continue
                    
                    if dest[section].has_key(option):
                        dest[section][option]["value"] = config[section][option]["value"]
                        
                    #else:
                    #    dest[section][option] = config[section][option]
                
                
            #else:
            #    dest[section] = config[section]

    #----------------------------------------------------------------------
    def saveConfig(self, config, filename):
        """saves config to filename"""
        with open(filename, "wb") as f:
            chmod(filename, 0600)
            f.write("version: %i \n" % CONF_VERSION)
            for section in config.iterkeys():
                f.write('\n%s - "%s":\n' % (section, config[section]["desc"]))
                
                for option, data in config[section].iteritems():
                    
                    if option == "desc": continue
                    
                    if isinstance(data["value"], list):
                        value = "[ \n"
                        for x in data["value"]:
                            value += "\t\t" + str(x) + ",\n"
                        value += "\t\t]\n"
                    else:
                        if type(data["value"]) in (str,unicode):
                            value = data["value"] + "\n"
                        else:
                            value = str(data["value"]) + "\n"
                    try:
                        f.write('\t%s %s : "%s" = %s' % (data["type"], option, data["desc"], value) )
                    except UnicodeEncodeError:
                        f.write('\t%s %s : "%s" = %s' % (data["type"], option, data["desc"], value.encode("utf8")) )

    def cast(self, typ, value):
        """cast value to given format"""
        if type(value) not in (str, unicode):
            return value
        
        elif typ == "int":
            return int(value)
        elif typ == "bool":
            return True if value.lower() in ("1","true", "on", "an","yes") else False
        elif typ == "time":
            if not value: value = "0:00"
            if not ":" in value: value+=":00"
            return value
        elif typ in ("str","file","folder"):
            try:
                return value.encode("utf8")
            except:
                return value
        else:
            return value
                
    #----------------------------------------------------------------------
    def save(self):
        """saves the configs to disk"""
        
        self.saveConfig(self.config, "pyload.conf")
        self.saveConfig(self.plugin, "plugin.conf")
        
    #----------------------------------------------------------------------
    def __getitem__(self, section):
        """provides dictonary like access: c['section']['option']"""
        return Section(self, section)
    
    #----------------------------------------------------------------------
    def get(self, section, option):
        """get value"""
        val = self.config[section][option]["value"]
        try:
            if type(val) in (str,unicode):
                return val.decode("utf8")
            else:
                return val
        except:
            return val
        
    #----------------------------------------------------------------------
    def set(self, section, option, value):
        """set value"""

        value = self.cast(self.config[section][option]["type"], value)
            
        self.config[section][option]["value"] = value
        self.save()
        
    #----------------------------------------------------------------------
    def getPlugin(self, plugin, option):
        """gets a value for a plugin"""
        val = self.plugin[plugin][option]["value"]
        try:
            if type(val) in (str, unicode):
                return val.decode("utf8")
            else:
                return val
        except:
            return val
    
    #----------------------------------------------------------------------
    def setPlugin(self, plugin, option, value):
        """sets a value for a plugin"""

        value = self.cast(self.plugin[plugin][option]["type"], value)
        
        self.plugin[plugin][option]["value"] = value
        self.save()

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

    #----------------------------------------------------------------------
    def addPluginConfig(self, config):
        """adds config option with tuple (plugin, name, type, desc, default)"""
        
        if not self.plugin.has_key(config[0]):
            self.plugin[config[0]] = { "desc" : config[0],
                                       config[1] : {
                                           "desc" : config[3],
                                           "type" : config[2],
                                           "value" : self.cast(config[2], config[4])
                                       } }
        else:
            if not self.plugin[config[0]].has_key(config[1]):
                self.plugin[config[0]][config[1]] = {
                                           "desc" : config[3],
                                           "type" : config[2],
                                           "value" : self.cast(config[2], config[4])
                                       }

    def deleteOldPlugins(self):
        """ remove old plugins from config """

        for name in IGNORE:
            if self.plugin.has_key(name):
                del self.plugin[name]

########################################################################
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")