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.engine.meprofile;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.ListIterator;
  
  import android.graphics.BitmapFactory;
  
  import com.vodafone360.people.database.DatabaseHelper;
  import com.vodafone360.people.database.tables.ContactChangeLogTable;
  import com.vodafone360.people.database.tables.ContactsTable;
  import com.vodafone360.people.database.tables.StateTable;
  import com.vodafone360.people.database.tables.ContactChangeLogTable.ContactChangeInfo;
  import com.vodafone360.people.database.tables.ContactChangeLogTable.ContactChangeType;
  import com.vodafone360.people.datatypes.Contact;
  import com.vodafone360.people.datatypes.ContactChanges;
  import com.vodafone360.people.datatypes.ContactDetail;
  import com.vodafone360.people.datatypes.UserProfile;
  import com.vodafone360.people.datatypes.ContactDetail.DetailKeys;
  import com.vodafone360.people.engine.presence.PresenceDbUtils;
  import com.vodafone360.people.service.ServiceStatus;
  import com.vodafone360.people.utils.LogUtils;
  import com.vodafone360.people.utils.ThirdPartyAccount;
  import com.vodafone360.people.utils.ThumbnailUtils;
  /**
   * This class is a set of utility methods called by
   * SyncMeEngine to save/read data to/from the database. 
   *
   */
  public class SyncMeDbUtils {
      /** 
       * Default Me Profile Name. 
       * This is set as an empty string to make it easy to detect when no Name has been set.
       */
      public static final String ME_PROFILE_DEFAULT_NAME = "";
      
      /**
       * Me profile local contact id.
       */
      private static Long sMeProfileLocalContactId;
  
      /**
       * Mime type for the uploaded thumbnail picture of the me profile.
       */
      private static final String PHOTO_MIME_TYPE = "image/png";
  
      /**
       * This method create a Me Profile contact in the database.
       * @param dbHelper DatabaseHelper - the database.
       * @param meProfile Contact - the Me Profile contact
       * @return ServiceStatus - ServiceStatus.SUCCESS when the new contact is
       *         successfully created.
       */
      public static ServiceStatus setMeProfile(final DatabaseHelper dbHelper, Contact meProfile) {
          ServiceStatus status = ServiceStatus.ERROR_DATABASE_CORRUPT;
          // the contact didn't exist before
          if (sMeProfileLocalContactId == null) {
              List<Contact> contactList = new ArrayList<Contact>();
              contactList.add(meProfile);
              status = dbHelper.syncAddContactList(contactList, false, false);
              if (ServiceStatus.SUCCESS == status) {
                  sMeProfileLocalContactId = meProfile.localContactID;
                  status = StateTable.modifyMeProfileID(sMeProfileLocalContactId, dbHelper.getWritableDatabase());
                  PresenceDbUtils.resetMeProfileIds();
                  if (ServiceStatus.SUCCESS != status) {
                      List<ContactsTable.ContactIdInfo> idList = new ArrayList<ContactsTable.ContactIdInfo>();
                      ContactsTable.ContactIdInfo contactIdInfo = new ContactsTable.ContactIdInfo();
                      contactIdInfo.localId = meProfile.localContactID;
                      contactIdInfo.serverId = meProfile.contactID;
                      contactIdInfo.nativeId = meProfile.nativeContactId;
                      idList.add(contactIdInfo);
                      dbHelper.syncDeleteContactList(idList, false, false);
                  }
              }
          }
          return status;
      }
  
      /**
       * This method reads Me Profile contact from the database.
       * @param dbHelper DatabaseHelper - the database
       * @param contact Contact - the empty (stub) contact to read into
       * @return ServiceStatus - ServiceStatus.SUCCESS when the contact is
       * successfully filled, ServiceStatus.ERROR_NOT_FOUND - if the Me
       * Profile needs to be created first.
       */
      public static ServiceStatus fetchMeProfile(final DatabaseHelper dbHelper, Contact contact) {
          if (sMeProfileLocalContactId == null) {
              return ServiceStatus.ERROR_NOT_FOUND;
          }
          return dbHelper.fetchContact(sMeProfileLocalContactId, contact);
      }
  
      /**
       * This method returns the Me Profile localContactId...
       * @param dbHelper DatabaseHelper - the database
       * @return Long - Me Profile localContactId.
       */
      public static Long getMeProfileLocalContactId(DatabaseHelper dbHelper) {
          if (dbHelper == null)
              return null;
          if (sMeProfileLocalContactId == null) {
              sMeProfileLocalContactId = StateTable.fetchMeProfileId(dbHelper.getReadableDatabase());
          }
          return sMeProfileLocalContactId;
      }
  
      /**
       * This method sets Me Profile localContactId...
       * @param meProfileId Long - localContactID
       */
      public static void setMeProfileId(final Long meProfileId) {
          sMeProfileLocalContactId = meProfileId;
      }
      
      /**
       * Checks if a given contact id belongs to the Me Contact.
       * @param dbHelper DatabaseHelper - the database
  	 * @param localContactId The contact id to check
       * @return True if contact being edited is the me profile.
       */
      public static boolean isMeProfile(DatabaseHelper dbHelper, long localContactId) {
          return Long.valueOf(
                  localContactId).equals(SyncMeDbUtils.getMeProfileLocalContactId(dbHelper));
      }
  
  
      /**
       * This method updates current Me Profile with changes from user profile.
       * @param dbHelper DatabaseHelper - database
       * @param currentMeProfile Contact - current me profile, from DB
       * @param profileChanges - the changes to the current Me Profile
       * @return String - the profile avatar picture url, null if no picture can
       *  be found.
       */
      public static String updateMeProfile(final DatabaseHelper dbHelper, final Contact currentMeProfile,
              final UserProfile profileChanges) {
          if (processMyContactChanges(dbHelper, currentMeProfile, profileChanges) == ServiceStatus.SUCCESS) {
              return processMyContactDetailsChanges(dbHelper, currentMeProfile, profileChanges);
          }
          return null;
      }
  
      /**
       * This method stores the getMyChanges() response to database - contacts part.
       * @param dbHelper DatabaseHelper - database.
       * @param currentMeProfile Contact - me profile contact.
       * @param profileChanges UserProfile - the contact changes.
       * @return ServiceStatus - SUCCESS if the contact changes have been successfully processed stored. 
       */
      private static ServiceStatus processMyContactChanges(final DatabaseHelper dbHelper,
              final Contact currentMeProfile, final UserProfile profileChanges) {
  
          boolean profileChanged = false;
          if (profileChanges.userID != null) {
              currentMeProfile.userID = profileChanges.userID;
              profileChanged = true;
          }
          if (profileChanges.aboutMe != null) {
              currentMeProfile.aboutMe = profileChanges.aboutMe;
              profileChanged = true;
          }
          if (profileChanges.contactID != null) {
              currentMeProfile.contactID = profileChanges.contactID;
              profileChanged = true;
          }
          if (profileChanges.gender != null) {
              currentMeProfile.gender = profileChanges.gender;
              profileChanged = true;
          }
          if (profileChanges.profilePath != null) {
              currentMeProfile.profilePath = profileChanges.profilePath;
              profileChanged = true;
          }
          if (profileChanges.sources != null) {
              currentMeProfile.sources.clear();
              currentMeProfile.sources.addAll(profileChanges.sources);
              profileChanged = true;
          }
          if (profileChanges.updated != null) {
              currentMeProfile.updated = profileChanges.updated;
              profileChanged = true;
          }
          if (profileChanged) {
              ArrayList<Contact> contactList = new ArrayList<Contact>();
              contactList.add(currentMeProfile);
              return dbHelper.syncModifyContactList(contactList, false, false);
          }
          return ServiceStatus.SUCCESS;
      }
  
      /**
       * This method stores the getMyChanges() response to database - details part.
       * @param dbHelper DatabaseHelper - database.
       * @param currentMeProfile Contact - me profile contact.
       * @param profileChanges UserProfile - the contact changes.
       * @return ServiceStatus - SUCCESS if the contact changes have been successfully processed stored. 
       */
      private static String processMyContactDetailsChanges(final DatabaseHelper dbHelper,
              final Contact currentMeProfile, final UserProfile profileChanges) {
          String ret = null;
          final ArrayList<ContactDetail> modifiedDetailList = new ArrayList<ContactDetail>();
          final ArrayList<ContactDetail> addedDetailList = new ArrayList<ContactDetail>();
          final ArrayList<ContactDetail> deletedDetailList = new ArrayList<ContactDetail>();
  
          for (ContactDetail newDetail : profileChanges.details) {
              boolean found = false;
              for (int i = 0; i < currentMeProfile.details.size(); i++) {
                  ContactDetail oldDetail = currentMeProfile.details.get(i);
                  if (DatabaseHelper.doDetailsMatch(newDetail, oldDetail)) {
                      found = true;
                      if (newDetail.deleted != null && newDetail.deleted.booleanValue()) {
                          deletedDetailList.add(oldDetail);
                      } else if (DatabaseHelper.hasDetailChanged(oldDetail, newDetail)) {
                          newDetail.localDetailID = oldDetail.localDetailID;
                          newDetail.localContactID = oldDetail.localContactID;
                          newDetail.nativeContactId = oldDetail.nativeContactId;
                          newDetail.nativeDetailId = oldDetail.nativeDetailId;
                          modifiedDetailList.add(newDetail);
  
                          if (newDetail.key == DetailKeys.PHOTO) {
                              dbHelper.markMeProfileAvatarChanged();
                              ret = newDetail.value;
                          }
                      }
                      break;
                  }
              }
              
              // if the detail was not found in the old profile and either there was no deleted flag 
              // in the response or the deleted flag was false we have to add the new detail to the list
              if ( (!found) && ((null == newDetail.deleted) || (!newDetail.deleted.booleanValue())) ) {
                  newDetail.localContactID = currentMeProfile.localContactID;
                  newDetail.nativeContactId = currentMeProfile.nativeContactId;
                  
                  if (newDetail.key == DetailKeys.PHOTO) {
                      dbHelper.markMeProfileAvatarChanged();
                      ret = newDetail.value;
                  }
                  addedDetailList.add(newDetail);
              }
          }
          if (!addedDetailList.isEmpty()) {
              dbHelper.syncAddContactDetailList(addedDetailList, false, false);
          }
          if (!modifiedDetailList.isEmpty()) {
              dbHelper.syncModifyContactDetailList(modifiedDetailList, false, false);
          }
          if (!deletedDetailList.isEmpty()) {
              dbHelper.syncDeleteContactDetailList(deletedDetailList, false, false);
          }
          return ret;
  
      }
  
      /**
       * The utility method to save Contacts/setMe() response to the database...
       * @param dbHelper Database - database
       * @param uploadedMeProfile Contact - me profile which has been uploaded in
       *            Contacts/setMe() call
       * @param result ContactChanges - the contents of response Contacts/setMe().
       *            The contact details in response need to be in the same order
       *            as they were in setMe() request
       */
      public static void updateMeProfileDbDetailIds(final DatabaseHelper dbHelper,
              final ArrayList<ContactDetail> uploadedDetails, final ContactChanges result) {
  
          Contact uploadedMeProfile = new Contact();
          SyncMeDbUtils.fetchMeProfile(dbHelper, uploadedMeProfile);
          boolean changed = false;
          if (result.mUserProfile.userID != null
                  && !result.mUserProfile.userID.equals(uploadedMeProfile.userID)) {
              uploadedMeProfile.userID = result.mUserProfile.userID;
              changed = true;
          }
          if (result.mUserProfile.contactID != null
                  && !result.mUserProfile.contactID.equals(uploadedMeProfile.contactID)) {
              uploadedMeProfile.contactID = result.mUserProfile.contactID;
              changed = true;
          }
          if (changed) {
              dbHelper.modifyContactServerId(uploadedMeProfile.localContactID,
                      uploadedMeProfile.contactID, uploadedMeProfile.userID);
          }
          ListIterator<ContactDetail> destIt = uploadedDetails.listIterator();
  
          for (ContactDetail srcDetail : result.mUserProfile.details) {
              if (!destIt.hasNext()) {
                  LogUtils
                          .logE("SyncMeDbUtils updateProfileDbDetailsId() - # of details in response > # in request");
                  return;
              }
              final ContactDetail destDetail = destIt.next();
              if (srcDetail.key == null || !srcDetail.key.equals(destDetail.key)) {
                  LogUtils.logE("SyncMeDbUtils updateProfileDbDetailsId() - details order is wrong");
                  break;
              }
              
              destDetail.unique_id = srcDetail.unique_id;
              dbHelper.syncContactDetail(destDetail.localDetailID, destDetail.unique_id);
          }
      }
  
      /**
       * The utility method to save the status text change to the database...
       * @param dbHelper DatabaseHelper - database
       * @param statusText String - status text
       * @return ContactDetail - the modified or created ContactDetail with key
       *         ContactDetail.DetailKeys.PRESENCE_TEXT
       */
      public static ContactDetail updateStatus(final DatabaseHelper dbHelper, final String statusText) {
          Contact meContact = new Contact();
          if (fetchMeProfile(dbHelper, meContact) == ServiceStatus.SUCCESS) {
              // try to modify an existing detail
              for (ContactDetail detail : meContact.details) {
                  if (detail.key == ContactDetail.DetailKeys.PRESENCE_TEXT) {
                      detail.value = statusText;
                      // Currently it's only possible to post a status on
                      // Vodafone sns
                      detail.alt = ThirdPartyAccount.SNS_TYPE_VODAFONE;
                      if (ServiceStatus.SUCCESS == dbHelper.modifyContactDetail(detail)) {
                          return detail;
                      }
                  }
              }
              // create a new detail instead
              ContactDetail contactDetail = new ContactDetail();
              contactDetail.setValue(statusText, ContactDetail.DetailKeys.PRESENCE_TEXT, null);
              contactDetail.alt = ThirdPartyAccount.SNS_TYPE_VODAFONE;
              contactDetail.localContactID = meContact.localContactID;
              if (ServiceStatus.SUCCESS == dbHelper.addContactDetail(contactDetail)) {
                  return contactDetail;
              }
          }
          return null;
      }
  
      /**
       * The utility method to save Contacts/setMe() response for the status text
       * change to the database...
       * @param dbHelper DatabaseHelper - database.
       * @param ContactChanges result - status text change.
       */
      public static void savePresenceStatusResponse(final DatabaseHelper dbHelper, ContactChanges result) {
          Contact currentMeProfile = new Contact();
          if (ServiceStatus.SUCCESS == SyncMeDbUtils.fetchMeProfile(dbHelper, currentMeProfile)) {
              boolean changed = false;
              if (result.mUserProfile.userID != null
                      && (!result.mUserProfile.userID.equals(currentMeProfile.userID))) {
                  currentMeProfile.userID = result.mUserProfile.userID;
                  changed = true;
              }
              if (result.mUserProfile.contactID != null
                      && (!result.mUserProfile.contactID.equals(currentMeProfile.contactID))) {
                  currentMeProfile.contactID = result.mUserProfile.contactID;
                  changed = true;
              }
              if (changed) {
                  dbHelper.modifyContactServerId(currentMeProfile.localContactID,
                          currentMeProfile.contactID, currentMeProfile.userID);
              }
              
              for (ContactDetail oldStatus : currentMeProfile.details) {
                  if (oldStatus.key == ContactDetail.DetailKeys.PRESENCE_TEXT) {
                      for (ContactDetail newStatus : result.mUserProfile.details) {
                          if (newStatus.key == ContactDetail.DetailKeys.PRESENCE_TEXT) {
                              oldStatus.unique_id = newStatus.unique_id;
                              dbHelper.syncContactDetail(oldStatus.localDetailID, oldStatus.unique_id);
                              break;
                          }
                      }
                  }
              }
          }
      }
  
      /**
       * A utility method to save the Me Profile contact before sending the
       * updates to backend
       * 
       * @param dbHelper DataBaseHelper - database
       * @param meProfile - the new me Profile to push to server
       * @return - ArrayList of ContactDetails to be pushed to server
       */
      public static ArrayList<ContactDetail> saveContactDetailChanges(final DatabaseHelper dbHelper,
              final Contact meProfile) {
          ArrayList<ContactDetail> updates = new ArrayList<ContactDetail>();
          populateWithModifiedDetails(dbHelper, updates, meProfile);
  
          // add the deleted details from the change log table to the contact
          // details
          populateWithDeletedContactDetails(dbHelper, updates, meProfile.contactID);
          return updates;
      }
  
      private static void populateWithModifiedDetails(final DatabaseHelper dbHelper,
              final ArrayList<ContactDetail> updates, Contact meProfile) {
          boolean avatarChanged = dbHelper.isMeProfileAvatarChanged();
          for (ContactDetail detail : meProfile.details) {
              // LogUtils.logV("meProfile.details:" + detail);
              if (avatarChanged && detail.key == ContactDetail.DetailKeys.PHOTO) {
                  populatePhotoDetail(dbHelper, meProfile, detail);
                  updates.add(detail);
  
              } else if (detail.key != ContactDetail.DetailKeys.VCARD_INTERNET_ADDRESS
                      && detail.key != ContactDetail.DetailKeys.VCARD_IMADDRESS
                      && detail.key != ContactDetail.DetailKeys.PRESENCE_TEXT) {
                  // fix for bug 16029 - it's a server issue (getMe() returns
                  // broken details) but the workaround on the client side is
                  // just
                  // not to add the extra details to setMe() request
                  detail.updated = null;
                  // LogUtils.logV("meProfile.details: put");
                  updates.add(detail);
              }
          }
      }
  
      /**
       * This method reads a photo data from a file into the ContactDetail...
       * @param dbHelper DatabaseHelper - database 
       * @param meProfile Contact - me profile contact
       * @param detail ContactDetail - the detail to write the photo into.
       */
      private static void populatePhotoDetail(final DatabaseHelper dbHelper, final Contact meProfile,
              final ContactDetail detail) {
          String path = ThumbnailUtils.thumbnailPath(meProfile.localContactID);
          detail.photo = BitmapFactory.decodeFile(path);
          if (detail.photo == null) {
              LogUtils.logE("SyncMeDbUtils saveContactDetailChanges: " + "Unable to decode avatar");
          }
          detail.photo_mime_type = PHOTO_MIME_TYPE;
          // when sending the "bytes" the "val" (photoDetail.value)has to
          // be null, otherwise the the picture on the website is not
          // updated
          detail.value = null;
          detail.updated = null;
          detail.order = 0;
      }
  
      /**
       * This method adds the deleted details to the detail list sent to server...
       * @param dbHelper DatabaseHelper - database 
       * @param contactDetails List<ContactDetail> - the deleted details list 
       * @param contactId Long - Me Profile local contact id.
       */
      private static void populateWithDeletedContactDetails(final DatabaseHelper dbHelper,
              final List<ContactDetail> contactDetails, final Long contactId) {
          List<ContactChangeInfo> deletedDetails = new ArrayList<ContactChangeInfo>();
          if (!ContactChangeLogTable.fetchMeProfileChangeLog(deletedDetails,
                  ContactChangeType.DELETE_DETAIL, dbHelper.getReadableDatabase(), contactId)) {
              LogUtils.logE("UploadServerContacts populateWithDeletedContactDetails -"
                      + " Unable to fetch contact changes from database");
              return;
          }
          for (int i = 0; i < deletedDetails.size(); i++) {
              ContactChangeInfo info = deletedDetails.get(i);
              final ContactDetail detail = new ContactDetail();
  
              detail.localDetailID = info.mLocalDetailId;
              detail.key = info.mServerDetailKey;
              detail.unique_id = info.mServerDetailId;
              detail.deleted = true;
  
              contactDetails.add(detail);
          }
          dbHelper.deleteContactChanges(deletedDetails);
      }
  
  }
  