#
#
#
import re
import json
import sqlite3

delimeter = "|"


#
# Database Schema
#
class DBSchema:
  """
  This class represents a complete database schema
  """

  sqlmaster = "SELECT name, sql FROM sqlite_master WHERE type='table'"

  def __init__(self):
    self.conn = None
    self.cur = None
    self.tableNames = []
    self.tables = {}
    self.jsonData = None
    return


  def scanDBFile(self, filein):
    # try to open sqlite file
    try:
      (self.conn, self.cur) = self.__openDB(filein)
    except Exception, e:
      print e
      return -2
    
    # read database schema
    try:
      self.__readDatabase()
    except Exception, e:
      print e
      return -3

    return 1


  def importDBSchema(self, filein):
    try:
      fh = open(fileout, "r")
      self.jsonData = json.loads(fh)
    except Exception, e:
      print e


  def __openDB(self, filein):
    conn = sqlite3.connect(filein)
    cur = conn.cursor()
    return (conn, cur)


  def __readDatabase(self):
    for row in self.cur.execute(self.sqlmaster):
      newTable = TableDefinition()
      newTable.loadTable(row[0], row[1])
      self.tableNames.append(newTable.name())
      self.tables[newTable.name()] = newTable
    return


  def debugFingerprint(self):
    keys = self.tables.keys()
    for key in keys:
      print "[[ TABLE: <" + key + "> ]]"
      tableDef = self.tables[key]
      print str(tableDef.SQLstr())
      tableDef.toJSON()


  def writeFingerprint(self, filehandle):
    ahash = {}
    thash = {}
    mhash = {}
    ahash['tables'] = thash
    ahash['metadata'] = mhash

    # metadata
    mhash['app-name']   = 'dbfp'
    mhash['app-ver']    = '0.50'
    mhash['format-ver'] = '0.90'

    # tables
    keys = self.tables.keys()
    for key in keys:
      thash[key] = self.tables[key].fields

    json.dump(ahash, filehandle, sort_keys=True, indent=4)


  def getErrorString(self, errorCode):
    retval = "ERROR: unknown error code: " + str(errorCode)
    if (errorCode == -2):
      retval = "ERROR: problem opening file, or not sqlite database"
    elif (errorCode == -3):
      retval = "ERROR: problem reading database"
    return retval


#
#
#
class TableDefinition:
  """
  This class represents the definition of database table
  """

  tableschemaregex = r'\((.*)\)'


  def __init__(self):
    self.tableName = ""
    self.sqlStr    = ""
    self.fields    = {}
    self.primarykeyFlag = False
    self.uniqueFlag     = False


  def loadTable(self, tableName, sqlStr):
    self.tableName = tableName
    self.sqlStr = sqlStr
    results = re.search(self.tableschemaregex, sqlStr)
    if results:
      colstr = results.group(1)
      print "[[ TABLE: <" + tableName + "> ]]"
      columns = colstr.split(',')
      for col in columns:
        newField = self.__parseCreateStr(col.strip())
        if newField:
          self.fields[newField['name']] = newField

  # Table Definition
  #
  #  CREATE TABLE contacts (_id INTEGER PRIMARY KEY AUTOINCREMENT,name_raw_contact_id INTEGER REFERENCES raw_contacts(_id),
  #      photo_id INTEGER REFERENCES data(_id),photo_file_id INTEGER REFERENCES photo_files(_id),
  #      custom_ringtone TEXT,send_to_voicemail INTEGER NOT NULL DEFAULT 0,
  #      times_contacted INTEGER NOT NULL DEFAULT 0,last_time_contacted INTEGER,
  #      starred INTEGER NOT NULL DEFAULT 0,pinned INTEGER NOT NULL DEFAULT 2147483647,
  #      has_phone_number INTEGER NOT NULL DEFAULT 0,lookup TEXT,
  #      status_update_id INTEGER REFERENCES data(_id),contact_last_updated_timestamp INTEGER)
  #
  #  CREATE TABLE sent_files_v2 (uid INTEGER, phone TEXT, sphone TEXT, deleted INTEGER, 
  #                              PRIMARY KEY (uid, phone)
  def __parseCreateStr(self, sqltext):
    try:
      newField = {}
      #                     photo_id INTEGER REFERENCES data(_id)
      #                     name_raw_contact_id INTEGER REFERENCES raw_contacts(_id)
      results = re.match(r'(?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*\s+(\w+)\s+REFERENCESS\s+(\W+)', sqltext)
      if results:
        newField['name'] = results.group(1)
        newField['datatype'] = results.group(2)
        newField['references'] = 1
        newField['referencesdata'] = results.group(3)
        return newField
      #                     pinned INTEGER NOT NULL DEFAULT 2147483647
      #                     send_to_voicemail INTEGER NOT NULL DEFAULT 0
      results = re.match(r'(?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*\s+(\w+)\s+NOT NULL\s+DEFAULT\s+(\w+)', sqltext)
      if results:
        newField['name'] = results.group(1)
        newField['datatype'] = results.group(2)
        newField['notnull'] = True
        newField['default'] = results.group(3)
        return newField
      #                     pinned INTEGER DEFAULT 2147483647
      #                     send_to_voicemail INTEGER DEFAULT 0
      results = re.match(r'(?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*\s+(\w+)\s+DEFAULT\s+(\w+)', sqltext)
      if results:
        newField['name'] = results.group(1)
        newField['datatype'] = results.group(2)
        newField['notnull'] = True
        newField['default'] = results.group(3)
        return newField
      #                     _id INTEGER PRIMARY KEY AUTOINCREMENT
      results = re.match(r'(?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*\s+(\w+)\s+PRIMARY KEY\s+AUTOINCREMENT', sqltext)
      if results:
        newField['name'] = results.group(1)
        newField['datatype'] = results.group(2)
        newField['primarykey'] = True
        newField['autoincrement'] = True
        return newField
      #                     _id INTEGER PRIMARY KEY
      results = re.match(r'(?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*\s+(\w+)\s+PRIMARY KEY', sqltext)
      if results:
        newField['name'] = results.group(1)
        newField['datatype'] = results.group(2)
        newField['primarykey'] = True
        return newField
      #                     FileID INTEGER NOT NULL
      results = re.match(r'(?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*\s+(\w+)\s+NOT NULL', sqltext)
      if results:
        newField['name'] = results.group(1)
        newField['datatype'] = results.group(2)
        newField['notnull'] = True
        return newField
      #                     PRIMARY KEY (field_name, 
      results = re.match(r'PRIMARY KEY \((?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*\,?', sqltext)
      if results:
        field = self.fields[results.group(1)]
        field['primarykey'] = True
        self.primarykeyFlag = True
        return False
      #                     UNIQUE(field_name, 
      results = re.match(r'UNIQUE\((?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*\,?', sqltext)
      if results:
        field = self.fields[results.group(1)]
        field['unique'] = True
        self.uniqueFlag = True;
        return False
      #                     custom_ringtone TEXT
      results = re.match(r'(?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*\s+(\w+)', sqltext)
      if results:
        newField['name'] = results.group(1)
        newField['datatype'] = results.group(2)
        return newField
      #                     field_name)
      results = re.match(r'(?:[`|\"|\'])*(\w+)(?:[`|\"|\'])*(\)?)', sqltext)
      if results:
        field = self.fields[results.group(1)]
        field = self.fields[results.group(2)]
        if (self.primarykeyFlag):
          field['primarykey'] = True
          if (field):
            self.primarykeyFlag = False
        elif (self.uniqueFlag):
          field['unique'] = True
          if (field):
            self.uniqueFlag = False

        return False

      print 'INFO: field definition not recognized: "' + sqltext + '"'
      #                     photo_id INTEGER REFERENCES data(_id)
      # results = re.match(r'', sqltext)
      # if results:
      #   newField['name'] = results.group(1)
      #   newField['datatype'] = results.group(2)
      #   return newField
    except Exception, e:
      return None

    return None


  def fields(self):
    return self.fields


  def toJSON(self):
    print json.dumps(self.fields)


  def toFile(self, filehandle):
    json.dump(self.fields, filehandle, sort_keys=True, indent=4)


  def __str__(self):
    global delimeter
    retstr = ""
    retstr = json.dumps(self.fields)

    return retstr    


  def name(self):
    return self.tableName


  def SQLstr(self):
    return self.sqlStr


