import sys
import re
from pprint import pprint
import chardet


from tklib.tkutils import pconv, pint, pfloat, conv_float
from tklib.tkfile import tkFile
import tklib.tkre as tkre


infile  = 'test.ini'
outfile = 'test-out.ini'

class tkIniFile(tkFile):
    """ 
    tklib Inifile class
    """

    var = {}
    # args: ProgramPath, UseAsIniFile, CreateIniFile
    def __init__(self, path = None, mode = 'r', OpenFile = 1, IsPrint = True, **args):
        super().__init__(path, mode, OpenFile, IsPrint = IsPrint, **args)
#        self.fp   = None
#        self.path = None
#        self.mode = 'r'
#        self.path = self.IfYes(path is not None, path, self.path)
#        self.mode = self.IfYes(mode is not None, mode, self.mode)

        self.KeyHead       = "System/"
        self.SystemKeyHead = self.KeyHead
        self.ProgramPath   = ''
        self.CreateIniFile = 0
        self.UseAsIniFile  = 0
        self.update(**args)

        self.var[self.KeyHead + "UseAsIniFile"]     = self.UseAsIniFile;
        self.var[self.KeyHead + "CreateIniFile"]    = self.CreateIniFile;
        self.var[self.SystemKeyHead +"ProgramFile"] = self.ProgramPath;

# this called from super().__init__
#        if self.path:
#            self.SetPath(self.path, self.mode)
#            self.Open(self.path, self.mode)

    def __del__(self):
        self.close()
        super(tkIniFile, self).__del__()

    def __str__(self):
        return self.ClassPath()


    def read_all(self, path = None, section = None, AddSection = False, ignore_keys = [], IsPrint = False, encoding = None):
        if not self.fp or path is not None:
            self.close()

            self.open(path = path, mode = 'r', IsPrint = IsPrint, encoding = encoding)

        if not self.fp:
            return None

        inf = {}
        secname = ''
        while 1:
            line = self.ReadLine()
            if not line:
                return inf
            if line == '':
                continue
            if tkre.Match('#', line):
                continue

            match = re.match(r'\s*\[(.*?)]', line)
            if match:
                secname = match.group(1)
                continue

            if section is not None and secname != section:
                continue

            list = tkre.Match(r'\s*(\S.*?)\s*=\s*(.*)\s*$', line)
            if list is not None and len(list) >= 2:
                if list[1] in ignore_keys:
                    continue

                if AddSection:
                    key = secname + '/' + list[1]
                else:
                    key = list[1]

                val = list[2]
                inf[key] = val
#                print("{}: {}".format(key, val))

        self.close()

        return inf

    def ReadAll(self, path = None, AddSection = 0, ignore_keys = [], IsPrint = False, encoding = None):
        return self.read_all(path = path, AddSection = AddSection, ignore_keys = ignore_keys, 
                        IsPrint = IsPrint, encoding = encoding)

    def GetNextSection(self):
        lines = []
        line = self.ReadLine()
        if line == '':
            return '', lines
#        print("l1: ", line, end='')
        match = re.match(r'\s*\[(.*?)]', line)
        if match:
            secname = match.group(1)
        else:
            secname = ''
        lines.append(line)
#        print("sec [{}]".format(secname))
        while 1:
            pos = self.Tell()
            line = self.ReadLine()
#            print("l2: ", line, end='')
            if line == '':
                break
            if re.match(r'\s*\[', line):
                self.Seek(pos)
                break
            lines.append(line)
        return secname, lines
        
    def FindSectionFromStr(self, section, lines = None):
        if lines is None:
            lines = self.lines

        i0   = None
# insert point: the last non-blank line in the section
        iins = None
        i1   = None
        for i in range(len(lines)):
            regsection = re.sub(r'([\[\]\(\)])', r'\\\1', section)
            if tkre.Match(r'\s*\[{}\]'.format(regsection), lines[i]):
                i0 = i
                break
        if i0 is not None:
            i1 = len(lines)
            for i in range(i0+1, len(lines)):
                if tkre.Match(r'\s*\[(.*?)]', lines[i]):
                    i1 = i - 1
                    break
# the insertion point is the last non-blank line in the section
                if tkre.Search(r'\S', lines[i]):
                    iins = i
#if the sction has no key, iins will be the next line to the section title
            if iins is None:
                iins = i1
            return i0, iins, i1 - 1
        return None, None, None

    def FindLastNonBlankLine(self, lines = None):
        if lines is None:
            lines = self.lines

        n = len(lines)
        il = 0
        for i in range(0, n):
            if tkre.Search(r'\S', lines[i]):
                il = i
        return il

    def FindKeyFromSectionRange(self, i0, i1, key, lines = None):
        if lines is None:
            lines = self.lines

        n = len(lines)
        if i1+1 > n:
            i1 = n - 1
#        print("range: ", i0, i1)
        for i in range(i0, i1+1):
#            print("i=", i, "  key=", key)
#            print(lines[i])
            regkey = re.sub(r'([\[\]\(\)])', r'\\\1', key)
#            print("s, reg=", key, regkey)
#            exit()
            if tkre.Match(r'\s*{}\s*='.format(regkey), lines[i]):
#                print("hit")
                return i
        return None

    def WriteString(self, section, key, value, outfile = None, IsPrint = False):
        return self.write_string(section, key, value, outfile = outfile, is_print = IsPrint)

    def write_from_scratch(self, outfile, section, **kwargs):
        fp = tkFile(outfile, mode = 'w')
        if not fp: return False

        fp.write(f"[{section}]\n")
        for key in kwargs:
            val = conv_float(kwargs[key])
            fp.write(f"{key}={val}\n")

        fp.close()
        return True

    def write_string(self, section, key, value, outfile = None, is_print = False):
        debug = 0
        if outfile is None: outfile = self.path

        value = conv_float(value)

        lines = []
        if not self.open(path = outfile, IsPrint = False): #is_print):
            if is_print: print("Warning in tkinifile.WriteString: Can not read [{}]".format(self.path))
        else:
            lines = self.ReadLineList()
            self.close()

        i0, inotblank, i1 = self.FindSectionFromStr(section, lines)
        if debug:
            print(f"190 section={section}: key={key}: i0={i0} - inotblank={inotblank} - i1={i1}")

# Section is not found
        if i0 is None:
            il = self.FindLastNonBlankLine(lines)
            if debug:
#                print(f"{lines=}")
                print(f"197 i0 is None: il={il}", il)
# if file includes some data, add the new section with a blank line
            if il is not None and il > 0:
                if debug:
                    print(f"201 i0 is None: il={il}")
                lines.insert(il+1, "\n")
                il += 1
# add the new section
            lines.insert(il+1, "[{}]\n".format(section))
            lines.insert(il+2, "{}={}\n".format(key, value))
# section is found
        else:
            ik = self.FindKeyFromSectionRange(i0, inotblank, key, lines)
            if debug:
                print(f"211  key={key} ik={ik}")
# key is not found, will be added to the next line
            if ik is None:
                ik = inotblank
                lines.insert(ik+1, "{}={}\n".format(key, value))
# the key line ik will be replaced
            else:
                lines[ik] = "{}={}\n".format(key, value)

#        print("lines:******************")
#        for l in lines:
#            print(l, end='')
#        exit()

        out = tkIniFile(outfile, 'w')
        if out.fp is None:
            print("false")
            return False

#        out.Rewind()
        ret = out.write_lines(lines)

        out.close()

        return ret

    def GetString(self, section = None, key = None, defstr = None, IsPrint = False):
        return self.get_string(section = section, key = key, def_val = defstr, is_print = IsPrint)
        
    def get_string(self, section = None, key = None, def_val = None, is_print = False):
        re_flag = re.IGNORECASE

        self.Rewind()
        if section is None:
            return self.ReadLines()
        if key is None:
            return self.GetSection(section, is_print = is_print)
        
        self.open(path = self.path, mode = self.mode, IsPrint = is_print)
        if not self.fp:
            if is_print:
                self.errormsg("Error in tkIniFile.GetString(): Can not read [{}]".format(self.path))
            return def_val

        if section != "":
            key0 = f"^\\[{section}\\]"
            line = self.SkipTo(key0, 0)
            if line is None:
                return def_val
#        print("section header = ", line)

#        key0 = r"^\s*{}\s*=".format(key)
        key0 = r"^\s*{}\s*=\s*(.*)\s*$".format(key)
#        print("key0=[{}]".format(key0))
        while 1:
            line = self.ReadLine()
            if not line:
                return def_val

#            print(f"{line=}")

            match = re.match(r"\s*\[", line)
            if match:
                return def_val
            match = re.search(key0, line, re_flag)
            if match:
                str = match.group(1)
#                print("match:[{}]".format(str))
#                str = re.sub(key0, '', line)
#                str = self.DelCRLF(str)
                return str
        return def_val


    def GetSection(self, section):
        re_flag = re.IGNORECASE
        if section is None:
            return self.ReadLines()

        lines = []
#        self.Open(self.path, self.mode)
#        if not self.fp:
#            self.errormsg(sys._getframe().f_code.co_name, 
#                "Can not read [{}]".format(self.path))
#            return lines

        self.Rewind()
        key0 = "^\\[{}\\]".format(section)
        line = self.SkipTo(key0, 0)
        if line is None:
            return ''
#        print("section header = ", line)

        while 1:
            line = self.ReadLine()
            if line is None:
                return defstr
            match = re.match("\\s*[", line)
#            match = re.match("\\s*\[", line)
            if match:
                return lines
            line = self.DelCRLF(line)
            lines.append(line)                

        return lines


def main():
    ini = tkIniFile(infile, 'r')

    s = ini.ReadLine()
    print("")
    print("first line=[{}]".format(s))

    title = ini.GetString("Boot", "Title", "none")
    print("")
    print("title=[{}]".format(title))
    sec = ini.GetString("Boot")
    print("")
    print("section [{}]=".format("Boot"), sec)

#    ini.WriteString("EditFile", "fname", "a.txt", outfile)
    print("")
    print("WriteString test")
#    ini.WriteString("EditFile", "fname2", "abc.txt", outfile)
    ini.WriteString("EditFile", "fname2", "abc.txt")

    ini.close()
    

if __name__ == "__main__":
    main()
