import re
import sys, types
from PostgresContainer import PostgresContainer, FTSS_CONTAIMENT_TABLE

import Escapec

from Ft.Rdf import Model

SYSTEM_MODEL_NAME = 'system'
USER_MODEL_NAME = 'user'

def InitializeModule():
    """
    Post-import hook to initialize module's runtime variables that are not
    required at import time, but will be needed before the module-level
    functions are called.
    """
    global _pg
    global PostgresRdf

    # Load the required database interface module
    import _pg

    # Verify the required PyGreSQL version
    from distutils.version import LooseVersion
    if LooseVersion(_pg.__version__) < LooseVersion('3.2'):
        raise ValueError("You need _pg module version 3.2 or later to use"
                         " the Server Postgres Driver")

    # Incorporate the RDF driver module
    from Ft.Rdf.Drivers import Postgres as PostgresRdf
    PostgresRdf.InitializeModule()
    return


def Exists(properties):
    """See if this repo exists.  The return value for this is three state.
    1.  Everything is present
    0.  Some things are there
    -1  Nothing is there.
    """
    db = _pg.connect(dbname='template1',
                     host=properties['Host'],
                     port=int(properties['Port']),
                     user=properties['User'],
                     passwd=properties['Passwd'],
                     )
    pg_db_name = 'ft__' + properties['DbName']
    rt = db.query("SELECT datname FROM pg_database WHERE datname='%s'" % pg_db_name)
    if rt and rt.ntuples() > 0:
        rdfConn = BuildRdfConnString(properties)
        if not PostgresRdf.ExistsDb(rdfConn,SYSTEM_MODEL_NAME):return -1
        if not PostgresRdf.ExistsDb(rdfConn,USER_MODEL_NAME):return 0
        pg_db_name = 'ft__' + properties['DbName']
        pg_db = _pg.connect(dbname = pg_db_name,
                            host=properties['Host'],
                            port=int(properties['Port']),
                            user=properties['User'],
                            passwd=properties['Passwd'],
                            )

        for tn in ['ftss_strings',
                   FTSS_CONTAIMENT_TABLE]:
            if not pg_db.query("select tablename from pg_tables where tablename = '%s'" % tn).getresult():
                return 0

        if pg_db.query("select tablename from pg_tables where tablename = 'ftss_stringdata'").getresult():
            raise SystemExit("You have an old version of the database, you either need to back out your code update or completely destroy the Postgres data base dropdb")

        if not pg_db.query("select indexname from pg_indexes where indexname = 'ftss_strings_idx'").getresult():
            return 0

        return 1

    return -1


def Initialize(properties):
    """Initialize a new copy of the repo.  This is not the same as a 4ss_manager init.  This
    is very raw.  The adapter will take care of calling our other interfaces
    (createContainer, createUser, etc) with all of the information
    about the rest of the 'init' stuff to do
    """
    rdfConn = BuildRdfConnString(properties)
    PostgresRdf.CreateDb(rdfConn,SYSTEM_MODEL_NAME)
    PostgresRdf.CreateDb(rdfConn,USER_MODEL_NAME)

    pg_db_name = 'ft__' + properties['DbName']
    pg_db = _pg.connect(dbname = pg_db_name,
                        host=properties['Host'],
                        port=int(properties['Port']),
                        user=properties['User'],
                        passwd = properties['Passwd'],
                        )

    pg_db.query('CREATE TABLE ftss_strings (path text, type int4, content bytea)')
    pg_db.query('CREATE UNIQUE INDEX ftss_strings_idx ON ftss_strings (path,type)')
    containerDrv = PostgresContainer(pg_db)
    containerDrv.initialize()


def Destroy(properties,tryOnly=0):
    """Completly destroy the repo"""

    rdfConn = BuildRdfConnString(properties)
    PostgresRdf.DestroyDb(rdfConn,SYSTEM_MODEL_NAME)
    PostgresRdf.DestroyDb(rdfConn,USER_MODEL_NAME)

    pg_db_name = 'ft__' + properties['DbName']
    pg_db = _pg.connect(dbname = pg_db_name,
                        host=properties['Host'],
                        port=int(properties['Port']),
                        user=properties['User'],
                        passwd=properties['Passwd'],
                        )

    for tn in ['ftss_strings',
               FTSS_CONTAIMENT_TABLE]:
        try:
            pg_db.query('DROP TABLE %s' % tn)
        except _pg.error:
            sys.stderr.write("Unable to drop table %s\n" % tn);


def Maintain(properties):
    """
    Perform any maintenance on the db
    """
    ###We cannot do this in a transaction!!!

    pg_db_name = 'ft__' + properties['DbName']
    pg_db = _pg.connect(dbname = pg_db_name,
                        host=properties['Host'],
                        port = properties['Port'],
                        user = properties['User'],
                        passwd = properties['Passwd'],
                        )

    pg_db.query("VACUUM")
    pg_db.close()


def BuildRdfConnString(properties):
    #rdf:userName/password@hostName:port:dbName
    st = "rdf:"
    if properties['User']:
        if properties['Passwd']:
            st += properties['User'] + '/' + properties['Passwd'] + '@'
        else:
            st += properties['User'] + '@'
    if properties['Host']:
        st += properties['Host'] + ':'
    if properties['Port'] != -1:
        st += str(properties['Port']) + ':'
    return st + properties['DbName']


class PostgresDriver(PostgresContainer):
    """Store information in a Postgres Database"""

    def __init__(self,
                 dbName,
                 host,
                 port,
                 user,
                 passwd):

        self._dbName = dbName
        self._host = host
        self._port = port
        self._user = user
        self._passwd = passwd
        pg_db_name = 'ft__' + dbName
        self._db = _pg.connect(dbname = pg_db_name,
                               host=host,
                               port = port,
                               user = user,
                               passwd = passwd,
                               )

        self._db.query('BEGIN')
        d = PostgresRdf.DbAdapter('',SYSTEM_MODEL_NAME)
        d._db = self._db
        self._systemRdfModel = Model.Model(d)
        d = PostgresRdf.DbAdapter('',USER_MODEL_NAME)
        d._db = self._db
        self._userRdfModel = Model.Model(d)

    def createFile(self,path,typ,content):
        """When creating a resource,
        store the content"""
        #Verify that we were given a string object
        if not isinstance(content, types.StringType):
            type_name = type(content).__name__
            raise TypeError("Content must be a string, not %r" % type_name)
        content = Escapec.escape(content)
        self._db.query("""INSERT INTO ftss_strings (path,type,content) VALUES ('%s', %d,'%s')""" % (str(path), typ, content))
        return

    def hasFile(self,path,typ):
        """See if we have any meta information about this resource"""
        return not not self._db.query("SELECT type FROM ftss_strings WHERE path = '%s' and type=%d" % (str(path),typ)).getresult()

    def fetchFile(self, path, typ):
        data = None
        try:
            data = self._db.query("SELECT content FROM ftss_strings WHERE path = '%s' and type=%d" % (str(path),typ)).getresult()
        except AttributeError:
            return None
        if not data:
            return None
        return Escapec.unescape(data[0][0])

    def updateFile(self, path, typ, content):
        """Update only the content about this resource"""
        #Verify that we were given a string object
        if not isinstance(content, types.StringType):
            type_name = type(content).__name__
            raise TypeError("Content must be a string, not %r" % type_name)
        content = Escapec.escape(content)
        self._db.query("""UPDATE ftss_strings SET content = '%s' WHERE path='%s' and type=%d""" % (content,str(path), typ))
        return

    def deleteFile(self, path, typ):
        """Delete an object"""
        self._db.query("DELETE FROM ftss_strings where path = '%s' and type = %d" % (str(path),typ))

    def getSystemModel(self):
        return self._systemRdfModel

    def getUserModel(self):
        return self._userRdfModel

    def commit(self):
        """Make it so!"""
        self._db.query('COMMIT')
        self._db.close()
        self._db = None
        self._systemRdfModel._db = None
        self._systemRdfModel = None
        self._userRdfModel._db = None
        self._userRdfModel = None

    def rollback(self):
        """Undo it"""
        self._db.close()
        self._db = None
        self._systemRdfModel._db = None
        self._systemRdfModel = None
        self._userRdfModel._db = None
        self._userRdfModel = None


def Begin(**properties):
    """Begin a new transaction.  Every driver must support this interface.
    The properties keyword arguments are passed from the config file
    (or where ever) to the driver.  The Begin file is responsible
    for doing what ever is needed to validate these arguements
    """
    return PostgresDriver(properties['DbName'],
                          properties['Host'],
                          properties['Port'],
                          properties['User'],
                          properties['Passwd'])

NAME='Postgres'
