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.content;
  
  import java.util.Hashtable;
  import java.util.List;
  
  import com.vodafone360.people.database.DatabaseHelper;
  import com.vodafone360.people.datatypes.BaseDataType;
  import com.vodafone360.people.datatypes.ExternalResponseObject;
  import com.vodafone360.people.engine.BaseEngine;
  import com.vodafone360.people.engine.IEngineEventCallback;
  import com.vodafone360.people.engine.EngineManager.EngineId;
  import com.vodafone360.people.service.ServiceUiRequest;
  import com.vodafone360.people.service.io.QueueManager;
  import com.vodafone360.people.service.io.Request;
  import com.vodafone360.people.service.io.ResponseQueue.DecodedResponse;
  
  /**
   * Content engine for downloading and uploading all kind of content (pictures,
   * videos, files)
   */
  public class ContentEngine extends BaseEngine {
      
      /**
       * The amount of time in milliseconds that the engine is allowed to run.
       */
      private final static int ALLOWED_RUNNING_TIME_MS = 150;
  
      /**
       * Constructor for the ContentEngine.
       * 
       * @param eventCallback IEngineEventCallback for calling the constructor of
       *            the super class
       * @param dbHelper Instance of DatabaseHelper
       */
      public ContentEngine(final IEngineEventCallback eventCallback, final DatabaseHelper dbHelper) {
          super(eventCallback);
          this.mDbHelper = dbHelper;
          mEngineId = EngineId.CONTENT_ENGINE;
      }
  
      /**
       * Queue with unprocessed ContentObjects.
       */
      private FiFoQueue mUnprocessedQueue = new FiFoQueue();
  
      /**
       * Queue with ContentObjects for downloads.
       */
      private FiFoQueue mDownloadQueue = new FiFoQueue();
  
      /**
       * Queue with ContentObjects for uploads.
       */
      private FiFoQueue mUploadQueue = new FiFoQueue();
  
      /**
       * Instance of DatabaseHelper.
       */
      private DatabaseHelper mDbHelper;
  
      /**
       * Hashtable to match requests to ContentObjects.
       */
      private Hashtable<Integer, ContentObject> requestContentObjectMatchTable = new Hashtable<Integer, ContentObject>();
  
      /**
       * Getter for the local instance of DatabaseHelper.
       * 
       * @return local instance of DatabaseHelper
       */
      public final DatabaseHelper getDatabaseHelper() {
          return mDbHelper;
      }
  
      /**
       * Processes one ContentObject.
       * 
       * @param co ContentObject to be processed
       */
      public final void processContentObject(final ContentObject co) {
          mUnprocessedQueue.add(co);
      }
  
      /**
       * Iterates over the ContentObject list and processes every element.
       * 
       * @param list List with ContentObjects which are to be processed
       */
      public final void processContentObjects(final List<ContentObject> list) {
          for (ContentObject co : list) {
              processContentObject(co);
          }
      }
  
      /**
       * Processes the main queue and splits it into the download and upload
       * queues.
       */
      private void processQueue() {
          ContentObject co;
          // picking unprocessed ContentObjects
          while ((co = mUnprocessedQueue.poll()) != null) {
              // putting them to downloadqueue ...
              if (co.getDirection() == ContentObject.TransferDirection.DOWNLOAD) {
                  mDownloadQueue.add(co);
              } else {
                  // ... or the uploadqueue
                  mUploadQueue.add(co);
              }
          }
      }
  
      /**
       * Determines the next RunTime of this Engine It first processes the
       * in-queue and then look.
       * 
       * @return time in milliseconds from now when the engine should be run
       */
      @Override
      public final long getNextRunTime() {
          processQueue();
          // if there are CommsResponses outstanding, run now
          if (isCommsResponseOutstanding()) {
              return 0;
          }
          return (mDownloadQueue.size() + mUploadQueue.size() > 0) ? 0 : -1;
      }
  
      /**
       * Empty implementation without function at the moment.
       */
      @Override
      public void onCreate() {
      }
  
      /**
       * Empty implementation without function at the moment.
       */
      @Override
      public void onDestroy() {
      }
  
      /**
       * Empty implementation without function at the moment.
       */
      @Override
      protected void onRequestComplete() {
      }
  
      /**
       * Empty implementation without function at the moment.
       */
      @Override
      protected void onTimeoutEvent() {
      }
  
      /**
       * Processes the response Finds the matching contentobject for the repsonse
       * using the id of the response and sets its status to done. At last the
       * TransferComplete method of the ContentObject is called.
       * 
       * @param resp Response object that has been processed
       */
      @Override
      protected final void processCommsResponse(final DecodedResponse resp) {
          ContentObject co = requestContentObjectMatchTable.remove(resp.mReqId);
  
          if (co == null) { // check if we have an invalid response
              return;
          }
          
          List<BaseDataType> mDataTypes = resp.mDataTypes;
          // Sometimes it is null or empty
          if (mDataTypes == null || mDataTypes.size()==0) {
              co.setTransferStatus(ContentObject.TransferStatus.ERROR);
              RuntimeException exc = new RuntimeException("Empty response returned");
              co.getTransferListener().transferError(co, exc);
              return;
          }
  
          Object data = mDataTypes.get(0);
          if (mDataTypes.get(0).getType() == BaseDataType.SERVER_ERROR_DATA_TYPE
                  || mDataTypes.get(0).getType() == BaseDataType.SYSTEM_NOTIFICATION_DATA_TYPE) {
              co.setTransferStatus(ContentObject.TransferStatus.ERROR);
              RuntimeException exc = new RuntimeException(data.toString());
              co.getTransferListener().transferError(co, exc);
          } else {
              co.setTransferStatus(ContentObject.TransferStatus.DONE);
              co.setExtResponse((ExternalResponseObject) data);
              co.getTransferListener().transferComplete(co);
          }
      }
  
      /**
       * Empty implementation of abstract method from BaseEngine.
       */
      @Override
      protected void processUiRequest(final ServiceUiRequest requestId, final Object data) {
  
      }
  
      /**
       * run method of this engine iterates over the downloadqueue, makes requests
       * out of ContentObjects and puts them into QueueManager queue.
       */
      @Override
      public final void run() {
  
          // set it to true so at least one response is treated per call to run()
          boolean carryOn = true;
          final long runStartTime = System.currentTimeMillis();
  
          while (isCommsResponseOutstanding() && carryOn) {
              // process as many responses as we can during the allowed time slot
              processCommsInQueue();
              carryOn = System.currentTimeMillis() - runStartTime < ALLOWED_RUNNING_TIME_MS;
          }
  
          // do not send more requests if we are still struggling with
          // outstanding responses
          if (isCommsResponseOutstanding()) return;
          
          ContentObject co;
          boolean queueChanged = false;
          
          while ((co = mDownloadQueue.poll()) != null) {
              queueChanged = true;
              // set the status of this contentobject to transferring
              co.setTransferStatus(ContentObject.TransferStatus.TRANSFERRING);
              Request request = new Request(co.getUrl().toString(), co.getUrlParams(), engineId());
              QueueManager.getInstance().addRequest(request);
              // important: later we will match done requests back to the
              // contentobject using this map
              requestContentObjectMatchTable.put(request.getRequestId(), co);
          }
          if (queueChanged) {
              QueueManager.getInstance().fireQueueStateChanged();
          }
      }
  
      @Override
      public void onReset() {
  
          super.onReset();
          requestContentObjectMatchTable.clear();
          mUnprocessedQueue.clear();
          mDownloadQueue.clear();
          mUploadQueue.clear();
      }
  }
  