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.contactsync;
  
  import com.vodafone360.people.engine.contactsync.NativeContactsApi.Account;
  import com.vodafone360.people.utils.LogUtils;
  import com.vodafone360.people.utils.VersionUtils;
  
  public class NativeExporter {
  
      /**
       * The undefined result when the NativeImporter has not been run yet.
       * @see #getResult()
       */
      public final static int RESULT_UNDEFINED = -1;
      
      /**
       * The ok result when the NativeImporter has finished successfully.
       * @see #getResult()
       */
      public final static int RESULT_OK = 0;
      
      /**
       * The undefined result when the NativeImporter has not been run yet.
       * @see #getResult()
       */
      public final static int RESULT_ERROR = 1;
      
      /**
       * Number of contacts to be processed "per tick".
       * @see #tick()
       */
      private final static int MAX_CONTACTS_OPERATION_COUNT = 2;
      
      /**
       * Handler to the People Contacts API.
       */
      private PeopleContactsApi mPeopleContactsApi;
      
      /**
       * Handle to the Native Contacts API. 
       */
      private NativeContactsApi mNativeContactsApi;
      
      /**
       * Internal state representing the task to perform: gets the list of local contacts IDs to be synced to Native.
       */
      private final static int STATE_GET_CONTACT_IDS = 0;
      
      /**
       * Internal state representing the task to perform: iterates through the list of syncable contacts IDs and sync to Native side.
       */
      private final static int STATE_ITERATE_THROUGH_IDS = 1;
      
      /**
       * Internal state representing the task to perform: final state, nothing else to perform.
       */
      private final static int STATE_DONE = 2;
      
      /**
       * The current state.
       */
      private int mState = STATE_GET_CONTACT_IDS;
      
      /**
       * The list of local IDs from people contacts that need to be synced to Native.
       */
      private long[] mSyncableContactsIds;
      
      /**
       * The index in the current people id.
       * @see #mSyncableContactsIds
       */
      private int mCurrentSyncableIdIndex = 0;
      
      /**
       * The result status.
       */
      private int mResult = RESULT_UNDEFINED;
      
      /**
       * The total count of IDs to process (Native Database + People Database).
       */
      private int mTotalIds = 0;
      
      /**
       * The current count of processed IDs.
       */
      private int mProcessedIds = 0;
      
      /**
       * The Native Account where to write the Contacts. 
       */
      private Account mAccount = null;
      
      /**
       * Constructor.
       * 
       * @param pca handler to the People Contacts API
       * @param nca handler to the Native Contacts API
       */
      public NativeExporter(PeopleContactsApi pca, NativeContactsApi nca) {
          
          mPeopleContactsApi = pca;
          mNativeContactsApi = nca;
          
          if (VersionUtils.is2XPlatform()) {
              
              final Account[] accounts = mNativeContactsApi.getAccountsByType(NativeContactsApi.PEOPLE_ACCOUNT_TYPE);
              
              if (accounts != null) {
                  
                  mAccount = accounts[0];
              }
          }
      }
      
      /**
       * Gets the current position in the list of IDs.
       * 
       * This can be used to track the current progress.
       * @see #getCount()
       * 
       * @return the last processed id position in the list of IDs
       */
      public int getPosition() {
          
          return mProcessedIds;
      }
      
      /**
       * Gets the total number of IDs to process.
       * 
       * @return the number of IDs to process
       */
      public int getCount() {
          
          return mTotalIds;
      }
      
      /**
       * Sets the internal state to DONE with the provided result status.
       * 
       * @param result the result status to set
       */
      private void complete(int result) {
          
          mState = STATE_DONE;
          mResult = result;
      }
  
      
      /**
       * Tick method to call each time there is time for processing the Native Contacts Import.
       * 
       * Note: the method will block for some time to process a certain number of Contacts and then will
       *       return. It will have to be called until it returns true meaning that the Import is over.
       * 
       * @return true when the Import Task is finished, false if not
       */
      public boolean tick() {
  
          switch(mState) {
              case STATE_GET_CONTACT_IDS:
                  getContactIds();
                  break;
              case STATE_ITERATE_THROUGH_IDS:
                  iterateThroughSyncableIds();
                  break;
          }
          
          return mState == STATE_DONE;
      }
          
      /**
       * Gets the Import result.
       * 
       * @see #RESULT_OK
       * @see #RESULT_ERROR
       * @see #RESULT_UNDEFINED
       * 
       * @return the Import result
       */
      public int getResult() {
          
          return mResult;
      }
      
      /**
       * Gets the list of local contacts IDs that need to be synced to Native.
       */
      private void getContactIds() {
          
          LogUtils.logD("NativeExporter.getIdList()");
          
          mSyncableContactsIds = mPeopleContactsApi.getNativeSyncableContactIds();
          
          // check if we have some work to do
          if (mSyncableContactsIds != null) {
              mState = STATE_ITERATE_THROUGH_IDS;    
          } else {
              complete(RESULT_OK);
          }
      }
      
      /**
       * Iterates through the list of syncable contacts IDs and sync to Native side.
       */
      private void iterateThroughSyncableIds() {
          
          LogUtils.logD("NativeExporter.iterateThroughSyncableIds()");
          
          final int limit = Math.min(mSyncableContactsIds.length, mCurrentSyncableIdIndex + MAX_CONTACTS_OPERATION_COUNT);
          
          while (mCurrentSyncableIdIndex < limit) {
              
              final ContactChange[] changes = mPeopleContactsApi.getNativeSyncableContactChanges(mSyncableContactsIds[mCurrentSyncableIdIndex]);
              
              if (changes != null) {
                  
                  exportContactChanges(changes);
              }
  
              mCurrentSyncableIdIndex++;
          }
          
          if (mCurrentSyncableIdIndex == mSyncableContactsIds.length) {
              // Nothing else to do
              complete(RESULT_OK);
          }
      }
      
      /**
       * Exports the contact changes to the native address book.
       * 
       * @param changes the array of ContactChange that represent a full contact, a deleted contact or an updated contact
       */
      private void exportContactChanges(ContactChange[] changes) {
          
          // the ContactChange array that we'll get back from native
          ContactChange[] nativeResponse = null;
          
          switch(changes[0].getType()) {
              
              case ContactChange.TYPE_ADD_CONTACT:
                  // add the contact on Native side
                  
                  //the account can be null (theoretically)
                  if (mAccount != null) {
                      nativeResponse = mNativeContactsApi.addContact(mAccount, changes);
                      // sync back the native IDs on People side
                      if (!mPeopleContactsApi.syncBackNewNativeContact(changes, nativeResponse)) {
                          LogUtils.logE("NativeExporter.exportContactChanges() - Add Contact failed!");
                      }    
                  }
                  break;
              case ContactChange.TYPE_DELETE_CONTACT:
                  // delete the contact on Native side
                  mNativeContactsApi.removeContact(changes[0].getNabContactId());
                  // acknowledge the people side about deletion
                  if (!mPeopleContactsApi.syncBackDeletedNativeContact(changes[0])) {
                      LogUtils.logE("NativeExporter.exportContactChanges() - Syncing back Contact deletion to Client side failed!");
                  }
                  break;
              case ContactChange.TYPE_UPDATE_CONTACT:
              case ContactChange.TYPE_ADD_DETAIL:
              case ContactChange.TYPE_DELETE_DETAIL:
              case ContactChange.TYPE_UPDATE_DETAIL:
                  // update the contact on Native side
                  nativeResponse = mNativeContactsApi.updateContact(changes);
                  // acknowledge People side about deleted details and added details Native IDs
                  if (!mPeopleContactsApi.syncBackUpdatedNativeContact(changes, nativeResponse)) {
                      LogUtils.logE("NativeExporter.exportContactChanges() - Update Contact failed!");
                  }
                  break;
              default:
                  LogUtils.logE("NativeExporter.exportContactChanges() - Aborted exporting because of unknown type("+changes[0].getType()+")!");
                  break;
          }
      }
  }
  