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

#ifndef EXTENSIONS_BROWSER_APP_WINDOW_APP_WINDOW_REGISTRY_H_
#define EXTENSIONS_BROWSER_APP_WINDOW_APP_WINDOW_REGISTRY_H_

#include <list>
#include <set>
#include <string>

#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/singleton.h"
#include "base/observer_list.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/devtools_agent_host_observer.h"
#include "ui/gfx/native_widget_types.h"

namespace content {
class BrowserContext;
class DevToolsAgentHost;
class WebContents;
}

namespace extensions {

class AppWindow;

// The AppWindowRegistry tracks the AppWindows for all platform apps for a
// particular browser context.
class AppWindowRegistry : public KeyedService,
                          public content::DevToolsAgentHostObserver {
 public:
  class Observer {
   public:
    // Called just after a app window was added.
    virtual void OnAppWindowAdded(AppWindow* app_window);
    // Called when the window icon changes.
    virtual void OnAppWindowIconChanged(AppWindow* app_window);
    // Called just after a app window was removed.
    virtual void OnAppWindowRemoved(AppWindow* app_window);
    // Called just after a app window was hidden. This is different from
    // window visibility as a minimize does not hide a window, but does make
    // it not visible.
    virtual void OnAppWindowHidden(AppWindow* app_window);
    // Called just after a app window was shown.
    virtual void OnAppWindowShown(AppWindow* app_window, bool was_hidden);
    // Called just after a app window was activated.
    virtual void OnAppWindowActivated(AppWindow* app_window);

   protected:
    virtual ~Observer();
  };

  typedef std::list<AppWindow*> AppWindowList;
  typedef AppWindowList::const_iterator const_iterator;
  typedef std::set<std::string> InspectedWindowSet;

  explicit AppWindowRegistry(content::BrowserContext* context);
  ~AppWindowRegistry() override;

  // Returns the instance for the given browser context, or NULL if none. This
  // is a convenience wrapper around
  // AppWindowRegistry::Factory::GetForBrowserContext().
  static AppWindowRegistry* Get(content::BrowserContext* context);

  void AddAppWindow(AppWindow* app_window);
  void AppWindowIconChanged(AppWindow* app_window);
  // Called by |app_window| when it is activated.
  void AppWindowActivated(AppWindow* app_window);
  void AppWindowHidden(AppWindow* app_window);
  void AppWindowShown(AppWindow* app_window, bool was_hidden);
  void RemoveAppWindow(AppWindow* app_window);

  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  // Returns a set of windows owned by the application identified by app_id.
  AppWindowList GetAppWindowsForApp(const std::string& app_id) const;
  const AppWindowList& app_windows() const { return app_windows_; }

  // Close all app windows associated with an app.
  void CloseAllAppWindowsForApp(const std::string& app_id);

  // Helper functions to find app windows with particular attributes.
  AppWindow* GetAppWindowForWebContents(
      const content::WebContents* web_contents) const;
  AppWindow* GetAppWindowForNativeWindow(gfx::NativeWindow window) const;
  // Returns an app window for the given app, or NULL if no app windows are
  // open. If there is a window for the given app that is active, that one will
  // be returned, otherwise an arbitrary window will be returned.
  AppWindow* GetCurrentAppWindowForApp(const std::string& app_id) const;
  // Returns an app window for the given app and window key, or NULL if no app
  // window with the key are open. If there is a window for the given app and
  // key that is active, that one will be returned, otherwise an arbitrary
  // window will be returned.
  AppWindow* GetAppWindowForAppAndKey(const std::string& app_id,
                                      const std::string& window_key) const;

  // Returns whether a AppWindow's ID was last known to have a DevToolsAgent
  // attached to it, which should be restored during a reload of a corresponding
  // newly created |web_contents|.
  bool HadDevToolsAttached(content::WebContents* web_contents) const;

  class Factory : public BrowserContextKeyedServiceFactory {
   public:
    static AppWindowRegistry* GetForBrowserContext(
        content::BrowserContext* context,
        bool create);

    static Factory* GetInstance();

   private:
    friend struct base::DefaultSingletonTraits<Factory>;

    Factory();
    ~Factory() override;

    // BrowserContextKeyedServiceFactory
    KeyedService* BuildServiceInstanceFor(
        content::BrowserContext* context) const override;
    bool ServiceIsCreatedWithBrowserContext() const override;
    bool ServiceIsNULLWhileTesting() const override;
    content::BrowserContext* GetBrowserContextToUse(
        content::BrowserContext* context) const override;
  };

 private:
  // Ensures the specified |app_window| is included in |app_windows_|.
  // Otherwise adds |app_window| to the back of |app_windows_|.
  void AddAppWindowToList(AppWindow* app_window);

  // Bring |app_window| to the front of |app_windows_|. If it is not in the
  // list, add it first.
  void BringToFront(AppWindow* app_window);

  // Create a key that identifies an AppWindow across App reloads. If the window
  // was given an id in CreateParams, the key is the extension id, a colon
  // separator, and the AppWindow's |id|. If there is no |id|, the
  // chrome-extension://extension-id/page.html URL will be used. If the
  // WebContents is not for a AppWindow, return an empty string.
  std::string GetWindowKeyForWebContents(
      content::WebContents* web_contents) const;
  std::string GetWindowKeyForAgentHost(
      content::DevToolsAgentHost* agent_host) const;

  // content::DevToolsAgentHostObserver overrides.
  void DevToolsAgentHostAttached(
      content::DevToolsAgentHost* agent_host) override;
  void DevToolsAgentHostDetached(
      content::DevToolsAgentHost* agent_host) override;

  content::BrowserContext* context_;
  AppWindowList app_windows_;
  InspectedWindowSet inspected_windows_;
  base::ObserverList<Observer> observers_;
};

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_APP_WINDOW_APP_WINDOW_REGISTRY_H_
