/*
 *  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/source/map_no_stl.h"

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

namespace webrtc {

MapNoStlItem::MapNoStlItem(int id, void* item)
    : next_(0),
      prev_(0),
      item_id_(id),
      item_ptr_(item) {
}

MapNoStlItem::~MapNoStlItem() {
}

void* MapNoStlItem::GetItem() {
  return item_ptr_;
}

int MapNoStlItem::GetId() {
  return item_id_;
}

unsigned int MapNoStlItem::GetUnsignedId() {
  return static_cast<unsigned int>(item_id_);
}

void MapNoStlItem::SetItem(void* ptr) {
  item_ptr_ = ptr;
}

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

MapNoStl::~MapNoStl() {
  if (First()) {
    WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1,
                 "Potential memory leak in MapNoStl");
    while (Erase(First()) == 0)
    {}
  }
  delete critical_section_;
}

int MapNoStl::Size() const {
  return size_;
}

int MapNoStl::Insert(int id, void* ptr) {
  MapNoStlItem* new_item = new MapNoStlItem(id, ptr);

  CriticalSectionScoped lock(critical_section_);
  MapNoStlItem* item = first_;
  size_++;
  if (!item) {
    first_ = new_item;
    last_ = new_item;
    return 0;
  }
  while (item->next_) {
    // Three scenarios
    // 1. Item should be inserted first.
    // 2. Item should be inserted between two items
    // 3. Item should be inserted last
    if (item->GetId() > id) {
      new_item->next_ = item;
      item->prev_ = new_item;
      if (item == first_) {
        first_ = new_item;
      } else {
        new_item->prev_ = item->prev_;
        new_item->prev_->next_ = new_item;
      }
      return 0;
    }
    item = item->next_;
  }
  // 3
  item->next_ = new_item;
  new_item->prev_ = item;
  last_ = new_item;
  return 0;
}

MapNoStlItem* MapNoStl::First() const {
  return first_;
}

MapNoStlItem* MapNoStl::Last() const {
  return last_;
}

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

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

MapNoStlItem* MapNoStl::Find(int id) const {
  CriticalSectionScoped lock(critical_section_);
  MapNoStlItem* item = Locate(id);
  return item;
}

int MapNoStl::Erase(MapNoStlItem* item) {
  if (!item) {
    return -1;
  }
  CriticalSectionScoped lock(critical_section_);
  return Remove(item);
}

int MapNoStl::Erase(const int id) {
  CriticalSectionScoped lock(critical_section_);
  MapNoStlItem* item = Locate(id);
  if (!item) {
    return -1;
  }
  return Remove(item);
}

MapNoStlItem* MapNoStl::Locate(int id) const {
  MapNoStlItem* item = first_;
  while (item) {
    if (item->GetId() == id) {
      return item;
    }
    item = item->next_;
  }
  return 0;
}

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

} // namespace webrtc
