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;
  
  import java.util.List;
  import java.util.concurrent.ConcurrentLinkedQueue;
  
  import android.os.Bundle;
  
  import com.vodafone360.people.datatypes.BaseDataType;
  import com.vodafone360.people.datatypes.ServerError;
  import com.vodafone360.people.engine.EngineManager.EngineId;
  import com.vodafone360.people.service.ServiceStatus;
  import com.vodafone360.people.service.ServiceUiRequest;
  import com.vodafone360.people.service.agent.UiAgent;
  import com.vodafone360.people.service.io.ResponseQueue;
  import com.vodafone360.people.utils.LogUtils;
  
  /**
   * Base-class for all Engines implemented by the People Client.
   */
  public abstract class BaseEngine {
  
      /**
       * All engines must set this field to a unique ID
       */
      protected EngineId mEngineId = EngineId.UNDEFINED;
  
      /**
       * Callback provided by {@link EngineManager}
       */
      protected IEngineEventCallback mEventCallback;
  
      /**
       * Current UI Request if one is active, otherwise null.
       * 
       * @see ServiceUiRequest
       */
      protected ServiceUiRequest mActiveUiRequest;
  
      /**
       * Current timeout based on current time in milliseconds if one is pending,
       * otherwise null.
       */
      protected volatile Long mCurrentTimeout;
  
      /**
       * true if a Comms response is waiting in the comms response queue for
       * processing, false otherwise.
       */
      private volatile boolean mCommsResponseOutstanding = false;
  
      /**
       * Set by the {@link #setReqId(int)} function to store the request ID when a
       * engine makes a request to the server. All responses received will then be
       * filtered by this value automatically inside the
       * {@link #processCommsInQueue()} function.
       */
      private Integer mActiveRequestId;
  
      /**
       * true if a UI request is waiting in the UI request queue for processing,
       * false otherwise.
       */
      private boolean mUiRequestOutstanding;
  
      /**
       * true if the engine is deactivated (for test purposes), false otherwise.
       */
      private boolean mDeactivated;
  
      /**
       * Class to encapsulate client (UI) request information.
       */
      private static class UiQueueItem {
          /**
           * Request type.
           */
          private ServiceUiRequest mRequestId;
  
          /**
           * Data associated with the request.
           */
          private Object mData;
  
          @Override
          public String toString() {
              return "UiQueueItem [mData=" + mData + ", mRequestId=" + mRequestId + "]";
          }
  
          @Override
          public int hashCode() {
              final int prime = 31;
              int result = 1;
              result = prime * result + ((mData == null) ? 0 : mData.hashCode());
              result = prime * result + ((mRequestId == null) ? 0 : mRequestId.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;
              UiQueueItem other = (UiQueueItem)obj;
              if (mData == null) {
                  if (other.mData != null)
                      return false;
              } else if (!mData.equals(other.mData))
                  return false;
              if (mRequestId == null) {
                  if (other.mRequestId != null)
                      return false;
              } else if (!mRequestId.equals(other.mRequestId))
                  return false;
              return true;
          }
      }
  
      /**
       * Used to implement the UI request queue.
       */
      private final ConcurrentLinkedQueue<UiQueueItem> mUiQueue = new ConcurrentLinkedQueue<UiQueueItem>();
  
      /**
       * Public constructor.
       * 
       * @param eventCallback This interface must be implemented by the engine
       *            client
       */
      public BaseEngine(IEngineEventCallback eventCallback) {
          mEventCallback = eventCallback;
          mDeactivated = false;
      }
  
      /**
       * Return the absolute time in milliseconds when the engine needs to run
       * (based on System.currentTimeMillis). (Maybe add currentTime in the future
       * to use as the current time, to enable JUnit tests to validate timeout
       * functionality).
       * 
       * @return -1 never needs to run, 0 needs to run as soon as possible,
       *         CurrentTime + 60000 to run in 1 minute, etc.
       */
      public abstract long getNextRunTime();
  
      /**
       * Do some work but anything that takes longer than 1 second must be broken
       * up. (Maybe add currentTime as parameter, to enable JUnit tests to
       * validate timeout functionality)
       */
      public abstract void run();
  
      /**
       * This will be called immediately after creation.
       */
      public abstract void onCreate();
  
      /**
       * This will be called just before the engine is shutdown.
       */
      public abstract void onDestroy();
  
      /**
       * Helper function for use by the derived engine class to add a UI request
       * to the queue. This will be run from the UI thread
       * @param request - {@link ServiceUiRequest}.
       * @param data - Object containing data related to the request.
       */
      public void addUiRequestToQueue(ServiceUiRequest request, Object data) {
          addUiRequestToQueue(request, data, false);
      }
  
      /**
       * Helper function for use by the derived engine class to add a UI request
       * to the queue. This will be run from the UI thread
       * @param request - {@link ServiceUiRequest}.
       * @param data - Object containing data related to the request.
       * @param throttle - boolean, pass TRUE if the engine should ignore the incoming ServiceUiRequests
       * when the request queue already contains requests of the same type waiting to be processed.
       */
      public void addUiRequestToQueue(ServiceUiRequest request, Object data, boolean throttle) {
          if (mDeactivated) {
              onUiRequestComplete(request, ServiceStatus.ERROR_NOT_IMPLEMENTED, null);
              return;
          }
          UiQueueItem item = new UiQueueItem();
          item.mRequestId = request;
          item.mData = data;
          synchronized (mUiQueue) {
              if (!throttle || !mUiQueue.contains(item)) {
                  mUiQueue.add(item);  
                  LogUtils.logD("Added to the queue, ServiceUiRequest."+request);
              } 
              mUiRequestOutstanding = true;
          }
          mEventCallback.kickWorkerThread();
      }
      /**
       * Return id for this engine.
       * 
       * @return EngineId identifying concrete engine
       */
      public EngineId engineId() {
          return mEngineId;
      }
  
      /**
       * Helper function for use by the derived engine class to fetch the next UI
       * request from the queue. Returns null if the queue is empty.
       */
      private UiQueueItem fetchNextUiRequest() {
          UiQueueItem item = null;
          synchronized (mUiQueue) {
              item = mUiQueue.poll();
              if (mUiQueue.isEmpty()) {
                  mUiRequestOutstanding = false;
              }
          }
          return item;
      }
  
      /**
       * Helper function to determine if there is any work outstanding in the UI
       * request queue. Returns false if the queue is empty.
       */
      protected boolean isUiRequestOutstanding() {
          synchronized (mUiQueue) {
              // mActiveUiRequest must not be null if its not
              // and there is more than one request in a queue
              // engine might go into endless loop
              if (mActiveUiRequest != null) {
                  return false;
              }
              return mUiRequestOutstanding;
          }
      }
  
      /**
       * Helper function which must be called to complete a UI request. Normally
       * this will not be called directly by the derived engine implementation.
       * Instead the completeUiRequest function should be used.
       * 
       * @param request The request Id to complete
       * @param status The ServiceStatus code
       * @param data Response data (object type is request specific)
       */
      private void onUiRequestComplete(ServiceUiRequest request, ServiceStatus status, Object data) {
          mEventCallback.onUiEvent(ServiceUiRequest.UI_REQUEST_COMPLETE, request.ordinal(), status
                  .ordinal(), data);
      }
  
      /**
       * The derived engine implementation must call the processCommsInQueue()
       * function (normally from within the run() implementation), otherwise this
       * will not be called. This function is called for each Comms response that
       * arrives on the in queue.
       * 
       * @param resp The comms response
       */
      protected abstract void processCommsResponse(ResponseQueue.DecodedResponse resp);
  
      /**
       * The derived engine implementation must call the processUiQueue() function
       * (normally from within the run() implementation), otherwise this will not
       * be called. This function is called for each UI request that arrives on
       * the queue. It should start processing the request. If this function takes
       * longer than 1 second to complete, it should be broken up. Once a request
       * is finished the processUiRequest function must be called.
       * 
       * @param requestId The UI request ID
       * @param data Request data (object type is request specific)
       */
      protected abstract void processUiRequest(ServiceUiRequest requestId, Object data);
  
      /**
       * The derived engine implementation must call the processTimeout() function
       * (normally from within the run() implementation), otherwise this will not
       * be called. This function will be called when a timeout occurs (started by
       * setTimeout and cancelled by clearTimeout). If this function takes longer
       * than 1 second to complete it should be broken up.
       */
      protected abstract void onTimeoutEvent();
  
      /**
       * Called by the EngineManager when a comms response is received. Will set
       * the response outstanding flag and kick the worker thread.
       */
      public void onCommsInMessage() {
      	mCommsResponseOutstanding = true;
          mEventCallback.kickWorkerThread();
      }
  
      /**
       * Should be called by the getNextRunTime() and run() functions to check if
       * there are any comms responses waiting to be processed.
       * 
       * @return true if there are 1 or more responses to process.
       */
      protected boolean isCommsResponseOutstanding() {
      	return mCommsResponseOutstanding;
      }
  
      /**
       * Should be called by the run() function to process the comms in queue.
       * Calling this function will result in the processCommsResponse function
       * being called once. The derived engine implementation should do its
       * processing of each response in that function. If the engine set the
       * request ID using the setReqId function then messages which don't match
       * will be taken off the queue and deleted.
       * 
       * @return true if a response was taken from the queue and processed.
       */
      protected boolean processCommsInQueue() {
          final ResponseQueue queue = ResponseQueue.getInstance();
          if (queue != null) {
              final ResponseQueue.DecodedResponse resp = queue.getNextResponse(mEngineId);
              if (resp == null) {
                  mCommsResponseOutstanding = false;
                  return false;
              }
              boolean processResponse = false;
              if (resp.mReqId == null || mActiveRequestId == null) {
                  processResponse = true;
              } else if (mActiveRequestId.equals(resp.mReqId)) {
                  mActiveRequestId = null;
                  processResponse = true;
              }
              if (processResponse) {
                  processCommsResponse(resp);
              }
              return processResponse;
          } else {
              throw new RuntimeException(
                      "BaseEngine.processCommsInQueue - ResponseQueue cannot be null");
          }
      }
  
      /**
       * A helper function for the derived engine implementation to use. It checks
       * the returned comms response data against the expected type and handles
       * all common error cases.
       * 
       * @param requiredResp The expected type
       * @param data The data received from the comms response
       * @return SUCCESS if the first element in the list is of the expected type,
       *         ERROR_COMMS if the first element in the list is an unexpected
       *         type, ERROR_COMMS_BAD_RESPONSE if data is not valid for specyfied
       *         type or null otherwise if the data is of type ZError, a suitable
       *         error code.
       */
      public static ServiceStatus getResponseStatus(int requiredResponseType,
              List<BaseDataType> data) {
          ServiceStatus errorStatus = ServiceStatus.ERROR_COMMS;
          if (data != null) {
              if (data.size() == 0 || data.get(0).getType() == requiredResponseType) {
                  if (requiredResponseType == BaseDataType.CONTACT_CHANGES_DATA_TYPE && 
                          data.size() == 0) {
                      errorStatus = ServiceStatus.ERROR_COMMS_BAD_RESPONSE;
                  } else {
                      errorStatus = ServiceStatus.SUCCESS;
                  }
              } else if (data.get(0).getType() == BaseDataType.SERVER_ERROR_DATA_TYPE) {
                  final ServerError error = (ServerError)data.get(0);
                  LogUtils.logE("Server error: " + error);
                  errorStatus = error.toServiceStatus();
              } else {
                  LogUtils.logD(
                          "BaseEngine.genericHandleResponse: Unexpected type [" + requiredResponseType
                          + "] but received [" + data.get(0).getType() + "]");
              }
          } else {
              errorStatus = ServiceStatus.ERROR_COMMS_BAD_RESPONSE;
          }
          return errorStatus;
      }
  
      /**
       * Should be called by the run() function to process the UI request queue.
       * Calling this function will result in the processUiRequest function being
       * called once and this will be set to the active request. The derived
       * engine implementation should do its processing of each request in that
       * function. Note the engine must not process any more requests until the
       * current one has been completed.
       * 
       * @return true if a response was taken from the queue and processed.
       */
      protected boolean processUiQueue() {
          if (mActiveUiRequest != null) {
              return false;
          }
          final UiQueueItem uiItem = fetchNextUiRequest();
          if (uiItem != null) {
              mActiveUiRequest = uiItem.mRequestId;
              processUiRequest(uiItem.mRequestId, uiItem.mData);
              return true;
          }
          return false;
      }
  
      /**
       * A helper function that can be called by the derived engine implementation
       * to complete the current UI request.
       * 
       * @param status The result of the request
       */
      protected void completeUiRequest(ServiceStatus status) {
          completeUiRequest(status, null);
      }
  
      /**
       * This function must be implemented in the derived engine implementation.
       * It can do any post-request complete cleanup.
       */
      protected abstract void onRequestComplete();
  
      /**
       * A helper function that can be called by the derived engine implementation
       * to complete the current UI request.
       * 
       * @param status The result of the request
       * @param data Response data (object type is request specific)
       */
      protected void completeUiRequest(ServiceStatus status, Object data) {
          onRequestComplete();
          if (mActiveUiRequest != null) {
              onUiRequestComplete(mActiveUiRequest, status, data);
              mActiveUiRequest = null;
          }
      }
  
      /**
       * This method was added to provide a the engine a way to notify UI of
       * unsolicited incoming messages/errors
       * 
       * @param request - the result of the request
       * @param status - SUCCESS or ERROR
       * @param data Response data (object type is request specific)
       */
      /*
       * protected void completeUnsolicitedUiRequest(ServiceUiRequest request,
       * ServiceStatus status, Object data) { onRequestComplete();
       * onUiRequestComplete(request, status, data); mActiveUiRequest = null; //
       * TODO: check if we need to set it null }
       */
  
      /**
       * Helper function that can be called by the derived engine implementation
       * to start an asynchronous timer. This will only work when: 1)
       * getCurrentTimeout() is called inside the getNextRunTime() function. 2)
       * The processTimeout() is called in the run() function 3) The
       * onTimeoutEvent() is implemented to handle the timeout event.
       * 
       * @param timeoutVal The timeout value (in milliseconds)
       */
      protected void setTimeout(long timeoutVal) {
          mCurrentTimeout = System.currentTimeMillis() + timeoutVal;
      }
  
      /**
       * Cancels the current timer (has no effect if the timer was not active).
       */
      protected void clearTimeout() {
          mCurrentTimeout = null;
      }
  
      /**
       * The result of this function must be returned by getNextRunTime() instead
       * of -1 for the timer to work.
       * 
       * @return The required next run time (in milliseconds) or -1 if no timer is
       *         active
       */
      protected long getCurrentTimeout() {
          if (mCurrentTimeout != null) {
              return mCurrentTimeout;
          }
          return -1;
      }
  
      /**
       * This function must be called by run() in the derived engine
       * implementation for the timer to work.
       * 
       * @return true if the timeout was processed
       */
      protected boolean processTimeout() {
          if (mCurrentTimeout != null) {
              long currentTimeMs = System.currentTimeMillis();
              if (currentTimeMs >= mCurrentTimeout) {
                  mCurrentTimeout = null;
                  onTimeoutEvent();
                  return true;
              }
          }
          return false;
      }
  
      /**
       * Called by the framework to deactivate the engine. This allows engines to
       * be deactivated by modifying the Settings (so features can be disabled in
       * the build).
       */
      protected void deactivateEngine() {
          mDeactivated = true;
      }
  
      /**
       * Called by the framework to determine if the engine should run.
       */
      public boolean isDeactivated() {
          return mDeactivated;
      }
  
      /**
       * Called by engines when an API call is made to ensure that the response
       * processed matches the request. If this function is not called the
       * framework will send all responses to the engine.
       */
      protected boolean setReqId(int reqId) {
          if (reqId == -1) {
              return false;
          }
          mActiveRequestId = reqId;
          return true;
      }
  
      /**
       * Called by engines in the UI thread to cancel all UI requests. The engine
       * should ensure that the active UI request is completed (if necessary)
       * before calling this function.
       */
      protected void emptyUiRequestQueue() {
          synchronized (mUiQueue) {
              mUiQueue.clear();
              mUiRequestOutstanding = false;
              mActiveUiRequest = null;
          }
      }
  
      /**
       * Engines can override this function to do any special handling when a reset is needed.
       * Note: if overriden, the engine shall call the super implementation when the reset is done.
       */
      public void onReset() {
          emptyUiRequestQueue();
          clearTimeout();
      }
      
      /**
       * This method fires states change of an engine to UI (between "busy" and
       * IDLE). This method is normally called after the new state has been
       * changed and published to ApplicationCache. The UI should refer to
       * ApplicationCache when processing this new ServiceRequest
       * 
       * @param request ServiceUIRequest UPDATING_UI or UPDATING_UI_FINISHED
       */
      public void fireNewState(ServiceUiRequest request, Bundle bundle) {
          UiAgent uiAgent = mEventCallback.getUiAgent();
          if (uiAgent != null && uiAgent.isSubscribed()) {
              uiAgent.sendUnsolicitedUiEvent(request, bundle);
          }
      }
  }
  