// Copyright 2013 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.

#ifndef EXTENSIONS_COMMON_EXTENSION_SET_H_
#define EXTENSIONS_COMMON_EXTENSION_SET_H_

#include <stddef.h>

#include <iterator>
#include <map>
#include <string>

#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "extensions/common/extension.h"
#include "url/gurl.h"

namespace extensions {

// The one true extension container. Extensions are identified by their id.
// Only one extension can be in the set with a given ID.
class ExtensionSet {
 public:
  typedef std::pair<base::FilePath, std::string> ExtensionPathAndDefaultLocale;
  typedef std::map<std::string, scoped_refptr<const Extension> > ExtensionMap;
  typedef base::Callback<void(const ExtensionIdSet&)>
      ModificationCallback;

  // Iteration over the values of the map (given that it's an ExtensionSet,
  // it should iterate like a set iterator).
  class const_iterator : public std::iterator<std::input_iterator_tag,
                                              scoped_refptr<const Extension> > {
   public:
    const_iterator();
    const_iterator(const const_iterator& other);
    explicit const_iterator(ExtensionMap::const_iterator it);
    ~const_iterator();
    const_iterator& operator++() {
      ++it_;
      return *this;
    }
    const_iterator operator++(int) {
      const const_iterator old(*this);
      ++it_;
      return old;
    }
    const scoped_refptr<const Extension>& operator*() const {
      return it_->second;
    }
    const scoped_refptr<const Extension>* operator->() const {
      return &it_->second;
    }
    bool operator!=(const const_iterator& other) const {
      return it_ != other.it_;
    }
    bool operator==(const const_iterator& other) const {
      return it_ == other.it_;
    }

   private:
    ExtensionMap::const_iterator it_;
  };

  ExtensionSet();
  ~ExtensionSet();

  size_t size() const;
  bool is_empty() const;

  // Iteration support.
  const_iterator begin() const { return const_iterator(extensions_.begin()); }
  const_iterator end() const { return const_iterator(extensions_.end()); }

  // Returns true if the set contains the specified extension.
  bool Contains(const std::string& id) const;

  // Adds the specified extension to the set. The set becomes an owner. Any
  // previous extension with the same ID is removed.
  // Returns true if there is no previous extension.
  bool Insert(const scoped_refptr<const Extension>& extension);

  // Copies different items from |extensions| to the current set and returns
  // whether anything changed.
  bool InsertAll(const ExtensionSet& extensions);

  // Removes the specified extension.
  // Returns true if the set contained the specified extnesion.
  bool Remove(const std::string& id);

  // Removes all extensions.
  void Clear();

  // Returns the extension ID, or empty if none. This includes web URLs that
  // are part of an extension's web extent.
  std::string GetExtensionOrAppIDByURL(const GURL& url) const;

  // Returns the Extension, or NULL if none.  This includes web URLs that are
  // part of an extension's web extent.
  // NOTE: This can return NULL if called before UpdateExtensions receives
  // bulk extension data (e.g. if called from
  // EventBindings::HandleContextCreated)
  const Extension* GetExtensionOrAppByURL(const GURL& url) const;

  // Returns the app specified by the given |url|, if one exists. This will
  // return NULL if there is no entry with |url|, or if the extension with
  // |url| is not an app.
  const Extension* GetAppByURL(const GURL& url) const;

  // Returns the hosted app whose web extent contains the URL.
  const Extension* GetHostedAppByURL(const GURL& url) const;

  // Returns a hosted app that contains any URL that overlaps with the given
  // extent, if one exists.
  const Extension* GetHostedAppByOverlappingWebExtent(
      const URLPatternSet& extent) const;

  // Returns true if |new_url| is in the extent of the same extension as
  // |old_url|.  Also returns true if neither URL is in an app.
  bool InSameExtent(const GURL& old_url, const GURL& new_url) const;

  // Look up an Extension object by id.
  const Extension* GetByID(const std::string& id) const;

  // Gets the IDs of all extensions in the set.
  ExtensionIdSet GetIDs() const;

  // Returns true if |info| should get extension api bindings and be permitted
  // to make api calls. Note that this is independent of what extension
  // permissions the given extension has been granted.
  bool ExtensionBindingsAllowed(const GURL& url) const;

  void set_modification_callback(
      const ModificationCallback& modification_callback) {
    modification_callback_ = modification_callback;
  }

 private:
  FRIEND_TEST_ALL_PREFIXES(ExtensionSetTest, ExtensionSet);

  ExtensionMap extensions_;

  // If non-null, called with the extension ids in this set after a modification
  // occurred. This is not called on Clear() which is typically used when
  // discarding the set (e.g., on shutdown) and we do not want to track that as
  // a real modification.
  ModificationCallback modification_callback_;

  DISALLOW_COPY_AND_ASSIGN(ExtensionSet);
};

}  // namespace extensions

#endif  // EXTENSIONS_COMMON_EXTENSION_SET_H_
