import os, os.path, sys

class Reader:
    def __init__ (self, filename, config):
        self.config        = config
        self.filename      = filename
        self.modules       = {}
        self.namespaces    = []
        self.classes       = {}
        self.elements      = {}
        self.globals       = {}
        self.nsStack       = []
        self.ns            = ""
        self.moduleText    = {}
        self.notes         = {}
        self.pyBookmarks   = []
        self.cppBookmarks  = []
        self.lastModule    = ""
        self.classStack    = []

    def nsName (self):
        if len (self.nsStack) > 1:
            self.ns = ".".join (self.nsStack)
        elif len (self.nsStack) == 1:
            self.ns = self.nsStack [0]
        else:
            self.ns = ""

        return self.ns

    def splitFields (self, s):
        f = s.split ()
        for i in range (len (f)):
            f [i] = f[i].strip ()
        return f

    def splitFieldsAddName (self, s, name):
        head, tail = s.split (":", 1)
        return [name, head.split () [1].strip (),tail]

    def readfile (self):
        self.module = ""
        self.ns     = ""
        self.cls    = ""
        self.entity = ""

#        self.readNotes ()
#        self.readBookmarks ()

        f = open (self.filename, "r")
        for line in f:
            if line.startswith ("module"):
                field = self.splitFields (line)
                if field [1] != self.module:
                    self.module = field [1]
                    self.newModule ()

            elif line.startswith ("namespace"):
                field = self.splitFields (line)
                if field [1] != self.ns:
                    self.namespaces.append (field [1])
                    self.nsStack.append (field [1])
                    self.nsName ()
                    self.newNamespace (field)

            elif line.startswith ("endns"):
                self.nsStack.pop ()
                self.nsName ()

            elif line.startswith ("class"):
                field = self.splitFieldsAddName (line, "class")
                self.cls = field [1]
                self.classStack.append (self.cls)
                self.newClass (field)

            elif line.startswith ("endcls"):
                if self.classStack:
                    self.classStack.pop ()
                    
                if self.classStack:                    
                    self.cls = self.classStack [-1]                                                
                else:
                    self.cls = ""                   

            elif line.startswith ("typedef"):
                self.addTypedef (field)

            elif line.startswith ("enum"):
                field = self.splitFieldsAddName (line, "enum")
                self.addItem (self.uniqueName ("enum", field [1]), field)

            elif line.startswith ("glenum"):
                field = self.splitFieldsAddName (line, "enum")
                self.addGlEnum (field)

            elif line.startswith ("function"):
                field = self.splitFieldsAddName (line, "method")
                self.addItem (self.uniqueName ("method", field [1]), field)

            elif line.startswith ("global"):
                field = self.splitFieldsAddName (line, "global")
                self.addGlobal (field)

            elif line.startswith ("operator"):
                field = self.splitFieldsAddName (line, "global")
                field [1] = field [1].replace ("<", "&lt;")
                field [1] = field [1].replace (">", "&gt;")
                self.addItem (self.uniqueName ("operator", field [1]), field)

            elif line.startswith ("variable"):
                field = self.splitFields (line)
                self.addItem ("variable:" + field [1], field)

    def newModule (self):
        self.modules [self.module] = []

    def newNamespace (self, field):
        fqName = ".".join (self.nsStack)
        if not fqName in self.namespaces:
            self.namespaces.append (fqName)

        if not fqName in self.modules [self.module]:
            self.modules [self.module].append (fqName)

        if not fqName in self.classes:
            self.classes [fqName] = []

    def newClass (self, field):
        if not field [1] in self.modules [self.module]:
            self.modules [self.module].append (field [1])
        if not field [1] in self.classes:
            self.classes [field [1]] = []
        self.elements  [":".join ([self.module, field [1]])] = field [2]

        if self.ns:
            self.classes [self.ns].append ("class:" + field [1])

    def addTypedef (self, field):
        self.classes [self.cls].append ("typedef:" + field [1])
        self.elements [":".join ([self.module, self.cls, "etypedef", field [1]])] = field [2:]

    def uniqueName (self, category, name, glob = False):
        if glob:
            d    = self.globals
            dkey = self.module
        else:
            d    = self.classes
            dkey = self.cls

        original = name
        i = 1

        while ":".join ([category, name]) in d [dkey]:
            name =  "-".join ([original, str (i)])
            i += 1

        return ":".join ([category, name])

    def addItem (self, key, field):
        self.classes [self.cls].append (key)
        self.elements [":".join ([self.module, self.cls, key])] = field [2:]

    def addGlEnum (self, field):
        if self.module not in self.globals:
            self.globals [self.module] = []

        if not self.ns:
            key = self.uniqueName ("glenum", field [1], True)
            self.globals [self.module].append (key)
            self.elements [":".join ([self.module, key])] = field [2:]
        else:
            key = self.uniqueName ("enum", field [1], True)
            self.classes [self.ns].append (key)
            self.elements [":".join ([self.module, self.ns, key])] = field [2:]

    def addGlobal (self, field):
        if self.module not in self.globals:
            self.globals [self.module] = []

        if not self.ns:
            key = self.uniqueName ("global", field [1], True)
            self.globals [self.module].append (key)
            self.elements [":".join ([self.module, key])] = field [2:]
        else:
            key = self.uniqueName ("method", field [1], True)
            self.classes [self.ns].append (key)
            self.elements [":".join ([self.module, self.ns, key])] = field [2:]


    def readModuleText (self, module):
        if module == self.lastModule:
            return True

        self.lastModule = module

        fn = os.path.join (os.path.dirname (self.config ["PyKDERef"]), module + ".txt")
        if not os.path.exists (fn):
            return False

        f = open (fn, "r")
        self.moduleText.clear ()
        collect = False
        for line in f:
            if line.startswith ("$end$"):
                if collect:
                    self.moduleText [key] = text
                collect = False
                continue
            elif line [0] == "$" and line [-2] == "$":
                key = line [1:-2]
                collect = True
                text = ""
                continue

            if collect:
                text += line

        f.close ()
        return True

#    def readNotes (self):
#        fn = os.path.join (self.config ["wabbit"], "notes.txt")
#        if not os.path.exists (fn):
#            return False

#        f = open (fn, "r")
#        self.notes.clear ()
#        collect = False
#        for line in f:
#            if line.startswith ("$end$"):
#                if collect:
#                    self.notes [key] = text
#                collect = False
#                continue
#            elif line [0] == "$" and line [-2] == "$":
#                key = line [1:-2]
#                collect = True
#                text = ""
#                continue

#            if collect:
#                text += line

#        f.close ()
#        return True

#    def writeNotes (self):
#        oldfn = os.path.join (self.config ["wabbit"], "notes.txt")
#        newfn = oldfn + ".new"

#        f = open (newfn, "w")

#        for key in self.notes:
#            f.write ("\n".join (["$%s$" % (key), self.notes [key], "$end$\n"]))

#        f.close ()

#        if os.path.exists (oldfn):
#            os.unlink (oldfn)
#        os.rename (newfn, oldfn)

#    def readBookmarks (self):
#        self.pyBookmarks  = []
#        self.cppBookmarks = []

#        fn = os.path.join (self.config ["wabbit"], "bookmarks.txt")
#        if not os.path.exists (fn):
#            return False

#        f = open (fn, "r")
#        collect = False
#        for line in f:
#            if line.startswith ("py"):
#                self.pyBookmarks.append (line.strip () [3:])
#            elif line.startswith ("cpp"):
#                self.cppBookmarks.append (line.strip () [4:])

#        f.close ()
#        return True

#    def writeBookmarks (self):
#        oldfn = os.path.join (self.config ["wabbit"], "bookmarks.txt")
#        newfn = oldfn + ".new"

#        f = open (newfn, "w")

#        for bookmark in self.pyBookmarks:
#            f.write ("py:" + bookmark + "\n")

#        for bookmark in self.cppBookmarks:
#            f.write ("cpp:" + bookmark + "\n")

#        f.close ()

#        if os.path.exists (oldfn):
#            os.unlink (oldfn)
#        os.rename (newfn, oldfn)

