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.Enumeration;
  import java.util.Hashtable;
  import java.util.Iterator;
  
  import android.os.Parcel;
  import android.os.Parcelable;
  
  import com.vodafone360.people.Settings;
  import com.vodafone360.people.datatypes.ContactSummary;
  import com.vodafone360.people.datatypes.ContactSummary.OnlineStatus;
  import com.vodafone360.people.engine.presence.NetworkPresence.SocialNetwork;
  import com.vodafone360.people.utils.LogUtils;
  
  /**
   * User is a class encapsulating the information about a user's presence state.
   */
  public class User implements Parcelable {
  
      /**
       * This constant is to identify the key for the {@link SocialNetwork} name
       *  in the Bundle of UiEvent generated by presence status change.  
       */
      public static final String NETWORK = "network";
      /**
       * This constant is to identify the key for the {@link OnlineStatus} name
       *  in the Bundle of UiEvent generated by presence status change.  
       */
      public static final String STATUS = "status";
      /**
       * This constant is to identify the key for the source of status change delivered
       *  in the Bundle of UiEvent generated by presence status change.  
       */
      public static final String SOURCE = "source";
      /**
       * This constant is to identify the source of status change delivered
       *  in the Bundle of UiEvent generated by presence status change as "requested by client".  
       */
      public static final int SOURCE_REQUESTED = 0;
      /**
       * This constant is to identify the source of status change delivered
       *  in the Bundle of UiEvent generated by presence status change as "received by client".  
       */
      public static final int SOURCE_RECEIVED = 1;
      /**
       * This constant is to identify the source of status change delivered
       *  in the Bundle of UiEvent generated by presence status change as "timed out".  
       */
      public static final int SOURCE_TIMED_OUT = 2;
      
      private static final String COLUMNS = "::";
      private static final String PRESENCE_PC = "pc",
                                  PRESENCE_MOBILE = "mobile";
      
      /** Database ID of the contact (e.g. "userid@gmail.com"). **/
      private long mLocalContactId;
  
      /** Overall presence state displayed in the common contact list. **/
      private int mOverallOnline;
  
      /**
       * Communities presence status:
       * {google:online, pc:online, mobile:online}.
       */
      private ArrayList<NetworkPresence> mPayload;
  
      /**
       * Default Constructor.
       */
      public User() {
      }
  
      /**
       * Constructor.
       * 
       * @param userId - user id in the contact list, e.g.
       *            "google::userid@gmail.com" or "882339"
       * @param payload - communities presence status {google:online, pc:online,
       *            mobile:online}
       */
      public User(String userId, Hashtable<String, String> payload) {
          if (payload != null) {
              if (Settings.LOG_PRESENCE_PUSH_ON_LOGCAT) {
                  LogUtils.logWithName(LogUtils.PRESENCE_INFO_TAG, "user id:"+ userId + ", " + payload);
              }
              
              if (payload.containsKey(PRESENCE_PC)) {
                  if (Settings.LOG_PRESENCE_PUSH_ON_LOGCAT) {
              	    LogUtils.logWithName(LogUtils.PRESENCE_INFO_TAG, "removed 360PC presence for " + userId);
                  }
              	payload.remove(PRESENCE_PC);
              }
              if (payload.containsKey(PRESENCE_MOBILE)) {
                  if (Settings.LOG_PRESENCE_PUSH_ON_LOGCAT) {
                      LogUtils.logWithName(LogUtils.PRESENCE_INFO_TAG, "removed 360MOBILE presence for " + userId);    
                  }
              	payload.remove(PRESENCE_MOBILE);
              }
              
              mOverallOnline = isOverallOnline(payload);
              mPayload = createPayload(userId, payload);
          }
      }
  
      /**
       * This method returns the localContactId for this contact in DB across the
       * application .
       *
       * @return the localContactId for this contact in DB
       */
      public long getLocalContactId() {
          return mLocalContactId;
      }
  
      /**
       * This method sets localContactId for this User. The id comes from DB. 
       * @param mLocalContactId long id.
       */
      public void setLocalContactId(long mLocalContactId) {
          this.mLocalContactId = mLocalContactId;
      }
  
      /**
       * Returns communities presence status
       * 
       * @return communities presence status, e.g. {google:online, pc:online,
       *         mobile:online}
       */
      public ArrayList<NetworkPresence> getPayload() {
          return mPayload;
      }
  
      /**
       * The method returns the OnlineStatus of this user on the provided network
       * @param network SocialNetwork.
       * @return NULL if network is null, OnlineStatus.OFFLINE if the information
       * about presence state on this network is not available, and actual status in other cases.  
       */
      public OnlineStatus getStatusForNetwork(SocialNetwork network) {
          if (network == null) {
              return null;
          }
          OnlineStatus os = OnlineStatus.OFFLINE;
          if (mPayload != null) {
              for (NetworkPresence np : mPayload) {
                  if (np.getNetworkId() == network.ordinal()) {
                      os = OnlineStatus.getValue(np.getOnlineStatusId());
                      break;
                  }
              }
          }
          return os;
      }
  
      /**
       * Returns communities presence status
       * 
       * @return communities presence status, e.g. {google:online, pc:online,
       *         mobile:online}
       */
      public void setPayload(ArrayList<NetworkPresence> payload) {
          mPayload = payload;
      }
  
      /**
       * Returns the overall user presence status
       * 
       * @return true if user is online at least at one community, e.g. true if
       *         {google:offline, pc:offline, mobile:online}
       */
      private int isOverallOnline(Hashtable<String, String> payload) {
          if (payload != null) {
              if (payload.values().contains(ContactSummary.OnlineStatus.ONLINE.toString()))
                  return ContactSummary.OnlineStatus.ONLINE.ordinal();
              if (payload.values().contains(ContactSummary.OnlineStatus.INVISIBLE.toString()))
                  return ContactSummary.OnlineStatus.INVISIBLE.ordinal();
              if (payload.values().contains(ContactSummary.OnlineStatus.IDLE.toString()))
                  return ContactSummary.OnlineStatus.IDLE.ordinal();
          }
  
          return ContactSummary.OnlineStatus.OFFLINE.ordinal();
      }
  
      /**
       * Returns the overall user presence status: in fact the one from the below
       * status states first encountered for all known user accounts next:
       * INVISIBLE, ONLINE, IDLE, OFFLINE
       * 
       * @return presence state
       */
      public int isOnline() {
          return mOverallOnline;
      }
  
      /**
       * @param payload
       * @return
       */
      private ArrayList<NetworkPresence> createPayload(String userId,
              Hashtable<String, String> payload) {
          ArrayList<NetworkPresence> presenceList = new ArrayList<NetworkPresence>(payload.size());
          String parsedUserId = parseUserName(userId);
          String key = null;
          SocialNetwork network = null;
          String value = null;
          OnlineStatus status = null;
          for (Enumeration<String> en = payload.keys(); en.hasMoreElements();) {
              key = en.nextElement();
              network = SocialNetwork.getValue(key);
              if (network != null) {
                  int keyIdx = network.ordinal();
                  value = payload.get(key);
                  if (value != null) {
                      status = OnlineStatus.getValue(value);
                      if (status != null) {
                          int valueIdx = status.ordinal();
                          presenceList.add(new NetworkPresence(parsedUserId, keyIdx, valueIdx));
                      }
                  }
              }
          }
          return presenceList;
      }
  
      /**
       * @param user
       * @return
       */
      private static String parseUserName(String userId) {
          if (userId != null) {
              int columnsIndex = userId.indexOf(COLUMNS);
              if (columnsIndex > -1) {
                  return userId.substring(columnsIndex + COLUMNS.length());
              } else {
                  return userId;
              }
          }
          return null;
      }
  
      @Override
      public int hashCode() {
          final int prime = 31;
          int result = 1;
          result = prime * result + mOverallOnline;
          result = prime * result + ((mPayload == null) ? 0 : mPayload.hashCode());
          return result;
      }
  
      @Override
      public boolean equals(Object obj) {
          if (this == obj)
              return true;
          if (obj == null)
              return false;
          if (getClass() != obj.getClass())
              return false;
          User other = (User)obj;
          if (mOverallOnline != other.mOverallOnline)
              return false;
          if (mPayload == null) {
              if (other.mPayload != null)
                  return false;
          } else if (!mPayload.equals(other.mPayload))
              return false;
          return true;
      }
  
      @Override
      public String toString() {
          final StringBuffer sb = new StringBuffer("User [mLocalContactId=");
          sb.append(mLocalContactId);
          sb.append(", mOverallOnline="); sb.append(mOverallOnline);
          sb.append(", mPayload="); sb.append(mPayload); sb.append("]");
          return sb.toString();
      }
  
      /**
       * This method sets the overall user presence status,
       * @parameter the online status id - the ordinal, see @OnlineStatus  
       */
      public void setOverallOnline(int overallOnline) {
          this.mOverallOnline = overallOnline;
      }
  
      /**
       * This method removes the network presence information with the given presence id from the User.
       * @param ordinal - the network id, ordinal in @see SocialNetworks
       */
      public void removeNetwork(int ordinal) {
          Iterator<NetworkPresence> itr = mPayload.iterator();
          NetworkPresence presence = null;
          while (itr.hasNext()) {
              presence = itr.next();
              if (presence.getNetworkId() == ordinal) {
                  itr.remove();
                  break;
              }
          }
      }
  
      @Override
      public final int describeContents() {
          return 0;
      }
  
      @Override
      public final void writeToParcel(final Parcel dest, final int flags) {
          dest.writeLong(mLocalContactId);
          dest.writeInt(mOverallOnline);
  
          writePayloadToParcel(dest, flags);
      }
  
      /***
       * Parcelable constructor for User.
       *
       * @param source User Parcel.
       */
      private void readFromParcel(final Parcel source) {
          mLocalContactId = source.readLong();
          mOverallOnline = source.readInt();
          readPayloadFromParcel(source);
      }
  
      /**
       * Helper function to get the payload into & out of a parcel.
       * Keeping this code in a helper function should help maintenance.
       * Doing it this way seems to be the only way to avoid constant
       * ClassNotFoundExceptions even with proper classloaders in place.
       *
       * @param dest User Parcel.
       * @param flags Flags.
       */
      public final void writePayloadToParcel(final Parcel dest, final int flags) {
          // Locals more efficient than members on Dalvik VM
          ArrayList<NetworkPresence> payload = mPayload;
          dest.writeInt(payload.size());
          for (NetworkPresence netPres : payload) {
              netPres.writeToParcel(dest, flags);
          }
      }
  
      /**
       * Helper function to get the payload into & out of a parcel.
       * Keeping this code in a helper function should help maintenance.
       * Doing it this way seems to be the only way to avoid constant
       * ClassNotFoundExceptions even with proper classloaders in place.
       *
       * @param source User Parcel.
       */
      private void readPayloadFromParcel(final Parcel source) {
          // Could do this directly into mPayload but locals are more efficient
          ArrayList<NetworkPresence> payload = new ArrayList<NetworkPresence>();
          for (int i = 0; i < source.readInt(); i++) {
              payload.add(new NetworkPresence(source));
          }
  
          mPayload = payload;
      }
  
      /***
       * Parcelable.Creator for User.
       */
      public static final Parcelable.Creator<User> CREATOR
          = new Parcelable.Creator<User>() {
  
          @Override
          public User createFromParcel(final Parcel source) {
              User newUser = new User();
              newUser.readFromParcel(source);
              return newUser;
          }
  
          @Override
          public User[] newArray(final int size) {
              return new User[size];
          }
      };
  }
  