// Copyright (c) 2011 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 CHROME_BROWSER_SERVICE_SERVICE_PROCESS_CONTROL_H_
#define CHROME_BROWSER_SERVICE_SERVICE_PROCESS_CONTROL_H_

#include <queue>
#include <set>
#include <string>
#include <vector>

#include "base/basictypes.h"
#include "base/callback.h"
#include "base/id_map.h"
#include "base/memory/scoped_ptr.h"
#include "base/process.h"
#include "base/task.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
#include "ipc/ipc_sync_channel.h"

class Profile;
class CommandLine;

namespace remoting {
struct ChromotingHostInfo;
}  // namespace remoting

// A ServiceProcessControl works as a portal between the service process and
// the browser process.
//
// It is used to start and terminate the service process. It is also used
// to send and receive IPC messages from the service process.
//
// THREADING
//
// This class is accessed on the UI thread through some UI actions. It then
// talks to the IPC channel on the IO thread.
class ServiceProcessControl : public IPC::Channel::Sender,
                              public IPC::Channel::Listener,
                              public NotificationObserver {
 public:
  typedef IDMap<ServiceProcessControl>::iterator iterator;
  typedef std::queue<IPC::Message> MessageQueue;
  typedef Callback1<const remoting::ChromotingHostInfo&>::Type
      RemotingHostStatusHandler;

  // An interface for handling messages received from the service process.
  class MessageHandler {
   public:
    virtual ~MessageHandler() {}

    // Called when we receive reply to remoting host status request.
    virtual void OnRemotingHostInfo(
        const remoting::ChromotingHostInfo& host_info) = 0;
  };

  // Construct a ServiceProcessControl with |profile|..
  explicit ServiceProcessControl(Profile* profile);
  virtual ~ServiceProcessControl();

  // Return the user profile associated with this service process.
  Profile* profile() const { return profile_; }

  // Return true if this object is connected to the service.
  bool is_connected() const { return channel_.get() != NULL; }

  // If no service process is currently running, creates a new service process
  // and connects to it.
  // If a service process is already running this method will try to connect
  // to it.
  // |success_task| is called when we have successfully launched the process
  // and connected to it.
  // |failure_task| is called when we failed to connect to the service process.
  // It is OK to pass the same value for |success_task| and |failure_task|. In
  // this case, the task is invoked on success or failure.
  // Note that if we are already connected to service process then
  // |success_task| can be invoked in the context of the Launch call.
  // Takes ownership of |success_task| and |failure_task|.
  void Launch(Task* success_task, Task* failure_task);

  // IPC::Channel::Listener implementation.
  virtual bool OnMessageReceived(const IPC::Message& message);
  virtual void OnChannelConnected(int32 peer_pid);
  virtual void OnChannelError();

  // IPC::Channel::Sender implementation
  virtual bool Send(IPC::Message* message);

  // NotificationObserver implementation.
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details);

  // Message handlers
  void OnCloudPrintProxyIsEnabled(bool enabled, std::string email);
  void OnRemotingHostInfo(const remoting::ChromotingHostInfo& host_info);

  // Send a shutdown message to the service process. IPC channel will be
  // destroyed after calling this method.
  // Return true if the message was sent.
  bool Shutdown();

  // Send request for cloud print proxy status and the registered
  // email address. The callback gets the information when received.
  bool GetCloudPrintProxyStatus(
      Callback2<bool, std::string>::Type* cloud_print_status_callback);

  // Send a message to enable the remoting service in the service process.
  // Return true if the message was sent.
  bool SetRemotingHostCredentials(const std::string& user,
                                  const std::string& auth_token);

  bool EnableRemotingHost();
  bool DisableRemotingHost();

  // Send request for current status of the remoting service.
  // MessageHandler::OnRemotingHostInfo() will be called when remoting host
  // status is available.
  bool RequestRemotingHostStatus();

  // Add a message handler for receiving messages from the service
  // process.
  void AddMessageHandler(MessageHandler* message_handler);

  // Remove a message handler from the list of message handlers. Must
  // not be called from a message handler (i.e. while a message is
  // being processed).
  void RemoveMessageHandler(MessageHandler* message_handler);

 private:
  // This class is responsible for launching the service process on the
  // PROCESS_LAUNCHER thread.
  class Launcher
      : public base::RefCountedThreadSafe<ServiceProcessControl::Launcher> {
   public:
    Launcher(ServiceProcessControl* process, CommandLine* cmd_line);
    // Execute the command line to start the process asynchronously.
    // After the comamnd is executed |task| is called with the process handle on
    // the UI thread.
    void Run(Task* task);

    bool launched() const { return launched_; }

   private:
    friend class base::RefCountedThreadSafe<ServiceProcessControl::Launcher>;
    virtual ~Launcher();

#if !defined(OS_MACOSX)
    void DoDetectLaunched();
#endif  // !OS_MACOSX

    void DoRun();
    void Notify();
    ServiceProcessControl* process_;
    scoped_ptr<CommandLine> cmd_line_;
    scoped_ptr<Task> notify_task_;
    bool launched_;
    uint32 retry_count_;
  };

  typedef std::vector<Task*> TaskList;

  // Helper method to invoke all the callbacks based on success on failure.
  void RunConnectDoneTasks();

  // Method called by Launcher when the service process is launched.
  void OnProcessLaunched();

  // Used internally to connect to the service process.
  void ConnectInternal();

  static void RunAllTasksHelper(TaskList* task_list);

  Profile* profile_;

  // IPC channel to the service process.
  scoped_ptr<IPC::SyncChannel> channel_;

  // Service process launcher.
  scoped_refptr<Launcher> launcher_;

  // Callbacks that get invoked when the channel is successfully connected or
  // if there was a failure in connecting.
  TaskList connect_done_tasks_;
  // Callbacks that get invoked ONLY when the channel is successfully connected.
  TaskList connect_success_tasks_;
  // Callbacks that get invoked ONLY when there was a connection failure.
  TaskList connect_failure_tasks_;

  // Callback that gets invoked when a status message is received from
  // the cloud print proxy.
  scoped_ptr<Callback2<bool, std::string>::Type> cloud_print_status_callback_;

  // Handler for messages from service process.
  std::set<MessageHandler*> message_handlers_;

  NotificationRegistrar registrar_;
};

#endif  // CHROME_BROWSER_SERVICE_SERVICE_PROCESS_CONTROL_H_