/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "list_wrapper.h"

#include "critical_section_wrapper.h"
#include "trace.h"

namespace webrtc {
ListItem::ListItem(const void* item)
    : next_(0),
      prev_(0),
      item_ptr_(item),
      item_(0)
{
}

ListItem::ListItem(const unsigned int item)
    : next_(0),
      prev_(0),
      item_ptr_(0),
      item_(item)
{
}

ListItem::~ListItem()
{
}

void* ListItem::GetItem() const
{
    return const_cast<void*>(item_ptr_);
}

unsigned int ListItem::GetUnsignedItem() const
{
    return item_;
}

ListWrapper::ListWrapper()
    : critical_section_(CriticalSectionWrapper::CreateCriticalSection()),
      first_(0),
      last_(0),
      size_(0)
{
}

ListWrapper::~ListWrapper()
{
    if (!Empty())
    {
        // TODO (hellner) I'm not sure this loggin is useful.
        WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1,
                   "Potential memory leak in ListWrapper");
        // Remove all remaining list items.
        while (Erase(First()) == 0)
        {}
    }
    delete critical_section_;
}

bool ListWrapper::Empty() const
{
    return !first_ && !last_;
}

unsigned int ListWrapper::GetSize() const
{
    return size_;
}

int ListWrapper::PushBack(const void* ptr)
{
    ListItem* item = new ListItem(ptr);
    CriticalSectionScoped lock(critical_section_);
    PushBackImpl(item);
    return 0;
}

int ListWrapper::PushBack(const unsigned int item_id)
{
    ListItem* item = new ListItem(item_id);
    CriticalSectionScoped lock(critical_section_);
    PushBackImpl(item);
    return 0;
}

int ListWrapper::PushFront(const unsigned int item_id)
{
    ListItem* item = new ListItem(item_id);
    CriticalSectionScoped lock(critical_section_);
    PushFrontImpl(item);
    return 0;
}

int ListWrapper::PushFront(const void* ptr)
{
    ListItem* item = new ListItem(ptr);
    CriticalSectionScoped lock(critical_section_);
    PushFrontImpl(item);
    return 0;
}

int ListWrapper::PopFront()
{
    return Erase(first_);
}

int ListWrapper::PopBack()
{
    return Erase(last_);
}

ListItem* ListWrapper::First() const
{
    return first_;
}

ListItem* ListWrapper::Last() const
{
    return last_;
}

ListItem* ListWrapper::Next(ListItem* item) const
{
    if(!item)
    {
        return 0;
    }
    return item->next_;
}

ListItem* ListWrapper::Previous(ListItem* item) const
{
    if (!item)
    {
        return 0;
    }
    return item->prev_;
}

int ListWrapper::Insert(ListItem* existing_previous_item, ListItem* new_item)
{
    if (!new_item)
    {
        return -1;
    }
    // Allow existing_previous_item to be NULL if the list is empty.
    // TODO (hellner) why allow this? Keep it as is for now to avoid
    // breaking API contract.
    if (!existing_previous_item && !Empty())
    {
        return -1;
    }
    CriticalSectionScoped lock(critical_section_);
    if (!existing_previous_item)
    {
        PushBackImpl(new_item);
        return 0;
    }
    ListItem* next_item = existing_previous_item->next_;
    new_item->next_ = existing_previous_item->next_;
    new_item->prev_ = existing_previous_item;
    existing_previous_item->next_ = new_item;
    if (next_item)
    {
        next_item->prev_ = new_item;
    }
    else
    {
        last_ = new_item;
    }
    size_++;
    return 0;
}

int ListWrapper::InsertBefore(ListItem* existing_next_item,
                              ListItem* new_item)
{
    if (!new_item)
    {
        return -1;
    }
    // Allow existing_next_item to be NULL if the list is empty.
    // Todo: why allow this? Keep it as is for now to avoid breaking API
    // contract.
    if (!existing_next_item && !Empty())
    {
        return -1;
    }
    CriticalSectionScoped lock(critical_section_);
    if (!existing_next_item)
    {
        PushBackImpl(new_item);
        return 0;
    }

    ListItem* previous_item = existing_next_item->prev_;
    new_item->next_ = existing_next_item;
    new_item->prev_ = previous_item;
    existing_next_item->prev_ = new_item;
    if (previous_item)
    {
        previous_item->next_ = new_item;
    }
    else
    {
        first_ = new_item;
    }
    size_++;
    return 0;
}

int ListWrapper::Erase(ListItem* item)
{
    if (!item)
    {
        return -1;
    }
    size_--;
    ListItem* previous_item = item->prev_;
    ListItem* next_item = item->next_;
    if (!previous_item)
    {
        if(next_item)
        {
            next_item->prev_ = 0;
        }
        first_ = next_item;
    }
    else
    {
        previous_item->next_ = next_item;
    }
    if (!next_item)
    {
        if(previous_item)
        {
            previous_item->next_ = 0;
        }
        last_ = previous_item;
    }
    else
    {
        next_item->prev_ = previous_item;
    }
    delete item;
    return 0;
}

void ListWrapper::PushBackImpl(ListItem* item)
{
    if (Empty())
    {
        first_ = item;
        last_ = item;
        size_++;
        return;
    }

    item->prev_ = last_;
    last_->next_ = item;
    last_ = item;
    size_++;
}

void ListWrapper::PushFrontImpl(ListItem* item)
{
    if (Empty())
    {
        first_ = item;
        last_ = item;
        size_++;
        return;
    }

    item->next_ = first_;
    first_->prev_ = item;
    first_ = item;
    size_++;
}
} //namespace webrtc
