/* vim: ts=2 sw=2 expandtab
 * BEGIN FLOCK GPL
 *
 * Copyright Flock Inc. 2005-2009
 * http://flock.com
 *
 * This file may be used under the terms of the
 * GNU General Public License Version 2 or later (the "GPL"),
 * http://www.gnu.org/licenses/gpl.html
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * END FLOCK GPL
 */

#include "flockPriorityQueue.h"
#include "nsArray.h"						// For the queue

NS_IMPL_ISUPPORTS1(flockPriorityQueue, flockIPriorityQueue)

NS_IMETHODIMP flockPQEnumerator::GetNext(nsISupports **_retval)
{
	
  return NS_OK;
}

NS_IMETHODIMP flockPQEnumerator::HasMoreElements(PRBool *_retval)
{
	*_retval = mFlockPriorityQueue->HasMoreElements();
  return NS_OK;
}


flockPriorityQueue::flockPriorityQueue()
{
  nsresult rv;
	rv = NS_NewArray(getter_AddRefs(mDataArray));
}

flockPriorityQueue::~flockPriorityQueue()
{
}

int flockPriorityQueue::compare(nsISupports *left, nsISupports *right)
{
	return 0;
}

void flockPriorityQueue::up(PRUint32 childIndex)
{
	nsCOMPtr<nsISupports> tmpChildElement;
	nsCOMPtr<nsISupports> tmpParentElement;
	PRUint32 parentIndex;
	
	tmpChildElement = do_QueryElementAt(mDataArray, childIndex);
	mDataArray->RemoveElementAt(childIndex);
	
	parentIndex = childIndex >> 1; // divide by 2
	while (parentIndex >= 1)
	{
    tmpParentElement = do_QueryElementAt(mDataArray, parentIndex);
	  if (compare(tmpParentElement, tmpChildElement) < 0)
	  {
	    // Move the element at parentIndex to the childIndex
	    mDataArray->RemoveElementAt(parentIndex);
	    mDataArray->InsertElementAt(tmpParentElement, childIndex, PR_FALSE);
	    
	    childIndex = parentIndex;
	    parentIndex = childIndex >> 1;
	  } else {
	    break;
	  }
	}
	mDataArray->InsertElementAt(tmpChildElement, childIndex, PR_FALSE);
}

void flockPriorityQueue::down(PRUint32 parentIndex)
{
	nsCOMPtr<nsISupports> tmpChildElement;
	nsCOMPtr<nsISupports> tmpNextChildElement;
	nsCOMPtr<nsISupports> tmpParentElement;
	PRUint32 childIndex;
  PRUint32 lastElementPos;
  mDataArray->GetLength(&lastElementPos);  // The size of this array DOES NOT CHANGE.

  tmpParentElement = do_QueryElementAt(mDataArray, parentIndex);
  mDataArray->RemoveElementAt(parentIndex);

  childIndex = parentIndex << 1; // multiply by 2
  while (childIndex <= lastElementPos)
  {
    tmpChildElement = do_QueryElementAt(mDataArray, childIndex);
		tmpNextChildElement = do_QueryElementAt(mDataArray, childIndex + 1);
		
    if ( (childIndex < lastElementPos) &&
         (compare(tmpNextChildElement, tmpChildElement) > 0) )
    {
      childIndex++;
      tmpChildElement = tmpNextChildElement;
    }
  
    if (compare(tmpParentElement, tmpChildElement) < 0)
    {
      // Move the element at parentIndex to the childIndex
      mDataArray->RemoveElementAt(childIndex);
      mDataArray->InsertElementAt(tmpParentElement, parentIndex, PR_FALSE);
    
      parentIndex = childIndex;
      childIndex = parentIndex << 1;
    } else {
      break;
    }
	}
	mDataArray->InsertElementAt(tmpParentElement, parentIndex, PR_FALSE);	
}

NS_IMETHODIMP flockPriorityQueue::Push(nsISupports *element)
{
  // Push this element on to the front of the array (FIFO)
  PRUint32 lastElementPos;

  mDataArray->InsertElementAt(element, 0, PR_FALSE);
  mDataArray->GetLength(&lastElementPos);
  up(lastElementPos);

  return NS_OK;
}

NS_IMETHODIMP flockPriorityQueue::Pop(nsISupports **_retval)
{
  nsCOMPtr<nsISupports> tmpChildElement;
  nsCOMPtr<nsISupports>  tmpParentElement;
  PRUint32 lastElementPos;

  // Extract the first element (POP)
  tmpParentElement = do_QueryElementAt(mDataArray, 0);
  mDataArray->RemoveElementAt(0);
  
  // Move the last element to the first position
  mDataArray->GetLength(&lastElementPos);
  tmpChildElement = do_QueryElementAt(mDataArray, lastElementPos);
  mDataArray->RemoveElementAt(lastElementPos);
  mDataArray->InsertElementAt(tmpChildElement, 0, PR_FALSE);
  
  down(0);
  
  *_retval = tmpParentElement;
  NS_ADDREF(*_retval);
  return NS_OK;
}

PRBool flockPriorityQueue::HasMoreElements()
{
	if (mDataArray)
	{
		PRUint32 length;
		mDataArray->GetLength(&length);
		return (length > 0);
	}
	return PR_FALSE;
}


nsresult FLOCK_NewPriorityQueue(flockIPriorityQueue **aResult)
{
	flockPriorityQueue* nPriorityQueue = new flockPriorityQueue();
	if (!nPriorityQueue) return NS_ERROR_OUT_OF_MEMORY;
	     
	*aResult = NS_STATIC_CAST(flockIPriorityQueue*, nPriorityQueue);
	NS_ADDREF(*aResult);
	 
	return NS_OK;	
}

