const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Ce = Components.Exception;
const CLASS_ID = Components.ID("{435ee6c1-809c-11de-8a39-0800200c9a66}");
const CLASS_NAME = "SQLite Service";
const CONTRACT_ID = "@nori090.org/sqlite-service;2";

const NSArray =
  new Components.Constructor("@mozilla.org/array;1", Ci.nsIMutableArray);

const HashPropertyBag =
  new Components.Constructor("@mozilla.org/hash-property-bag;1",
    Ci.nsIWritablePropertyBag);


const SELECT_STATEMENT = "SELECT * FROM ";

const SELECT_STATEMENT_LIMIT = " LIMIT 1";

function modicSQLiteService() {
  this._init();
}

modicSQLiteService.prototype = {

  _dbFileName : null,

  _dbConnection : null,

  _storageService : null,

  _directoryService : null,

  _init : function() {
    this._storageService =
      Cc["@mozilla.org/storage/service;1"].
        getService(Ci.mozIStorageService);
    this._directoryService =
      Cc["@mozilla.org/file/directory_service;1"].
        getService(Ci.nsIProperties);
  },

  uninit : function() {
    this._closeDBConnection();
  },

  initialize : function(aDBFileName) {
    this._dbFileName = aDBFileName;
  },

  _getDBConnection : function() {
    if (null == this._dbConnection || !this._dbConnection.connectionReady) {
      var dbFile =
        this._directoryService.get("ProfD", Ci.nsIFile);
      dbFile.append(this._dbFileName);
      this._dbConnection = this._storageService.openDatabase(dbFile);
    }

    return this._dbConnection;
  },

  _closeDBConnection : function() {
    try {
      this._dbConnection.close();
      delete this._dbConnection;
      this._dbConnection = null;
    } catch (e) {
      this._dbConnection = null;
    }
  },

  createStatement : function(aQueryString) {
    var db = this._getDBConnection();

    return db.createStatement(aQueryString);
  },

  executeSimpleSQL : function(aQueryString) {
    var db = this._getDBConnection();
    
    db.executeSimpleSQL(aQueryString);
  },

  executeNonQuery : function(aQueryStatement) {
    var db = this._getDBConnection();

    aQueryStatement.execute();

    return db.lastInsertRowID;
  },

  executeQuery : function(aQueryStatement) {
    var resultSet = new NSArray();
    var resultRow;
    var value;

    while (aQueryStatement.executeStep()) {
      resultRow = new HashPropertyBag();

      for (var i = 0; i < aQueryStatement.columnCount; i++) {
        value = "";
        switch (aQueryStatement.getTypeOfIndex(i)) {
          case aQueryStatement.VALUE_TYPE_NULL:
            value = aQueryStatement.getIsNull(i);
            break;
          case aQueryStatement.VALUE_TYPE_INTEGER:
            value = aQueryStatement.getInt32(i);
            break;
          case aQueryStatement.VALUE_TYPE_FLOAT:
            value = aQueryStatement.getDouble(i);
            break;
          case aQueryStatement.VALUE_TYPE_TEXT:
            value = aQueryStatement.getUTF8String(i);
            break;
          case aQueryStatement.VALUE_TYPE_BLOB:
            value = aQueryStatement.getBlob(i);
            break;
        }

        resultRow.setProperty(aQueryStatement.getColumnName(i), value);
      }

      resultSet.appendElement(resultRow, false);
    }

    aQueryStatement.reset();

    return resultSet;
  },

  existsColumnInTable : function(aTable, aColumn) {
    var exists = false;
    var queryStatement =
      this.createStatement(SELECT_STATEMENT + aTable + SELECT_STATEMENT_LIMIT);

    queryStatement.executeStep();

    for (var i = queryStatement.columnCount - 1; 0 <= i; i--) {
      if (queryStatement.getColumnName(i) == aColumn) {
        exists = true;
        break;
      }
    }

    queryStatement.reset();

    return exists;
  },

  closeConnection : function() {
    this._closeDBConnection();
  },

  QueryInterface : function(aIID) {
    if (!aIID.equals(Ci.modicISQLiteService) && !aIID.equals(Ci.nsISupports)) {
      throw Cr.NS_ERROR_NO_INTERFACE;
    }

    return this;
  }
};

var modicSQLiteServiceFactory = {
  createInstance: function (aOuter, aIID) {
    if (null != aOuter) {
      throw Cr.NS_ERROR_NO_AGGREGATION;
    }
    return (new modicSQLiteService()).QueryInterface(aIID);
  }
};

var modicSQLiteServiceModule = {
  registerSelf : function(aCompMgr, aLocation, aLoaderStr, aType) {
    aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
    aCompMgr.registerFactoryLocation(
      CLASS_ID, CLASS_NAME, CONTRACT_ID, aLocation, aLoaderStr, aType);
  },

  unregisterSelf : function(aCompMgr, aLocation, aLoaderStr) {
    // Close db connection
    modicSQLiteService.uninit();

    aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
    aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
  },

  getClassObject : function(aCompMgr, aClass, aIID) {
    if (!aIID.equals(Ci.nsIFactory)) {
      throw Cr.NS_ERROR_NOT_IMPLEMENTED;
    }

    if (aClass.equals(CLASS_ID)) {
      return modicSQLiteServiceFactory;
    }

    throw Cr.NS_ERROR_NO_INTERFACE;
  },

  canUnload : function(aCompMgr) {
    return true;
  }
};

function NSGetModule(aCompMgr, aFileSpec) {
  return modicSQLiteServiceModule;
}
