# Copyright (c) 2003-2004 Hyriand. All rights reserved.
#
# Based on code from PySoulSeek, original copyright note:
# Copyright (c) 2001-2003 Alexander Kanavin. All rights reserved.

"""
This module contains configuration classes for Nicotine.
"""

import ConfigParser
import string
import os
import cPickle
import shelve
import sys
import thread

from utils import _

class Config:
    """ 
    This class holds configuration information and provides the 
    following methods:

    needConfig() - returns true if configuration information is incomplete
    readConfig() - reads configuration information from ~/.nicotine/config
    setConfig(config_info_dict) - sets configuration information
    writeConfig - writes configuration information to ~/.nicotine/config

    The actual configuration information is stored as a two-level dictionary.
    First-level keys are config sections, second-level keys are config 
    parameters.
    """
    def __init__(self, filename):
	self.config_lock = thread.allocate_lock()
	self.config_lock.acquire()

        self.filename = filename
	self.parser = ConfigParser.ConfigParser()
	self.parser.read([self.filename])
	if sys.platform.startswith("win"):
		LOGDIR="C:\\My Documents"
	else:
		LOGDIR="~"
        self.sections = {"server":{"server":('server.slsknet.org', 2240), \
	"login":None,"passw":None,"firewalled":1, \
	"autosearch":[],"autoreply":"", "roomencoding": {}, "userencoding": {}, \
	"portrange": (2234,2239), "enc":"utf-8","userlist":[], \
	"banlist":[], "ignorelist":[],"autojoin":["nicotine"],"autoaway":15}, \
	"transfers":{"downloaddir":None,"sharedownloaddir":1,"shared":None, \
	"uploadbandwidth":10,"uselimit":0,"uploadlimit":150,"limitby":1,\
	"preferfriends":0, "useupslots":0, "uploadslots":2, "incompletedir":"", \
	"afterfinish":"", "afterfolder":"", "lock":1, \
	"usecustomban":0,"customban":"don't bother to retry", "queuelimit":100,\
	"friendsonly":0, "friendsnolimits":0, \
	"geoblock": 0, "geopanic": 0, "geoblockcc": [""], \
	"downloads":[],"sharedfiles":{},"sharedfilesstreams":{}, \
	"wordindex":{},"fileindex":{},"sharedmtimes":{},"rescanonstartup":0}, \
	"userinfo":{"descr":"''","pic":"","descrutf8":0},"logging": {"logcollapsed":0, \
	"logsdir":os.path.expanduser(LOGDIR),"privatechat":0,"chatrooms":0,"transfers":0}, \
	"searches":{"maxresults":50,"re_filter":0,"history":[], \
	"enablefilters":0, "defilter":["","","","",0,""], "filtercc":[], \
	"filterin":[],"filterout":[],"filtersize":[],"filterbr":[]}, \
 	"ui":{"chatme":"FOREST GREEN", "chatremote":"","chatlocal":"BLUE", \
 	"chathilite":"red", "search":"","searchq":"GREY", "decimalsep":",", \
 	"roomlistcollapsed": 0, "tabclosers": 1}, \
 	"urls":{"urlcatching":1,"protocols":{"http":"mozilla \"%s\" &"}, \
 	"humanizeurls":1}, "interests": {"likes":[], "dislikes":[]}, \
	"ticker": {"default": "", "rooms": {}, "hide": 0}}

 	try:
 	    f = open(filename+".alias")
 	    self.aliases = cPickle.load(f)
 	    f.close()
 	except:
 	    self.aliases = {}
	self.config_lock.release()
    
    
    def needConfig(self):
	for i in self.sections.keys():
	    for j in self.sections[i].keys():
		if self.sections[i][j] is None or self.sections[i][j] == '' and i not in ("userinfo", "ui", "ticker") and j not in ("incompletedir", "autoreply", 'afterfinish','afterfolder', 'geoblockcc'):
		    return 1
	return 0

    def readConfig(self):
        self.config_lock.acquire()
	path, fn = os.path.split(self.filename)
	try:
	    if not os.path.isdir(path):
	        os.makedirs(path)
	except OSError, msg:
	    print "Can't create directory '%s', reported error: %s" % (path, msg)
	
	for i in self.parser.sections():
	    for j in self.parser.options(i):
		val = self.parser.get(i,j, raw = 1)
		if i not in self.sections.keys():
		    print "Bogus config section:",i
		elif j not in self.sections[i].keys() and j != "filter":
		    print "Bogus config option",j,"section",i
		elif j in ['login','passw','enc','downloaddir','customban','descr','pic','logsdir','incompletedir', 'autoreply', 'afterfinish', 'afterfolder', 'default'] or (i == "ui" and j not in ["roomlistcollapsed", "tabclosers"]):
		    self.sections[i][j] = val
		else:
		    try:
		        self.sections[i][j] = eval(val, {})
		    except:
			self.sections[i][j] = None
			print "CONFIG ERROR: Couldn't decode %s section %s value %s" % (j, i, val)
	autojoin = self.sections["server"]["autojoin"]
	for user in self.sections["server"]["userlist"]:
		if len(user) == 2:
			user += [0,0]
	
	if "pyslsk" in autojoin and not "nicotine" in autojoin:
	    autojoin.append("nicotine")
	
	# decode the userinfo from local encoding to utf8 (1.0.3 -> 1.0.4 change)
	if not self.sections["userinfo"]["descrutf8"]:
	    try:
	        import locale
	        descr = eval(self.sections["userinfo"]["descr"], {}).decode(locale.nl_langinfo(locale.CODESET), "replace").encode("utf-8", "replace")
	        self.sections["userinfo"]["descr"] = descr.__repr__()
	    except:
	        pass
	    self.sections["userinfo"]["descrutf8"] = 1
	        
        sharedfiles = None
        sharedfilesstreams = None
        wordindex = None
        fileindex = None
        sharedmtimes = None
	try:
	    sharedfiles = shelve.open(self.filename+".files.db")
	    sharedfilesstreams =shelve.open(self.filename+".streams.db")
	    wordindex = shelve.open(self.filename+".wordindex.db")
	    fileindex = shelve.open(self.filename+".fileindex.db")
	    sharedmtimes = shelve.open(self.filename+".mtimes.db")
	except:
	    print _("Shared files database seems to be corrupted, rescan your shares")

	    if sharedfiles:
	        sharedfiles.close()
	    try:
	        os.unlink(self.filename+'.files.db')
	    except:
	        pass
            sharedfiles = shelve.open(self.filename+".files.db",flag='n')

	    if sharedfilesstreams:
	        sharedfilesstreams.close()
	    try:
	        os.unlink(self.filename+'.streams.db')
	    except:
	        pass
            sharedfilesstreams =shelve.open(self.filename+".streams.db",flag='n')

	    if wordindex:
	        wordindex.close()
	    try:
	        os.unlink(self.filename+'.wordindex.db')
	    except:
	        pass
            wordindex = shelve.open(self.filename+".wordindex.db",flag='n')

	    if fileindex:
	        fileindex.close()
	    try:
	        os.unlink(self.filename+'.fileindex.db')
	    except:
	        pass
            fileindex = shelve.open(self.filename+".fileindex.db",flag='n')

	    if sharedmtimes:
	        sharedmtimes.close()
	    try:
	        os.unlink(self.filename+'.mtimes.db')
	    except:
	        pass
            sharedmtimes = shelve.open(self.filename+".mtimes.db",flag='n')

	self.sections["transfers"]["sharedfiles"] = sharedfiles
	self.sections["transfers"]["sharedfilesstreams"] = sharedfilesstreams
	self.sections["transfers"]["wordindex"] = wordindex
	self.sections["transfers"]["fileindex"] = fileindex
	self.sections["transfers"]["sharedmtimes"] = sharedmtimes
	
	if self.sections["server"]["server"][0] == "mail.slsknet.org":
	    self.sections["server"]["server"] = ('server.slsknet.org', 2240)
	
	self.config_lock.release()
 
    def writeConfig(self):
        self.config_lock.acquire()
        for i in self.sections.keys():
            if not self.parser.has_section(i):
                self.parser.add_section(i)
            for j in self.sections[i].keys():
                if j not in ["sharedfiles","sharedfilesstreams","wordindex","fileindex","sharedmtimes"]:
                    self.parser.set(i,j,self.sections[i][j])
                else:
                    self.parser.remove_option(i,j)

	path, fn = os.path.split(self.filename)
	try:
	    if not os.path.isdir(path):
	        os.makedirs(path)
	except OSError, msg:
	    print _("Can't create directory '%s', reported error: %s") % (path, msg)
	
        oldumask = os.umask(0077)

        try:
            s = os.stat(self.filename)
            if s.st_size > 0:
                try:
                    os.remove(self.filename + ".old")
                except OSError, s:
                    print s

                try:
                    os.rename(self.filename, self.filename + ".old")
                except OSError, error:
                    print _("Can't back config file up, error: %s") % error
        except OSError:
            pass
            
	try:
	    f = open(self.filename,"w")
	except IOError, (errno, strerror):
            print _("Can't save config file, I/O error(%s): %s") % (errno, strerror)
	else:
	    self.parser.write(f)
	    f.close()
        os.umask(oldumask)
        # A paranoid precaution since config contains the password
        try:
            os.chmod(self.filename, 0600)
        except:
            pass
        self.config_lock.release()

    def setShares(self,files,streams,wordindex,fileindex,mtimes):
	if self.sections["transfers"]["sharedfiles"] == files:
	    return
        self.config_lock.acquire()
	self.sections["transfers"]["sharedfiles"].close()
	self.sections["transfers"]["sharedfilesstreams"].close()
	self.sections["transfers"]["sharedmtimes"].close()
	self.sections["transfers"]["wordindex"].close()
	self.sections["transfers"]["fileindex"].close()
	self.sections["transfers"]["sharedfiles"] = shelve.open(self.filename+".files.db",'n')
	self.sections["transfers"]["sharedfilesstreams"] = shelve.open(self.filename+".streams.db",'n')
	self.sections["transfers"]["sharedmtimes"] = shelve.open(self.filename+".mtimes.db",'n')
	self.sections["transfers"]["wordindex"] = shelve.open(self.filename+".wordindex.db",'n')
	self.sections["transfers"]["fileindex"] = shelve.open(self.filename+".fileindex.db",'n')
	for (i,j) in files.items():
	    self.sections["transfers"]["sharedfiles"][i] = j
	for (i,j) in streams.items():
           self.sections["transfers"]["sharedfilesstreams"][i] = j
	for (i,j) in mtimes.items():
            self.sections["transfers"]["sharedmtimes"][i] = j
	for (i,j) in wordindex.items():
	    self.sections["transfers"]["wordindex"][i] = j
        for (i,j) in fileindex.items():
            self.sections["transfers"]["fileindex"][i] = j
        self.config_lock.release()

	

    def writeShares(self):
        self.config_lock.acquire()
	self.sections["transfers"]["sharedfiles"].sync()
	self.sections["transfers"]["sharedfilesstreams"].sync()
	self.sections["transfers"]["wordindex"].sync()
	self.sections["transfers"]["fileindex"].sync()
	self.sections["transfers"]["sharedmtimes"].sync()
	self.config_lock.release()

    def pushHistory(self, history, text, max):
	if text in history:
	    history.remove(text)
	elif len(history) >= max:
	   del history[-1]
	history.insert(0, text)
	self.writeConfig()
	
    def writeAliases(self):
        self.config_lock.acquire()
        f = open(self.filename+".alias","w")
        cPickle.dump(self.aliases, f, 1)
        f.close()
        self.config_lock.release()

    def AddAlias(self, rest):
	if rest:
	    args = rest.split(" ", 1)
	    if len(args) == 2:
	        if args[0] in ("alias", "unalias"):
	            return "I will not alias that!\n"
	        self.aliases[args[0]] = args[1]
	        self.writeAliases()
	    if self.aliases.has_key(args[0]):
	        return "Alias %s: %s\n" % (args[0], self.aliases[args[0]])
	    else:
	        return _("No such alias (%s)") % rest + "\n"
        else:
           m = "\n" + _("Aliases:") + "\n"
           for i in self.aliases.keys():
               m = m + "%s: %s\n" % (i, self.aliases[i])
           return m+"\n"

    def Unalias(self, rest):
	if rest and self.aliases.has_key(rest):
	    x = self.aliases[rest]
	    del self.aliases[rest]
	    self.writeAliases()
	    return _("Removed alias %s: %s") % (rest, x) + "\n"
	else:
	    return _("No such alias (%s)") % rest + "\n"
