# Written by John Hoffman
# see LICENSE.txt for license information

# reads/writes a Windows-style INI file
# format:
#
#   aa = "bb"
#   cc = 11
#
#   [eee]
#   ff = "gg"
#
# decodes to:
# d = { '': {'aa':'bb','cc':'11'}, 'eee': {'ff':'gg'} }
#
# the encoder can also take this as input:
#
# d = { 'aa': 'bb, 'cc': 11, 'eee': {'ff':'gg'} }
#
# though it will only decode in the above format.  Keywords must be strings.
# Values that are strings are written surrounded by quotes, and the decoding
# routine automatically strips any.
# Booleans are written as integers.  Anything else aside from string/int/float
# may have unpredictable results.

from cStringIO import StringIO
from traceback import print_exc
from types import DictType, StringType
try:
    from types import BooleanType
except ImportError:
    BooleanType = None

try:
    True
except:
    True = 1
    False = 0

DEBUG = False

def ini_write(f, d, comment=''):
    try:
        a = {'':{}}
        for k,v in d.items():
            assert type(k) == StringType
            k = k.lower()
            if type(v) == DictType:
                if DEBUG:
                    print 'new section:' +k
                if k:
                    assert not a.has_key(k)
                    a[k] = {}
                aa = a[k]
                for kk,vv in v:
                    assert type(kk) == StringType
                    kk = kk.lower()
                    assert not aa.has_key(kk)
                    if type(vv) == BooleanType:
                        vv = int(vv)
                    if type(vv) == StringType:
                        vv = '"'+vv+'"'
                    aa[kk] = str(vv)
                    if DEBUG:
                        print 'a['+k+']['+kk+'] = '+str(vv)
            else:
                aa = a['']
                assert not aa.has_key(k)
                if type(v) == BooleanType:
                    v = int(v)
                if type(v) == StringType:
                    v = '"'+v+'"'
                aa[k] = str(v)
                if DEBUG:
                    print 'a[\'\']['+k+'] = '+str(v)
        r = open(f,'w')
        if comment:
            for c in comment.split('\n'):
                r.write('# '+c+'\n')
            r.write('\n')
        l = a.keys()
        l.sort()
        for k in l:
            if k:
                r.write('\n['+k+']\n')
            aa = a[k]
            ll = aa.keys()
            ll.sort()
            for kk in ll:
                r.write(kk+' = '+aa[kk]+'\n')
        success = True
    except:
        if DEBUG:
            print_exc()
        success = False
    try:
        r.close()
    except:
        pass
    return success


if DEBUG:
    def errfunc(lineno, line, err):
        print '('+str(lineno)+') '+err+': '+line
else:
    errfunc = lambda lineno, line, err: None

def ini_read(f, errfunc = errfunc):
    try:
        r = open(f,'r')
        ll = r.readlines()
        d = {}
        dd = {'':d}
        for i in xrange(len(ll)):
            l = ll[i]
            l = l.strip()
            if not l:
                continue
            if l[0] == '#':
                continue
            if l[0] == '[':
                if l[-1] != ']':
                    errfunc(i,l,'syntax error')
                    continue
                l1 = l[1:-1].strip().lower()
                if not l1:
                    errfunc(i,l,'syntax error')
                    continue
                if dd.has_key(l1):
                    errfunc(i,l,'duplicate section')
                    d = dd[l1]
                    continue
                d = {}
                dd[l1] = d
                continue
            try:
                k,v = l.split('=',1)
            except:
                try:
                    k,v = l.split(':',1)
                except:
                    errfunc(i,l,'syntax error')
                    continue
            k = k.strip().lower()
            v = v.strip()
            if len(v) > 1 and ( (v[0] == '"' and v[-1] == '"') or
                                (v[0] == "'" and v[-1] == "'") ):
                v = v[1:-1]
            if not k:
                errfunc(i,l,'syntax error')
                continue
            if d.has_key(k):
                errfunc(i,l,'duplicate entry')
                continue
            d[k] = v
        if DEBUG:
            print dd
    except:
        if DEBUG:
            print_exc()
        dd = None
    try:
        r.close()
    except:
        pass
    return dd
