content: /*
   * CDDL HEADER START
   *
   * The contents of this file are subject to the terms of the Common Development
   * and Distribution License (the "License").
   * You may not use this file except in compliance with the License.
   *
   * You can obtain a copy of the license at
   * src/com/vodafone360/people/VODAFONE.LICENSE.txt or
   * http://github.com/360/360-Engine-for-Android
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * When distributing Covered Code, include this CDDL HEADER in each file and
   * include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt.
   * If applicable, add the following below this CDDL HEADER, with the fields
   * enclosed by brackets "[]" replaced with your own identifying information:
   * Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   *
   * Copyright 2010 Vodafone Sales & Services Ltd.  All rights reserved.
   * Use is subject to license terms.
   */
  
  package com.vodafone360.people.database.tables;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import android.content.ContentValues;
  import android.database.Cursor;
  import android.database.SQLException;
  import android.database.sqlite.SQLiteDatabase;
  import android.database.sqlite.SQLiteException;
  
  import com.vodafone360.people.database.DatabaseHelper;
  import com.vodafone360.people.datatypes.Contact;
  import com.vodafone360.people.service.ServiceStatus;
  import com.vodafone360.people.utils.CloseUtils;
  import com.vodafone360.people.utils.LogUtils;
  
  /**
   * Contains all the functionality related to the contact groups database table.
   * This table stores all contact/group associations (0-many to 0-many
   * relationship). This class is never instantiated hence all methods must be
   * static.
   * 
   * @version %I%, %G%
   */
  public abstract class ContactGroupsTable {
      /**
       * Name of the table as it appears in the database
       */
      public static final String TABLE_NAME = "ContactGroupRelations";
  
      /**
       * Represents the data stored in a record
       */
      private static class ContactGroup {
  
          /**
           * The local contact ID from Contacts table
           */
          private Long mLocalContactId = null;
  
          /**
           * The server group ID
           */
          private Long mZybGroupId = null;
      }
  
      /**
       * An enumeration of all the field names in the database.
       */
      public static enum Field {
          LOCALRELATIONID("LocalRelationId"),
          LOCALCONTACTID("LocalContactId"),
          ZYBGROUPID("ZybGroupId");
  
          /**
           * The name of the field as it appears in the database
           */
          private final String mField;
  
          /**
           * Constructor
           * 
           * @param field - The name of the field (see list above)
           */
          private Field(String field) {
              mField = field;
          }
  
          /**
           * @return the name of the field as it appears in the database.
           */
          public String toString() {
              return mField;
          }
      }
  
      /**
       * Create Contact Groups Table.
       * 
       * @param writeableDb A writable SQLite database
       * @throws SQLException If an SQL compilation error occurs
       */
      public static void create(SQLiteDatabase writeableDb) throws SQLException {
          DatabaseHelper.trace(true, "ContactGroupsTable.create()");
          writeableDb.execSQL("CREATE TABLE " + TABLE_NAME + " (" + Field.LOCALRELATIONID
                  + " INTEGER PRIMARY KEY AUTOINCREMENT, " + Field.LOCALCONTACTID + " LONG, "
                  + Field.ZYBGROUPID + " LONG);");
      }
  
      /**
       * Returns a ContentValues object that can be used to insert or update a new
       * contact/group association to the table.
       * 
       * @param contactGroup A ContactGroup object with the local Contact ID and
       *            Group Id set appropriately.
       * @return The ContactValues object
       */
      private static ContentValues fillUpdateData(ContactGroup contactGroup) {
          DatabaseHelper.trace(false, "ContactGroupsTable.fillUpdateData()");
          ContentValues contactValues = new ContentValues();
          contactValues.put(Field.LOCALCONTACTID.toString(), contactGroup.mLocalContactId);
          contactValues.put(Field.ZYBGROUPID.toString(), contactGroup.mZybGroupId);
          return contactValues;
      }
  
      /**
       * Associates a contact and a group
       * 
       * @param localContactId The local contact Id from the Contacts table
       * @param zybGroupId The server group ID from groups table
       * @param writeableDb A writable SQLite database
       * @return true if successful, false otherwise
       */
      public static boolean addContactToGroup(long localContactId, long zybGroupId,
              SQLiteDatabase writableDb) {
          DatabaseHelper.trace(true, "ContactGroupsTable.addContactToGroup()");
          try {
              final ContactGroup contactGroup = new ContactGroup();
              contactGroup.mLocalContactId = localContactId;
              contactGroup.mZybGroupId = zybGroupId;
              final ContentValues values = fillUpdateData(contactGroup);
              if (writableDb.insertOrThrow(TABLE_NAME, null, values) < 0) {
                  LogUtils.logE("ContactGroupsTable.addContactToGroup() "
                          + "Unable to insert new contact group summary");
                  return false;
              }
              return true;
  
          } catch (SQLException e) {
              LogUtils.logE("ContactGroupsTable.addContactToGroup() SQLException - "
                      + "Unable to insert new contact summary", e);
              return false;
          }
      }
  
      /**
       * Remove the association between contact and group.
       * 
       * @param localContactId The local contact Id from the Contacts table
       * @param zybGroupId The server group ID from groups table
       * @param writeableDb A writable SQLite database
       * @return true if successful, false otherwise
       */
      public static boolean deleteContactFromGroup(long localContactId, long zybGroupId,
              SQLiteDatabase writableDb) {
          DatabaseHelper.trace(true, "ContactGroupsTable.deleteContactFromGroup()");
          try {
              if (writableDb.delete(TABLE_NAME, Field.LOCALCONTACTID + "=" + localContactId + " AND "
                      + Field.ZYBGROUPID + "=" + zybGroupId, null) <= 0) {
                  LogUtils.logE("ContactGroupsTable.deleteContactFromGroup() "
                          + "Unable to delete contact from group");
                  return false;
              }
              return true;
  
          } catch (SQLException e) {
              LogUtils.logE("ContactGroupsTable.deleteContactFromGroup() SQLException - "
                      + "Unable to delete contact from group", e);
              return false;
          }
      }
  
      /**
       * Removes all references to a contact from the table (should be called when
       * a contact is deleted).
       * 
       * @param localContactId The local contact Id from the Contacts table
       * @param writeableDb A writable SQLite database
       * @return true if successful, false otherwise
       */
      public static boolean deleteContact(long localContactId, SQLiteDatabase writableDb) {
          DatabaseHelper.trace(true, "ContactGroupsTable.deleteContact()");
          try {
              if (writableDb.delete(TABLE_NAME, Field.LOCALCONTACTID + "=" + localContactId, null) < 0) {
                  LogUtils.logE("ContactGroupsTable.deleteContact() "
                          + "Unable to delete contact from contact groups table");
                  return false;
              }
              return true;
  
          } catch (SQLException e) {
              LogUtils.logE("ContactGroupsTable.deleteContact() SQLException - "
                      + "Unable to delete contact from contact groups table", e);
              return false;
          }
      }
  
      /**
       * Fetches all the groups associated with a given contact.
       * 
       * @param localContactId The local contact Id from the Contacts table
       * @param groupIds A list that will be populated with the result.
       * @param readableDb A readable SQLite database
       * @return true if successful, false otherwise
       */
      public static boolean fetchContactGroups(long localContactId, List<Long> groupIds,
              SQLiteDatabase readableDb) {
          DatabaseHelper.trace(false, "ContactGroupsTable.fetchContactGroups() localContactId["
                  + localContactId + "]");
          String[] args = {
              String.format("%d", localContactId)
          };
          Cursor c1 = null;
          groupIds.clear();
          try {
              c1 = readableDb.rawQuery("SELECT " + Field.ZYBGROUPID + " FROM " + TABLE_NAME
                      + " WHERE " + Field.LOCALCONTACTID + " = ?", args);
  
              while (c1.moveToNext()) {
                  if (!c1.isNull(0)) {
                      groupIds.add(c1.getLong(0));
                  }
              }
              c1.close();
  
          } catch (SQLiteException e) {
              LogUtils.logE("ContactGroupsTable.fetchContactGroups()"
                      + " Exception - Unable to fetch contact groups", e);
              return false;
          } finally {
              CloseUtils.close(c1);
              c1 = null;
          }
  
          return true;
      }
  
      /**
       * Merges the given contact group relation changes into the database.
       * 
       * @param contact the updated contact with the new group relations. If any
       *            of the group IDs are -1 the associated will be ignored.
       * @param writeableDb A writable SQLite database
       * @return SUCCESS or a suitable error code
       */
      public static ServiceStatus modifyContact(Contact contact, SQLiteDatabase db) {
          DatabaseHelper.trace(false, "ContactGroupsTable.modifyContact()");
  
          final List<Long> contactGroups = new ArrayList<Long>();
          final List<Long> updatedContactGroups = contact.groupList;
  
          // get the contact groups
          fetchContactGroups(contact.localContactID, contactGroups, db);
  
          // check deleted groups
          for (int i = 0; i < contactGroups.size();) {
              Long groupId = contactGroups.get(i);
              if (groupId != -1) {
                  if (!updatedContactGroups.contains(groupId)) {
                      contactGroups.remove(i);
                      if (!deleteContactFromGroup(contact.localContactID, groupId, db)) {
                          return ServiceStatus.ERROR_DATABASE_CORRUPT;
                      }
                  } else {
                      i++;
                  }
              } else {
                  i++;
              }
          }
  
          // check added groups
          for (Long groupId : updatedContactGroups) {
              if (groupId != -1) {
                  if (!contactGroups.contains(groupId)) {
                      if (!addContactToGroup(contact.localContactID, groupId, db)) {
                          return ServiceStatus.ERROR_DATABASE_CORRUPT;
                      }
                  }
              }
          }
  
          return ServiceStatus.SUCCESS;
      }
  }
  