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.presence;
  
  import java.util.ArrayList;
  import java.util.Hashtable;
  import java.util.List;
  
  import android.database.sqlite.SQLiteDatabase;
  import android.text.TextUtils;
  
  import com.vodafone360.people.database.DatabaseHelper;
  import com.vodafone360.people.database.tables.ContactDetailsTable;
  import com.vodafone360.people.database.tables.ContactSummaryTable;
  import com.vodafone360.people.database.tables.PresenceTable;
  import com.vodafone360.people.datatypes.Contact;
  import com.vodafone360.people.datatypes.ContactDetail;
  import com.vodafone360.people.datatypes.Identity;
  import com.vodafone360.people.datatypes.ContactSummary.OnlineStatus;
  import com.vodafone360.people.engine.EngineManager;
  import com.vodafone360.people.engine.meprofile.SyncMeDbUtils;
  import com.vodafone360.people.engine.presence.NetworkPresence.SocialNetwork;
  import com.vodafone360.people.service.ServiceStatus;
  import com.vodafone360.people.service.agent.UiAgent;
  import com.vodafone360.people.utils.LogUtils;
  
  public class PresenceDbUtils {
      /**
       * Invalid general purpose id
       * TODO: Move this and all other values like this to a common place
       */
      private static final long INVALID_ID = -1L;
      /**
       * the user id of the me profile contact
       */
      private static long sMeProfileUserId = INVALID_ID;
      /**
       * the local contact id of the me profile contact
       */
      private static long sMeProfileLocalContactId = INVALID_ID;
  
      public static void resetMeProfileIds() {
          sMeProfileUserId = INVALID_ID;
          sMeProfileLocalContactId = INVALID_ID;
      }
      
      /**
       * This method returns true if the provided user id matches the one of me profile.
       * @return TRUE if the provided user id matches the one of me profile.
       */
      private static boolean isMeProfile(String userId, DatabaseHelper databaseHelper) {
          return userId.equals(String.valueOf(getMeProfileUserId(databaseHelper)));
      }
      
      /**
       * @param databaseHelper
       * @return
       */
      protected static Long getMeProfileUserId(DatabaseHelper databaseHelper) {
          if (sMeProfileUserId == INVALID_ID) {
              Contact meProfile = new Contact();
              if (SyncMeDbUtils.fetchMeProfile(databaseHelper, meProfile) != ServiceStatus.ERROR_NOT_FOUND) {
                  sMeProfileUserId = meProfile.userID;
                  sMeProfileLocalContactId = meProfile.localContactID;
              }
  
          }
          return sMeProfileUserId;
  
      }
  
      /**
       * @param databaseHelper
       * @return
       */
      protected static User getMeProfilePresenceStatus(DatabaseHelper databaseHelper) {
          // if
          // (!sMeProfileLocalContactId.equals(databaseHelper.getMeProfileId())) {
          Long meProfileId = SyncMeDbUtils.getMeProfileLocalContactId(databaseHelper);
          sMeProfileLocalContactId = 
              (meProfileId == null || meProfileId.intValue() == INVALID_ID) ? INVALID_ID
                  : meProfileId;
          // LogUtils.logE("The DB Helper and Utils IDs are not synchronized");
          // }
          if(meProfileId == INVALID_ID) {
              LogUtils.logW(
                      "PresenceDbUtils.getMeProfilePresenceStatus() No local me contact id!");
          }
          
          User user = PresenceTable.getUserPresenceByLocalContactId(
                  meProfileId, databaseHelper.getWritableDatabase());
          if (user == null || (user.getPayload() == null)) { // the table is
              /* empty, need to set the status for the 1st time 
               * Get presence list constructed from identities
               */
              Hashtable<String, String> status =             
                  EngineManager.getInstance().getPresenceEngine().
                      getPresencesForStatus(OnlineStatus.ONLINE);
              user = new User(String.valueOf(getMeProfileUserId(databaseHelper)), status);
          }
                  
          return user;
      }
  
      /**
       * This method returns wrapper with the presence information for all user
       * networks
       * 
       * @param localContactId - the localContactId of the contact we want to get
       *            presence states information for.
       * @param databaseHelper
       * @return User wrapper with the presence information for all user networks.
       *         If no information is on the database the payload is NULL
       */
      public static User getUserPresenceStatusByLocalContactId(long localContactId,
              DatabaseHelper databaseHelper) {
          User user = PresenceTable.getUserPresenceByLocalContactId(localContactId, databaseHelper
                  .getWritableDatabase());
          LogUtils.logW("UI  called getUserPresenceStatusByLocalContactId: " + user);
          return user;
      }
  
      /**
       * Here we update the PresenceTable, and the ContactSummaryTable afterwards
       * the HandlerAgent receives the notification of presence states changes.
       *  
       * @param users List<User> - the list of user presence states
       * @param idListeningTo long - local contact id which this UI is watching, -1 is all contacts
       * @param dbHelper DatabaseHelper - the database.
       * @return TRUE if database has changed in result of the update.
       */
      protected static boolean updateDatabase(List<User> users, long idListeningTo,
              DatabaseHelper dbHelper) {
          boolean presenceChanged = false;
          boolean deleteNetworks = false;
           // list of network presence information we ignore - the networks where the user is offline.
          ArrayList<Integer> ignoredNetworks = new ArrayList<Integer>();
          SQLiteDatabase writableDb = dbHelper.getWritableDatabase();
  
          for (User user : users) {
              if (!user.getPayload().isEmpty()) {
                  long localContactId = -1;
                  ArrayList<NetworkPresence> payload = user.getPayload();
                  // if it is the me profile User
                  boolean meProfile = false;
                  String userId = null;
                  int networkId = 0;
                  for (NetworkPresence presence : payload) {
                      userId = presence.getUserId();
                      if (!TextUtils.isEmpty(userId)) {
                          networkId = presence.getNetworkId();
                          // if this is me profile contact 
                          if (isMeProfile(userId, dbHelper)) { 
                              localContactId = sMeProfileLocalContactId;
                              meProfile = true;
                              
                           } else { // 3rd party accounts
                               localContactId = ContactDetailsTable.findLocalContactIdByKey(
                                      SocialNetwork.getSocialNetworkValue(networkId).toString(), userId,
                                      ContactDetail.DetailKeys.VCARD_IMADDRESS, writableDb);
                               if (localContactId != -1) {
                                   break;
                               }
                          }
                      }
                  }
                  // set the local contact id
                  user.setLocalContactId(localContactId);
                  if (meProfile) {
                      if (deleteNetworks = processMeProfile(presenceChanged, user, ignoredNetworks)) {
                          // delete the information about offline networks from PresenceTable 
                          PresenceTable.setTPCNetworksOffline(ignoredNetworks, writableDb);
                      }
                  } 
                  if (user.getLocalContactId() > -1) {
                      // will not save infos from the ignored networks
                      updateUserRecord(user, ignoredNetworks, writableDb);
                      if (user.getLocalContactId() == idListeningTo) {
                          presenceChanged = true;    
                      }
                  }
              }
          }
          // if contact summary table needs extra refresh, to make sure no statuses are displayed for offline TPC networks users
          if (deleteNetworks) {
              ArrayList<Long> userIds = PresenceTable.getLocalContactIds(dbHelper.getWritableDatabase());
              ContactSummaryTable.setUsersOffline(userIds);
              presenceChanged = true;
          }
          if (idListeningTo == UiAgent.ALL_USERS) {
              presenceChanged = true;
          }
          return presenceChanged;
      }
      
      /**
       * This method writes the user presence status change from the passed User object
       *  to the database and then fills the same User object with updated information.  
       * @param user - the User presence change.
       * @param ignoredNetworks - the networks information from which must be ignored.
       * @param writableDb - database.
       */
      private static void updateUserRecord(User user , ArrayList<Integer> ignoredNetworks, SQLiteDatabase writableDb) {
  //      write the user presence update into the database and read the complete wrapper
          PresenceTable.updateUser(user, ignoredNetworks, writableDb);
          PresenceTable.getUserPresence(user, writableDb);
          
  //      update the user aggregated presence state in the ContactSummaryTable
          ContactSummaryTable.updateOnlineStatus(user);
      }
  
      /**
       * This method alters the User wrapper of me profile,
       *  and returns true if me profile information contains the ignored TPC networks information.
       * Based on the result this information may be deleted.
       * @param removePCPresence - if TRUE the PC network will be removed from the network list.
       * @param user - the me profile wrapper.
       * @param ignoredNetworks - the list if ignored integer network ids. 
       * @return
       */
      private static boolean processMeProfile(boolean removePCPresence, User user, ArrayList<Integer> ignoredNetworks){
          if (removePCPresence) {
              int max = OnlineStatus.OFFLINE.ordinal();
              // calculate the new aggregated presence status
              for (NetworkPresence presence : user.getPayload()) {
                  if (presence.getOnlineStatusId() > max) {
                      max = presence.getOnlineStatusId();
                  }
              }    
              user.setOverallOnline(max);                        
          }
          // the list of chat network ids in this User wrapper                
          ArrayList<Integer> userNetworks = new ArrayList<Integer>();
  
          ArrayList<NetworkPresence> payload = user.getPayload();
          
          for (NetworkPresence presence : payload) {
              int networkId = presence.getNetworkId();
              userNetworks.add(networkId);
              // 1. ignore offline TPC networks 
              if (presence.getOnlineStatusId() == OnlineStatus.OFFLINE.ordinal()) {
                  ignoredNetworks.add(networkId);
              }    
          }
          // 2. ignore the TPC networks presence state for which is unknown
          ArrayList<Identity> identities = EngineManager.getInstance().getIdentityEngine().getMyChattableIdentities();   
          SocialNetwork network = null;
          for (Identity identity : identities) {
              network = SocialNetwork.getValue(identity.mNetwork);
              if (network != null) {
                  if (!userNetworks.contains(network.ordinal())) {
                      ignoredNetworks.add(network.ordinal());
                  }   
              }
          }
           
          return !ignoredNetworks.isEmpty();
      }
      
      
      protected static boolean updateMyPresence(User user, DatabaseHelper dbHelper) {
          boolean contactsChanged = false;
          if (PresenceTable.updateUser(
          		user, null, dbHelper.getWritableDatabase()) != PresenceTable.USER_NOTADDED) {
              contactsChanged = (ContactSummaryTable.updateOnlineStatus(user) == ServiceStatus.SUCCESS);
          }
          return contactsChanged;
      }
  
      /**
       * Set all users to offline state
       * 
       * @param dbHelper
       */
      protected static void setPresenceOfflineInDatabase(DatabaseHelper dbHelper) {
          SQLiteDatabase writableDatabase = dbHelper.getWritableDatabase();
          PresenceTable.setAllUsersOffline(writableDatabase);
          ContactSummaryTable.setOfflineStatus();
      }
  
      /**
       * Removes all presence infos besides those related to MeProfile
       * 
       * @param dbHelper
       */
      protected static void resetPresenceStatesAcceptForMe(long localContactIdOfMe,
              DatabaseHelper dbHelper) {
          SQLiteDatabase writableDb = dbHelper.getWritableDatabase();
          if (writableDb != null) {
              LogUtils.logW(" PresenceDBUtils.resetPresenceStatesAcceptForMe: "
                      + "#rows affected by delete method "
                      + PresenceTable.setAllUsersOfflineExceptForMe(localContactIdOfMe, writableDb));
              ContactSummaryTable.setOfflineStatusExceptForMe(localContactIdOfMe);
          }
      }
  }
  