##
# File:  dbApi.py
# Date:  14-Feb-2015
#
# #

import sys
from wwpdb.utils.wf.dbapi.WfDbApi import WfDbApi

'''

Grab bag of methods to execute SQL commands on various WF status tables -

'''


class dbAPI(object):

    def __init__(self, depID, connection=None, verbose=False):

        if connection:
            self.con = connection
        else:
            self.con = WfDbApi(verbose=False)
        self.depID = depID
        self.verbose = verbose

    def close(self):

        self.con.close()

    def runSelectNQ(self, table=None, join=None, select=None, where=None, order=None, reverse=False, ordinal=0, run=True, limit=0):

        if where:
            for k, v in where.iteritems():
                where[k] = "'" + v + "'"
        return self.runSelect(table=table, join=join, select=select, where=where, order=order, reverse=reverse, ordinal=ordinal, run=run, limit=limit)

    def runSelect(self, table=None, join=None, select=None, where=None, order=None, reverse=False, ordinal=0, run=True, limit=0):
        '''
          Very simple select SQL creator - single table
          table = string
          values = list
          where = dictionary of equivalence
          order = list

          Joins are specified with multiple table string , and where clause with inner join defined

          IMPT : all attribute values MUST BE QUOTED if strings

          Returns a list of list  (rows of data columns)
          Returns an empty list if nothing returned
        '''

        if not table:
            print "WFE.dbAPI.runSelect : Undefined table"
            return []

        if not select:
            print "WFE.dbAPI.runSelect : Undefined select"
            return []

        depDB = {}
        depDB["DEP_SET_ID"] = self.depID

        try:
            if self.con.exist(depDB):
                #       if True:
                sql = "select " + ','.join(select) + ' from ' + str(table) + ' '
                if where:
                    sql += ' where ' + ' and '.join(['%s = %s' % (k, v) for k, v in where.iteritems()])
                if join:
                    sql += ' and ' + join
                if ordinal > 0:
                    sql += ' and ordinal >= ' + str(ordinal)
                if order:
                    sql += ' order by ' + ','.join(order)
                    if reverse:
                        sql += ' desc '
                if limit > 0:
                    sql += ' limit ' + str(limit)
                if self.verbose:
                    print "WFE.dbAPI.runSelect > " + str(sql)
                if run:
                    ret = self.con.runSelectSQL(sql)
                    return ret
                else:
                    return sql
            else:
                return []
        except Exception as e:
            print "WFE.dbAPI.runSelect :Exception " + str(e)
            return []

    def runUpdateOnOrdinal(self, table=None, ordinal=None, data=None, run=True):
        '''
          Very simple update/insert SQL creator base on ordinal
          table = string
          data = values to insert/update
          where clause is hard wired to ordinal (unique) so can only UPDATE
              a bad ordinal returns 0 rows udpated

          return number of rows updated
          if run = False : returns the SQL

          IMPT : all attribute values MUST BE QUOTED if strings
        '''

        if not table:
            print "WFE.dbAPI.runUpdateOnOrdinal : Undefined table"
            return False

        if not ordinal:
            print "WFE.dbAPI.runUpdateOnOrdinal : Undefined ordinal"
            return False

        try:
            sql = "update " + str(table) + " set " + ','.join(['%s = %s' % (k, v) for k, v in data.iteritems()])
            sql = sql + " where ordinal = " + str(ordinal)
            if self.verbose:
                print "WFE.dbAPI.runInsertUpdate(update) > " + str(sql)

            if run:
                ok = self.con.runUpdateSQL(sql)
                if not ok:
                    print "WFE.dbAPI.runSelect :False to update/insert data " + str(sql)
                return ok
            else:
                return sql
        except Exception as e:
            print "WFE.dbAPI.runSelect :Exception " + str(e)
            return False

    def runInsertUpdateNQ(self, table=None, depID=None, where=None, data=None, run=True):
        '''
          wrapper to add quotes to values
        '''

        if where:
            for k, v in where.iteritems():
                where[k] = "'" + v + "'"
        if data:
            for k, v in data.iteritems():
                data[k] = "'" + v + "'"
        return self.runInsertUpdate(table, depID, where, data, run)

    def runInsertNQ(self, table=None, depID=None, where=None, data=None, run=True):

        if where:
            for k, v in where.iteritems():
                where[k] = "'" + v + "'"
        if data:
            for k, v in data.iteritems():
                data[k] = "'" + v + "'"
        return self.runInsert(table, depID, where, data, run)

    def runInsert(self, table=None, depID=None, where=None, data=None, run=True):

        try:
            if depID:
                sql = "insert into " + str(table) + " (dep_set_id," + ','.join(['%s' % (k) for k, v in data.iteritems()])
                sql += ") values ('" + str(depID) + "'," + ','.join(['%s' % (v) for k, v in data.iteritems()]) + ")"

            if where:
                sql = "insert into " + str(table) + " (" + ','.join(['%s' % (k) for k, v in where.iteritems()])
                if data:
                    sql += "," + ','.join(['%s' % (k) for k, v in data.iteritems()])
                sql += ") values (" + ','.join(['%s' % (v) for k, v in where.iteritems()])
                if data:
                    sql += "," + ','.join(['%s' % (v) for k, v in data.iteritems()])
                sql += ")"

            if self.verbose:
                print "WFE.dbAPI.runInsertUpdate(insert) > " + str(sql)

            if run:
                return self.con.runInsertSQL(sql)
            else:
                return sql
        except Exception as e:
            print "WFE.dbAPI.runInsert :Exception " + str(e)
            return False

    def runUpdate(self, table=None, depID=None, where=None, data=None, run=True):

        try:
            sql = "update " + str(table) + " set " + ','.join(['%s = %s' % (k, v) for k, v in data.iteritems()])
            if depID:
                sql += " where dep_set_id = '" + str(depID) + "'"
            if where:
                sql += ' where ' + ' and '.join(['%s = %s' % (k, v) for k, v in where.iteritems()])

            if self.verbose:
                print "WFE.dbAPI.runInsertUpdate(update) > " + str(sql)

            if run:
                return self.con.runUpdateSQL(sql)
            else:
                return sql

        except Exception as e:
            print "WFE.dbAPI.runUpdate :Exception " + str(e)
            return False

    def runInsertUpdate(self, table=None, depID=None, where=None, data=None, run=True):
        '''
          Very simple update/insert SQL creator
          table = string
          data = values to insert/update
          where clause is hard wired to dep_set_id so can ONLy be used for
              for tables with unique rows on dep_set_id
              Ie if the table contains a row with depsetid - then it is update
                    if not - the data is inserted

          return number of rows updated
          if run = False : returns the SQL

          if depID is not none - then we test the existance based on depID and the table MUST BE UNIQUE on depID
            data is added to the row based on the dep_set_id + data
          if unique is not none - then we test the existance on the synthetic key based on the unique dictionary
            data is added to the row based on the where + data


          IMPT : all attribute values MUST BE QUOTED if strings
        '''

        if not table:
            print "WFE.dbAPI.runUpdateOnOrdinal : Undefined table"
            return False

        rowExists = False
        if depID:
            # then data is unique on depID
            depDB = {}
            depDB["DEP_SET_ID"] = depID
            if self.con.exist(depDB):
                rowExists = True

        else:
            if where:
                sql = "select ordinal from " + str(table) + " where " + ' and '.join(['%s = %s' % (k, v) for k, v in where.iteritems()])
                rows = self.con.runSelectSQL(sql)
                if rows and len(rows) > 0:
                    rowExists = True
            else:
                print "WFE.dbAPI.runUpdate: Undefined key "

        ok = True
        try:
            if rowExists:
                ok = self.runUpdate(table, depID, where, data, run)

            else:
                ok = self.runInsert(table, depID, where, data, run)

            if not run:
                return ok

            if not ok:
                print "WFE.dbAPI.runSelect :False to update/insert data "
                return ok
            else:
                return ok
        except Exception as e:
            print "WFE.dbAPI.runSelect :Exception " + str(e)
            return False


def main(argv):

    print "starting DBAPI test"

    depid = 'D_1100201819'
    ss = dbAPI(depid, verbose=True)

# table join test - notice that in the where clause be careful with the key must not repeat - so be carful with the inner join
    ret = ss.runSelect(
        table='deposition,wf_instance',
        select=[
            'deposition.dep_set_id',
            'wf_instance.wf_inst_id'],
        where={
            "deposition.dep_set_id": "wf_instance.dep_set_id",
            "wf_instance.dep_set_id": "'" +
            depid +
            "'",
            "wf_instance.wf_inst_id": "'W_001'"},
        run=True)
    print str(ret)

    ret = ss.runSelectNQ(table='deposition', select=['ordinal', 'dep_set_id', 'depPW', 'annotator_initials'], where={"dep_set_id": depid})
    print str(ret)


# test the orinal updates
    ret = ss.runSelectNQ(table='deposition', select=['ordinal', 'dep_set_id', 'depPW'], where={"dep_set_id": depid})
    print str(ret)
#   ret = ss.runUpdateOnOrdinal(table='deposition',ordinal='17823',data={"depPW":"'abcdef'"},run=True)
#   print str(ret)
    ret = ss.runSelectNQ(table='deposition', select=['ordinal', 'dep_set_id', 'depPW'], where={"dep_set_id": depid})
    print str(ret)
#   ret = ss.runUpdateOnOrdinal(table='deposition',ordinal='17823',data={"depPW":"'123456'"},run=True)
    print str(ret)
    ret = ss.runSelectNQ(table='deposition', select=['ordinal', 'dep_set_id', 'depPW'], where={"dep_set_id": depid})
    print str(ret)

    ret = ss.runSelectNQ(
        table='deposition,user_data',
        select=[
            'last_name',
            'role',
            'user_data.country'],
        join='deposition.dep_set_id=user_data.dep_set_id',
        where={
            "deposition.dep_set_id": depid})

# test the dep_set_id updates
#   ret = ss.runInsertUpdate(table='deposition',depID='D_1000200025',data={"depPW":"'abcdef'"},run=True)
    print str(ret)
    ret = ss.runSelect(table='deposition', select=['dep_set_id', 'depPW'], where={"dep_set_id": "'" + depid + "'"})
    print str(ret)
#   ret = ss.runInsertUpdate(table='deposition',depID='D_1000200025',data={"depPW":"'123456'"},run=True)
    print str(ret)
    ret = ss.runSelect(table='deposition', select=['dep_set_id', 'depPW'], where={"dep_set_id": "'" + depid + "'"})
    print str(ret)

    print "finished"

if __name__ == "__main__":

    main(sys.argv[1:])
