'''
Created on Jan 22, 2015

@author: dthaluru
'''
import re
import os

class rpmMacro(object):
    def __init__(self):
        self.macroName=""
        self.macroFlag=""
        self.content=""
        self.position=-1
        self.endposition=-1

    def setName(self,name):
        self.macroName=name
        
    def displayMacro(self):
        print "Macro:\n", self.macroName, " ",self.macroFlag," ",self.position," ",self.endposition
        print self.content
    
class StringUtils(object):
    
    def get_string_in_brackets(self, inputstring):
        inputstring=inputstring.strip()
        m = re.search(r"^\(([A-Za-z0-9_.-]+)\)",  inputstring)
        if m is None:
            return inputstring
        return m.group(1)

    def getFileNameFromURL(self,inputstring):
        index=inputstring.rfind("/")
        return inputstring[index+1:]
        
    
class dependentPackageData(object):

    def __init__(self):
        self.package=""
        self.version=""
        self.compare=""
        
class Package(object):
    def __init__(self, basePkg=None):
        self.summary=""
        self.name=""
        self.group=""
        self.license=""
        self.version=""
        self.release=""
        self.buildarch="x86_64"
        self.distribution=""
        self.basePkgName=""
        
        self.sources=[]
        self.patches=[]
        self.buildrequires=[]
        self.buildprovides=[]
        
        
        self.requires=[]
        self.provides=[]
        self.obsoletes=[]
        self.conflicts=[]
        
        self.descriptionMacro= None #rpmMacro().setName("description")
        self.postMacro=None#rpmMacro().setName("post")
        self.postunMacro=None#rpmMacro().setName("postun")
        self.filesMacro=None#rpmMacro().setName("files")
        self.packageMacro=None#rpmMacro().setName("package")
        
        if basePkg is not None:
            self.basePkgName=basePkg.name
            self.group=basePkg.group
            self.license=basePkg.license
            self.version=basePkg.version
            self.buildarch=basePkg.buildarch
            self.release=basePkg.release
            self.distribution=basePkg.distribution
        
    def decodeContents(self,content):
        if content.find("%{name}") != -1:
            if self.basePkgName == "":
                content = content.replace('%{name}',self.name)
            else:
                content = content.replace('%{name}',self.basePkgName)
        
        if content.find("%{release}") != -1:
            content = content.replace('%{release}',self.release)
        
        if content.find("%{version}") != -1:
            content = content.replace('%{version}',self.version)

        if content.find("%{?dist}") != -1:
            content = content.replace('%{?dist}',self.distribution)

        if content.find("%{dist}") != -1:
            content = content.replace('%{dist}',self.distribution)
        
        return content
    
    def updatePackageMacro(self,macro):
        if macro.macroName == "%post":
            self.postMacro=macro
            return True
        if macro.macroName == "%postun":
            self.postunMacro=macro
            return True
        if macro.macroName == "%files":
            self.filesMacro=macro
            return True
        if macro.macroName == "%description":
            self.descriptionMacro = macro
            return True
        return False

class SpecFile(object):
    def __init__(self):
        self.cleanMacro=rpmMacro().setName("clean")
        self.prepMacro=rpmMacro().setName("prep")
        self.buildMacro=rpmMacro().setName("build")
        self.installMacro=rpmMacro().setName("install")
        self.changelogMacro=rpmMacro().setName("changelog")
        self.checkMacro=rpmMacro().setName("check")
        self.packages={}
        self.specAdditionalContent=""
        
    
    def readPkgNameFromPackageMacro(self,data,basePkgName=None):
        data=" ".join(data.split())
        pkgHeaderName=data.split(" ")
        lenpkgHeaderName = len(pkgHeaderName)
        
        if (lenpkgHeaderName >= 3 and pkgHeaderName[1] != "-n"):
            lenpkgHeaderName = 1
        if (lenpkgHeaderName == 2 or lenpkgHeaderName == 1 ) and basePkgName is None :
            print "Invalid basePkgName"
            return False,None
        if lenpkgHeaderName == 3 :
            return True,pkgHeaderName[2]
        if lenpkgHeaderName == 2 :
            return True,basePkgName + "-"+pkgHeaderName[1]
        if lenpkgHeaderName == 1:
            return True, basePkgName
    
    def parseSpecFile(self,specfile):
        
        self.createDefaultPackage()
        currentPkg="default"
        specFile = open(specfile)
        lines = specFile.readlines()
        totalLines=len(lines)
        i=0
        while i < totalLines:
            line = lines[i].strip()
            if self.isSpecMacro(line):
                macro,i=self.readMacroFromFile(i, lines)
                self.updateMacro(macro)
            elif self.isPackageMacro(line):
                defaultpkg = self.packages.get('default')
                returnVal,packageName=self.readPkgNameFromPackageMacro(line, defaultpkg.name)
                packageName=defaultpkg.decodeContents(packageName)
                if not returnVal:
                    return False
                if re.search('^'+'%package',line) :
                    pkg = Package(defaultpkg)
                    pkg.name=packageName
                    currentPkg=packageName
                    self.packages[pkg.name]=pkg
                else:
                    if defaultpkg.name == packageName :
                        packageName = 'default'
                    if not self.packages.has_key(packageName):
                        return False
                    macro,i=self.readMacroFromFile(i, lines)
                    self.packages[packageName].updatePackageMacro(macro)
            elif self.isPackageHeaders(line):
                self.readPackageHeaders(line, self.packages[currentPkg])
            else:
                self.specAdditionalContent+=line+"\n"
            i=i+1
        specFile.close()
    
    def createDefaultPackage(self):
        pkg = Package()
        self.packages["default"]=pkg
    
    def readMacroFromFile(self,currentPos,lines):
        macro = rpmMacro()
        line = lines[currentPos]
        macro.position = currentPos
        macro.endposition=currentPos
        endPos=len(lines)
        line = " ".join(line.split())
        flagindex = line.find(" ")
        if flagindex != -1:
            macro.macroFlag=line[flagindex+1:]
            macro.macroName=line[:flagindex]
        else:
            macro.macroName=line

        if currentPos+1 < len(lines) and self.isMacro(lines[currentPos+1]):
            return macro,currentPos
            
        for j in range(currentPos+1,endPos):
            content = lines[j]
            if j+1 < endPos and self.isMacro(lines[j+1]):
                return macro,j
            macro.content += content +'\n'
            macro.endposition=j
        return macro,endPos
        

    def updateMacro(self,macro):
        if macro.macroName == "%clean":
            self.cleanMacro=macro
            return True
        if macro.macroName == "%prep":
            self.prepMacro=macro
            return True
        if macro.macroName == "%build":
            self.buildMacro=macro
            return True
        if macro.macroName == "%install":
            self.installMacro=macro
            return True
        if macro.macroName == "%changelog":
            self.changelogMacro=macro
            return True
        if macro.macroName == "%check":
            self.checkMacro=macro
            return True
        return False
            
    def isMacro(self,line):
        return self.isPackageMacro(line) or self.isSpecMacro(line)
    
    def isSpecMacro(self,line):
        if re.search('^'+'%clean',line) :
            return True
        elif re.search('^'+'%prep',line) :
            return True            
        elif re.search('^'+'%build',line) :
            return True
        elif re.search('^'+'%install',line) :
            return True
        elif re.search('^'+'%changelog',line) :
            return True
        elif re.search('^'+'%check',line) :
            return True
        return False
    
    def isPackageMacro(self,line):
        line=line.strip()

        if re.search('^'+'%post',line) :
            return True
        elif re.search('^'+'%postun',line) :
            return True
        elif re.search('^'+'%files',line) :
            return True
        elif re.search('^'+'%description',line) :
            return True
        elif re.search('^'+'%package',line) :
            return True
        return False
    
    def isPackageHeaders(self,line):
        if re.search('^'+'summary:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'name:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'group:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'license:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'version:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'release:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'distribution:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'requires:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'provides:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'obsoletes:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'conflicts:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'source[0-9]*:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'patch[0-9]*:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'buildrequires:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'buildprovides:',line,flags=re.IGNORECASE) :
            return True
        elif re.search('^'+'buildarch:',line,flags=re.IGNORECASE) :
            return True
        return False

    def readHeader(self,line):
        headerSplitIndex=line.find(":")
        if(headerSplitIndex+1 == len(line) ):
            print line
            print "Error:Invalid header"
            return False, None,None
        headerName=line[0:headerSplitIndex].lower()
        headerContent=line[headerSplitIndex+1:].strip()
        return True,headerName,headerContent


    def readDependentPackageData(self,line):
        strUtils = StringUtils()
        listPackages=line.split(",")
        listdependentpkgs=[]
        for line in listPackages:
            line=strUtils.get_string_in_brackets(line)
            listContents=line.split()
            totalContents = len(listContents)
            i=0
            while i < totalContents:
                dpkg = dependentPackageData()
                compare=None
                if i+2 < len(listContents):
                    if listContents[i+1] == ">=":
                        compare="gte"
                    elif listContents[i+1] == "<=":
                        compare="lte"
                    elif listContents[i+1] == "==":
                        compare="eq"
                    elif listContents[i+1] == "<":
                        compare="lt"
                    elif listContents[i+1] == ">":
                        compare="gt"
                    elif listContents[i+1] == "=":
                        compare="eq"
                    
                if compare is not None:
                    dpkg.package=listContents[i]
                    dpkg.compare=compare
                    dpkg.version=listContents[i+2]
                    i=i+3
                else:
                    dpkg.package=listContents[i]
                    i=i+1
                listdependentpkgs.append(dpkg)
        return listdependentpkgs

    def readPackageHeaders(self,line,pkg):
        
        returnVal,headerName,headerContent=self.readHeader(line)
        if not returnVal:
            return False

        headerContent=pkg.decodeContents(headerContent)
        if headerName == 'summary':
            pkg.summary=headerContent
            return True
        if headerName == 'name':
            pkg.name=headerContent
            return True
        if headerName == 'group':
            pkg.group=headerContent
            return True
        if headerName == 'license':
            pkg.license=headerContent
            return True
        if headerName == 'version':
            pkg.version=headerContent
            return True
        if headerName == 'buildarch':
            pkg.buildarch=headerContent
            return True
        if headerName == 'release':
            pkg.release=headerContent
            return True
        if headerName == 'distribution':
            pkg.distribution=headerContent
            return True
        if headerName.find('source') != -1:
            pkg.sources.append(headerContent)
            return True
        if headerName.find('patch') != -1:
            pkg.patches.append(headerContent)
            return True
        if headerName == 'requires' or headerName == 'provides' or headerName == 'obsoletes' or headerName == 'conflicts' or headerName == 'buildrequires' or headerName == 'buildprovides':
            dpkg=self.readDependentPackageData(headerContent)
            if dpkg is None:
                return False
            if headerName == 'requires':
                pkg.requires.extend(dpkg)
            if headerName == 'provides':
                pkg.provides.extend(dpkg)
            if headerName == 'obsoletes':
                pkg.obsoletes.extend(dpkg)
            if headerName == 'conflicts':
                pkg.conflicts.extend(dpkg)
            if headerName == 'buildrequires':
                pkg.buildrequires.extend(dpkg)
            if headerName == 'buildprovides':
                pkg.buildprovides.extend(dpkg)
                    
            return True
        return False

class Specutils(object):
    
    def __init__(self,specfile):
        self.specfile=""
        self.spec = SpecFile()
        if self.isSpecFile(specfile):
            self.specfile=specfile
            self.spec.parseSpecFile(self.specfile)
    
    def isSpecFile(self,specfile):
        if os.path.isfile(specfile) and specfile[-5:] == ".spec":
            return True
        return False
    
    def getSourceNames(self):
        sourceNames=[]
        strUtils = StringUtils()
        pkg = self.spec.packages.get('default')
        if pkg is None:
            return None
        for source in pkg.sources:
            sourceName=strUtils.getFileNameFromURL(source)
            sourceNames.append(sourceName)
        return sourceNames
    
    def getPatchNames(self):
        patchNames=[]
        strUtils = StringUtils()
        pkg = self.spec.packages.get('default')
        if pkg is None:
            return None
        for patch in pkg.patches:
            patchName=strUtils.getFileNameFromURL(patch)
            patchNames.append(patchName)
        return patchNames
    
    def getPackageNames(self):
        packageNames=[]
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            packageNames.append(pkg.name)
        return packageNames
    
    def getRPMNames(self):
        rpmNames=[]
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            rpmName=pkg.name+"-"+pkg.version+"-"+pkg.release
            rpmNames.append(rpmName)
        return rpmNames

    def getRPMName(self, pkgName):
        rpmName=None
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            if pkg.name == pkgName:
               rpmName=pkg.name+"-"+pkg.version+"-"+pkg.release
               break
        return rpmName

    def getRPMVersion(self, pkgName):
        version=None
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            if pkg.name == pkgName:
               version=pkg.version
               break
        return version

    def getRPMRelease(self, pkgName):
        release=None
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            if pkg.name == pkgName:
               release=pkg.release
               break
        return release

    def getLicense(self, pkgName):
        license=None
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            if pkg.name == pkgName:
               license=pkg.license
               break
        return license

    def getBuildArch(self, pkgName):
        buildArch="x86_64"
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            if pkg.name == pkgName:
               buildArch=pkg.buildarch
               break
        return buildArch
    
    def getRequiresAllPackages(self):
        depedentPackages=[]
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            for dpkg in pkg.requires:
                depedentPackages.append(dpkg.package)
        depedentPackages=list(set(depedentPackages))
        packageNames=self.getPackageNames()
        for pkgName in packageNames:
            if pkgName in depedentPackages:
                depedentPackages.remove(pkgName)
        return depedentPackages
    
    def getBuildRequiresAllPackages(self):
        depedentPackages=[]
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            for dpkg in pkg.buildrequires:
                depedentPackages.append(dpkg.package)
        depedentPackages=list(set(depedentPackages))
        packageNames=self.getPackageNames()
        for pkgName in packageNames:
            if pkgName in depedentPackages:
                depedentPackages.remove(pkgName)
        return depedentPackages
    
    
    def getRequires(self,pkgName):
        dependentPackages=[]
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            if pkg.name == pkgName:
               for dpkg in pkg.requires:
                  dependentPackages.append(dpkg.package)
        return dependentPackages

    def getBuildRequires(self,pkgName):
        dependentPackages=[]
        for key in self.spec.packages.keys():
            pkg = self.spec.packages.get(key)
            if pkg.name == pkgName:
               for dpkg in pkg.buildrequires:
                  dependentPackages.append(dpkg.package)
        return dependentPackages
        
    def getProvides(self,packageName):
        depedentPackages=[]
        defaultPkgName=self.spec.packages['default'].name
        pkg = None
        if self.spec.packages.has_key(packageName):
            pkg = self.spec.packages.get(packageName)
        if defaultPkgName == packageName:
            pkg=self.spec.packages['default']
        if pkg is not None:
            for dpkg in pkg.provides:
                depedentPackages.append(dpkg.package)
        else:
            print "package not found"
        return depedentPackages
    
    def getVersion(self):
        pkg = self.spec.packages.get('default')
        return pkg.version
    
    def getRelease(self):
        pkg = self.spec.packages.get('default')
        return pkg.release
        
def main():
    spec = Specutils("/workspace1/myrepos/photon/SPECS/docker/docker.spec")
    print "packages",spec.getPackageNames()
    print "packages",spec.getRPMNames()
    print "sources",spec.getSourceNames()
    print "patches",spec.getPatchNames()  
    print "requires",spec.getRequires('libltdl-devel')
    print "requires",spec.getRequires('libtool')
    
    print "provides",spec.getProvides('libtool')  
    print "all-requires",spec.getRequiresAllPackages()
    print "all-build-requires",spec.getBuildRequiresAllPackages()
    
if __name__ == '__main__':
    main()