#!/usr/bin/env python
##----------------------------------------------------------------------
## Build contrib/lib
## Called by post-update when necessary
##----------------------------------------------------------------------
## Copyright (C) 2007-2010 The NOC Project
## See LICENSE for details
##----------------------------------------------------------------------

from __future__ import with_statement
import os,sys,csv,subprocess,getopt,py_compile

# Static files to install
STATIC_FILES = []
# Static files to install only when not installed in system
SYSTEM_STATIC_FILES = ["pkg_resources.py"]

d=os.path.dirname(sys.argv[0])
if d:
    os.chdir(d)

##
## Main installer class
##
class Installer(object):
    CONTRIB=os.path.abspath(os.path.join("..","contrib"))
    SRC=os.path.join(CONTRIB,"src")
    LIB=os.path.join(CONTRIB,"lib")
    BIN=os.path.join(CONTRIB,"bin")
    SHARE=os.path.join(CONTRIB,"share")
    SRC_VERSIONS=os.path.join(SRC,"VERSION.csv")
    DST_VERSIONS=os.path.join(LIB,"VERSION.csv")
    ENV=os.environ.copy()
    ENV["PYTHONPATH"]= ":".join([LIB]+sys.path)
    ##
    def __init__(self,check_only=False):
        self.check_only=check_only
    
    def is_differ(self, path1, path2):
        if not os.path.isfile(path1) or not os.path.isfile(path2):
            return True
        with open(path1) as f1:
            with open(path2) as f2:
                return f1.read() == f2.read()
        return False
    
    def copy_when_differ(self, src, dst):
        with open(src) as s:
            ds = s.read()
        if os.path.isfile(dst):
            with open(dst) as d:
                dd = d.read()
        else:
            dd = None
        if ds == dd:
            return False
        # Check target directory is exist
        d, f = os.path.split(dst)
        if not os.path.exists(d):
            os.makedirs(d)
        # Write file
        with open(dst, "w") as d:
            d.write(ds)
        return True
        
    ##
    ## Read CSV file and returns hash {row[0]->row[1]}
    ##
    def read_csv(self,path):
        r={}
        if not os.path.exists(path):
            return r
        with open(path) as f:
            for row in csv.reader(f):
                if row:
                    r[row[0]]=row[1]
        return r
    ##
    ## Syncronize contrib apps
    ##
    def sync(self):
        if not self.check_only:
            print "Syncronizing contrib apps"
        sv=[]
        self.read_csv(self.SRC_VERSIONS)
        with open(self.SRC_VERSIONS) as f:
            for row in csv.reader(f):
                if row:
                    sv+=[row]
        iv=self.read_csv(self.DST_VERSIONS)
        update_iv=False
        fail=False
        if self.check_only:
            if (not self.check_static_files() or
                not self.check_system_static_files()):
                return False
        else:
            fail = (fail or
                    not self.install_static_files() or
                    not self.install_system_static_files())
        for app, version in sv:
            if app not in iv or iv[app] != version:
                if self.check_only: # Do nothing on version mismatch in check mode
                    return False
                print "Upgrading %s to version %s"%(app,version)
                if hasattr(self,"install_%s"%app):
                    r=getattr(self,"install_%s"%app)()
                else:
                    r=self.install(app)
                if r:
                    update_iv=True
                    iv[app]=version
                    print "%s: upgraded to version %s"%(app,version)
                else:
                    print "%s: failed to upgrade to version %s"%(app,version)
                    fail=True
                    break
            elif not self.check_only:
                print "%s is in sync (version %s)"%(app,version)
        if self.check_only: # Return immediately in check mode
            return True
        # Write updated versions
        if update_iv:
            with open(self.DST_VERSIONS,"w") as f:
                writer=csv.writer(f)
                writer.writerows(iv.items())
        return not fail
    ##
    ## Default installation
    ##
    def install(self,app):
        return subprocess.call(["python","setup.py","install",
            #"--prefix=%s"%self.CONTRIB,
            "--install-purelib=%s"%self.LIB,
            "--install-platlib=%s"%self.LIB,
            "--install-scripts=%s"%self.BIN,
            "--install-data=%s"%self.LIB
        ],
        cwd=os.path.join(self.SRC,app),
        env=self.ENV)==0
    
    ##
    ## Check static files in sync
    ##
    def check_static_files(self):
        for f in STATIC_FILES:
            src = os.path.join(self.SRC, f)
            dst = os.path.join(self.LIB, f)
            if self.is_differ(src, dst):
                return False
        return True

    ##
    ## Install static files
    ##
    def install_static_files(self):
        fail = False
        for f in STATIC_FILES:
            src = os.path.join(self.SRC, f)
            dst = os.path.join(self.LIB, f)
            if self.copy_when_differ(src, dst):
                print "Installing %s" % f
                py_compile.compile(dst)
            else:
                print "%s is in sync" % f
        return not fail
    ##
    ## Check system static files in sync
    ##
    def check_system_static_files(self):
        for f in SYSTEM_STATIC_FILES:
            mn = os.path.splitext(f)[0].replace("/", ".")
            try:
                __import__(mn, {}, {}, "*")
                continue
            except ImportError:
                pass
            src = os.path.join(self.SRC, f)
            dst = os.path.join(self.LIB, f)
            if self.is_differ(src, dst):
                return False
        return True

    ##
    ## Install static files
    ##
    def install_system_static_files(self):
        fail = False
        for f in SYSTEM_STATIC_FILES:
            mn = os.path.splitext(f)[0].replace("/", ".")
            try:
                __import__(mn, {}, {}, "*")
                continue
            except ImportError:
                pass
            src = os.path.join(self.SRC, f)
            dst = os.path.join(self.LIB, f)
            if self.copy_when_differ(src, dst):
                print "Installing %s" % f
                py_compile.compile(dst)
            else:
                print "%s is in sync" % f
        return not fail
    
    ##
    ## Install protobuf
    ##
    def install_protobuf(self):
        # Remove contrib/lib/google left from 2.2.0a
        import shutil
        shutil.rmtree(os.path.join(self.LIB,"google"),ignore_errors=True)
        # Install
        return self.install("protobuf")
    ##
    ## Install django
    ##
    def install_django(self):
        # Remove contrib/lib/django
        import shutil
        shutil.rmtree(os.path.join(self.LIB, "django"),ignore_errors=True)
        # Install
        return self.install("django")

##
## Check all required system modules present
##
def check_system():
    r=True
    # Check psycopg2
    try:
        import psycopg2
    except ImportError:
        print "Error: psycopg2 not found. Please install psycopg2 module (easy_install psycopg2)"
        r=False
    # Check gmpy
    try:
        import gmpy
    except ImportError:
        print "Warning: gmpy not found. Please install gmpy module (easy_install gmpy) if it possible"
    # Check pyCrypto
    try:
        from Crypto.PublicKey import RSA, DSA
    except ImportError:
        print "Error: pycrypto not found. Please install pycrypto module (easy_install pycrypto)"
        r=False
    return r

if __name__=="__main__":
    check_only=False
    system_only=False
    optlist,optargs=getopt.getopt(sys.argv[1:],"cs")
    for k,v in optlist:
        if k=="-c":
            check_only=True
        elif k=="-s":
            system_only=True
    if system_only:
        r=check_system()
    else:
        r=Installer(check_only=check_only).sync()
    sys.exit({True:0,False:1}[r])
