// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/error_map.h"

#include "base/lazy_instance.h"
#include "base/stl_util.h"
#include "extensions/common/extension.h"

namespace extensions {
namespace {

// The maximum number of errors to be stored per extension.
const size_t kMaxErrorsPerExtension = 100;

base::LazyInstance<ErrorList> g_empty_error_list = LAZY_INSTANCE_INITIALIZER;

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// ErrorMap::ExtensionEntry
ErrorMap::ExtensionEntry::ExtensionEntry() {
}

ErrorMap::ExtensionEntry::~ExtensionEntry() {
  DeleteAllErrors();
}

void ErrorMap::ExtensionEntry::DeleteAllErrors() {
  STLDeleteContainerPointers(list_.begin(), list_.end());
  list_.clear();
}

void ErrorMap::ExtensionEntry::DeleteIncognitoErrors() {
  ErrorList::iterator iter = list_.begin();
  while (iter != list_.end()) {
    if ((*iter)->from_incognito()) {
      delete *iter;
      iter = list_.erase(iter);
    } else {
      ++iter;
    }
  }
}

void ErrorMap::ExtensionEntry::DeleteErrorsOfType(ExtensionError::Type type) {
  ErrorList::iterator iter = list_.begin();
  while (iter != list_.end()) {
    if ((*iter)->type() == type) {
      delete *iter;
      iter = list_.erase(iter);
    } else {
      ++iter;
    }
  }
}

const ExtensionError* ErrorMap::ExtensionEntry::AddError(
    scoped_ptr<ExtensionError> error) {
  for (ErrorList::iterator iter = list_.begin(); iter != list_.end(); ++iter) {
    // If we find a duplicate error, remove the old error and add the new one,
    // incrementing the occurrence count of the error. We use the new error
    // for runtime errors, so we can link to the latest context, inspectable
    // view, etc.
    if (error->IsEqual(*iter)) {
      error->set_occurrences((*iter)->occurrences() + 1);
      delete *iter;
      list_.erase(iter);
      break;
    }
  }

  // If there are too many errors for an extension already, limit ourselves to
  // the most recent ones.
  if (list_.size() >= kMaxErrorsPerExtension) {
    delete list_.front();
    list_.pop_front();
  }

  list_.push_back(error.release());
  return list_.back();
}

////////////////////////////////////////////////////////////////////////////////
// ErrorMap
ErrorMap::ErrorMap() {
}

ErrorMap::~ErrorMap() {
  RemoveAllErrors();
}

const ErrorList& ErrorMap::GetErrorsForExtension(
    const std::string& extension_id) const {
  EntryMap::const_iterator iter = map_.find(extension_id);
  return iter != map_.end() ? *iter->second->list() : g_empty_error_list.Get();
}

const ExtensionError* ErrorMap::AddError(scoped_ptr<ExtensionError> error) {
  EntryMap::iterator iter = map_.find(error->extension_id());
  if (iter == map_.end()) {
    iter = map_.insert(std::pair<std::string, ExtensionEntry*>(
        error->extension_id(), new ExtensionEntry)).first;
  }
  return iter->second->AddError(error.Pass());
}

void ErrorMap::Remove(const std::string& extension_id) {
  EntryMap::iterator iter = map_.find(extension_id);
  if (iter == map_.end())
    return;

  delete iter->second;
  map_.erase(iter);
}

void ErrorMap::RemoveErrorsForExtensionOfType(const std::string& extension_id,
                                              ExtensionError::Type type) {
  EntryMap::iterator iter = map_.find(extension_id);
  if (iter != map_.end())
    iter->second->DeleteErrorsOfType(type);
}

void ErrorMap::RemoveIncognitoErrors() {
  for (EntryMap::iterator iter = map_.begin(); iter != map_.end(); ++iter)
    iter->second->DeleteIncognitoErrors();
}

void ErrorMap::RemoveAllErrors() {
  for (EntryMap::iterator iter = map_.begin(); iter != map_.end(); ++iter) {
    iter->second->DeleteAllErrors();
    delete iter->second;
  }
  map_.clear();
}

}  // namespace extensions