/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */

/*!
 * \file oscl_priqueue.cpp
 * \brief Implements a priority queue data structure similar to STL.
 *
 *
 */

#include "oscl_priqueue.h"


OSCL_EXPORT_REF void OsclPriorityQueueBase::push_heap(OsclAny* first, OsclAny* last)
{
    /*
    * The task of the push_heap function is to take the last
    * entry and continue to swap it with its parent until
    * the heap property is statisfied (i.e., all children are not
    * greater than the parent).  It is assumed that the new value
    * has already been pushed on the end of the container. The
    * first and last arguments should be the begin() and end() iterators
    * of the container.
    */

    int index = delta_T(first, last) - 1;
    int parent_index = (index - 1) / 2;
    while (index > 0 && pOpaqueType->compare_LT(pVec->increment_T(first, parent_index), pVec->increment_T(first, index)))
    {
        // swap the current index and the parent
        pOpaqueType->swap(pVec->increment_T(first, index), pVec->increment_T(first, parent_index));
        index = parent_index;
        parent_index = (index - 1) / 2;
    }
}

OSCL_EXPORT_REF void OsclPriorityQueueBase::pop_heap(OsclAny* first, OsclAny* last)
{

    /* This function works by swapping the first and last values in
    * the container and then pushes down the top element until the
    * parent-child relationship is satisfied (i.e., parent not less than
    * either of its children
    */

    // swap the first and last values
    pOpaqueType->swap(first, pVec->increment_T(last, -1));

    int index = 0;
    int child = 2 * index + 1;
    int new_last_index = delta_T(first, last) - 1;

    while (child < new_last_index)
    {
        if (((child + 1) < new_last_index)
                && (pOpaqueType->compare_LT(pVec->increment_T(first, child), pVec->increment_T(first, child + 1))))
        {
            // make certain the child index is ordered according the comparison
            child += 1;
        }

        if (pOpaqueType->compare_LT(pVec->increment_T(first, index), pVec->increment_T(first, child)))
        {
            // swap them
            pOpaqueType->swap(pVec->increment_T(first, index), pVec->increment_T(first, child));
            index = child;
            child = 2 * index + 1;
        }
        else
        {
            break;
        }
    }
}

OSCL_EXPORT_REF OsclAny* OsclPriorityQueueBase::find_heap(const OsclAny* input, OsclAny* first, OsclAny* last)
{
    /*
    * Find iterator pointing to the input element.
    * It is assumed that the input element is present.
    * If not present, it will return NULL.
    */
    for (OsclAny* pos = first; pos < last; pos = pVec->increment_T(pos, 1))
    {
        if (pOpaqueType->compare_EQ(pos, input))
            return pos;
    }
    return NULL;
}

//Remove an arbitrary element, by value.
//If there are multiple matches, this removes the first one it finds.
//Returns number of items removed(either 0 or 1).
OSCL_EXPORT_REF int OsclPriorityQueueBase::remove(const OsclAny* input)
{
    //First find the element to remove
    OsclAny* pos = find_heap(input, pVec->begin(), pVec->end());
    if (pos)
    {
        if (pVec->increment_T(pos, 1) == pVec->end())
        {
            // It's the last element-- just remove it without any re-ordering.
            pVec->pop_back();
        }
        else
        {
            // Move the element to the end & remove.
            pop_heap(pos, pVec->end());
            pVec->pop_back();
            // Re-order the front part of the queue.
            push_heap(pVec->begin(), pVec->increment_T(pos, 1));
        }
        return 1;
    }
    return 0;
}


