/*
 *  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 "webrtc/system_wrappers/interface/list_wrapper.h"

#include "webrtc/system_wrappers/interface/trace.h"

namespace webrtc {

ListItem::ListItem(const void* item)
    : this_iter_(),
      item_ptr_(item),
      item_(0) {
}

ListItem::ListItem(const unsigned int item)
    : this_iter_(),
      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()
    : list_() {
}

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) {}
  }
}

bool ListWrapper::Empty() const {
  return list_.empty();
}

unsigned int ListWrapper::GetSize() const {
  return list_.size();
}

int ListWrapper::PushBack(const void* ptr) {
  ListItem* item = new ListItem(ptr);
  list_.push_back(item);
  return 0;
}

int ListWrapper::PushBack(const unsigned int item_id) {
  ListItem* item = new ListItem(item_id);
  list_.push_back(item);
  return 0;
}

int ListWrapper::PushFront(const unsigned int item_id) {
  ListItem* item = new ListItem(item_id);
  list_.push_front(item);
  return 0;
}

int ListWrapper::PushFront(const void* ptr) {
  ListItem* item = new ListItem(ptr);
  list_.push_front(item);
  return 0;
}

int ListWrapper::PopFront() {
  if (list_.empty()) {
    return -1;
  }
  list_.pop_front();
  return 0;
}

int ListWrapper::PopBack() {
  if (list_.empty()) {
    return -1;
  }
  list_.pop_back();
  return 0;
}

ListItem* ListWrapper::First() const {
  if (list_.empty()) {
    return NULL;
  }
  std::list<ListItem*>::iterator item_iter = list_.begin();
  ListItem* return_item = (*item_iter);
  return_item->this_iter_ = item_iter;
  return return_item;
}

ListItem* ListWrapper::Last() const {
  if (list_.empty()) {
    return NULL;
  }
  // std::list::end() addresses the last item + 1. Decrement so that the
  // actual last is accessed.
  std::list<ListItem*>::iterator item_iter = list_.end();
  --item_iter;
  ListItem* return_item = (*item_iter);
  return_item->this_iter_ = item_iter;
  return return_item;
}

ListItem* ListWrapper::Next(ListItem* item) const {
  if (item == NULL) {
    return NULL;
  }
  std::list<ListItem*>::iterator item_iter = item->this_iter_;
  ++item_iter;
  if (item_iter == list_.end()) {
    return NULL;
  }
  ListItem* return_item = (*item_iter);
  return_item->this_iter_ = item_iter;
  return return_item;
}

ListItem* ListWrapper::Previous(ListItem* item) const {
  if (item == NULL) {
    return NULL;
  }
  std::list<ListItem*>::iterator item_iter = item->this_iter_;
  if (item_iter == list_.begin()) {
    return NULL;
  }
  --item_iter;
  ListItem* return_item = (*item_iter);
  return_item->this_iter_ = item_iter;
  return return_item;
}

int ListWrapper::Insert(ListItem* existing_previous_item,
                        ListItem* new_item) {
  // 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;
  }

  if (!new_item) {
    return -1;
  }

  std::list<ListItem*>::iterator insert_location = list_.begin();
  if (!Empty()) {
    insert_location = existing_previous_item->this_iter_;
    if (insert_location != list_.end()) {
      ++insert_location;
    }
  }

  list_.insert(insert_location, new_item);
  return 0;
}

int ListWrapper::InsertBefore(ListItem* existing_next_item,
                              ListItem* new_item) {
  // 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;
  }
  if (!new_item) {
    return -1;
  }

  std::list<ListItem*>::iterator insert_location = list_.begin();
  if (!Empty()) {
    insert_location = existing_next_item->this_iter_;
  }

  list_.insert(insert_location, new_item);
  return 0;
}

int ListWrapper::Erase(ListItem* item) {
  if (item == NULL) {
    return -1;
  }
  list_.erase(item->this_iter_);
  return 0;
}

} // namespace webrtc
