// Copyright (c) 2006-2008 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_TEST_AUTOMATION_AUTOMATION_PROXY_H__
#define CHROME_TEST_AUTOMATION_AUTOMATION_PROXY_H__

#include <string>

#include "base/basictypes.h"
#include "base/scoped_ptr.h"
#include "base/thread.h"
#include "chrome/common/ipc_channel_proxy.h"
#include "chrome/common/ipc_message.h"
#include "chrome/test/automation/automation_handle_tracker.h"
#include "chrome/test/automation/automation_messages.h"

class AutomationRequest;
class BrowserProxy;
class WindowProxy;
class TabProxy;
class AutocompleteEditProxy;

// This is an interface that AutomationProxy-related objects can use to
// access the message-sending abilities of the Proxy.
class AutomationMessageSender : public IPC::Message::Sender {
 public:
  // Sends a message synchronously (from the perspective of the caller's
  // thread, at least); it doesn't return until a response has been received.
  // This method takes ownership of the request object passed in.  The caller
  // is responsible for deleting the response object when they're done with it.
  // response_type should be set to the message type of the expected response.
  // A response object will only be available if the method returns true.
  // NOTE: This method will overwrite any routing_id on the request message,
  //       since it uses this field to match the response up with the request.
  virtual bool SendAndWaitForResponse(IPC::Message* request,
                                      IPC::Message** response,
                                      int response_type) = 0;

  // Sends a message synchronously; it doesn't return until a response has been
  // received or a timeout has expired.
  // The function returns true if a response is received, and returns false if
  // there is a failure or timeout (in milliseconds). If return after timeout,
  // is_timeout is set to true.
  // See the comments in SendAndWaitForResponse for other details on usage.
  // NOTE: When timeout occurs, the connection between proxy provider may be
  //       in transit state. Specifically, there might be pending IPC messages,
  //       and the proxy provider might be still working on the previous
  //       request.
  virtual bool SendAndWaitForResponseWithTimeout(IPC::Message* request,
                                                 IPC::Message** response,
                                                 int response_type,
                                                 uint32 timeout_ms,
                                                 bool* is_timeout) = 0;
};

// This is the interface that external processes can use to interact with
// a running instance of the app.
class AutomationProxy : public IPC::Channel::Listener,
                        public AutomationMessageSender {
 public:
  AutomationProxy();
  virtual ~AutomationProxy();

  // IPC callback
  virtual void OnMessageReceived(const IPC::Message& msg);
  virtual void OnChannelError();

  // Close the automation IPC channel.
  void Disconnect();

  // Waits for the app to launch and the automation provider to say hello
  // (the app isn't fully done loading by this point).
  // Returns true if the launch is successful
  bool WaitForAppLaunch();

  // Waits for any initial page loads to complete.
  // NOTE: this only fires once for a run of the application.
  // Returns true if the load is successful
  bool WaitForInitialLoads();

  // Waits for the initial destinations tab to report that it has finished
  // querying.  |load_time| is filled in with how long it took, in milliseconds.
  // NOTE: this only fires once for a run of the application.
  // Returns true if the load is successful.
  bool WaitForInitialNewTabUILoad(int* load_time);

  // Open a new browser window, returning true on success. |show_command|
  // identifies how the window should be shown.
  // False likely indicates an IPC error.
  bool OpenNewBrowserWindow(int show_command);

  // Fills the number of open browser windows into the given variable, returning
  // true on success. False likely indicates an IPC error.
  bool GetBrowserWindowCount(int* num_windows);

  // Block the thread until the window count changes.
  // First parameter is the original window count.
  // The second parameter is updated with the number of window tabs.
  // The third parameter specifies the timeout length for the wait loop.
  // Returns false if the window count does not change before time out.
  // TODO(evanm): this function has a confusing name and semantics; it should
  // be deprecated for WaitForWindowCountToBecome.
  bool WaitForWindowCountToChange(int count, int* new_counter,
                                  int wait_timeout);

  // Block the thread until the window count becomes the provided value.
  // Returns true on success.
  bool WaitForWindowCountToBecome(int target_count, int wait_timeout);

  // Returns the BrowserProxy for the browser window at the given index,
  // transferring ownership of the pointer to the caller.
  // On failure, returns NULL.
  //
  // Use GetBrowserWindowCount to see how many browser windows you can ask for.
  // Window numbers are 0-based.
  BrowserProxy* GetBrowserWindow(int window_index);

  // Returns the BrowserProxy for the browser window which was last active,
  // transferring ownership of the pointer to the caller.
  // If there was no last active browser window, or the last active browser
  // window no longer exists (for example, if it was closed), returns
  // GetBrowserWindow(0).
  BrowserProxy* GetLastActiveBrowserWindow();

  // Returns the WindowProxy for the currently active window, transferring
  // ownership of the pointer to the caller.
  // On failure, returns NULL.
  WindowProxy* GetActiveWindow();

  // Returns the browser this window corresponds to, or NULL if this window
  // is not a browser.  The caller owns the returned BrowserProxy.
  BrowserProxy* GetBrowserForWindow(WindowProxy* window);

  // Same as GetBrowserForWindow except return NULL if response isn't received
  // before the specified timeout.
  BrowserProxy* GetBrowserForWindowWithTimeout(WindowProxy* window,
                                               uint32 timeout_ms,
                                               bool* is_timeout);

  // Returns the WindowProxy for this browser's window. It can be used to
  // retreive view bounds, simulate clicks and key press events.  The caller
  // owns the returned WindowProxy.
  // On failure, returns NULL.
  WindowProxy* GetWindowForBrowser(BrowserProxy* browser);

  // Returns an AutocompleteEdit for this browser's window. It can be used to
  // manipulate the omnibox.  The caller owns the returned pointer.
  // On failure, returns NULL.
  AutocompleteEditProxy* GetAutocompleteEditForBrowser(BrowserProxy* browser);

  // Tells the browser to enable or disable network request filtering.  Returns
  // false if the message fails to send to the browser.
  bool SetFilteredInet(bool enabled);

  // These methods are intended to be called by the background thread
  // to signal that the given event has occurred, and that any corresponding
  // Wait... function can return.
  void SignalAppLaunch();
  void SignalInitialLoads();
  // load_time is how long, in ms, the tab contents took to load.
  void SignalNewTabUITab(int load_time);

  // Returns the ID of the automation IPC channel, so that it can be
  // passed to the app as a launch parameter.
  const std::wstring& channel_id() const { return channel_id_; }

  // AutomationMessageSender implementations.
  virtual bool Send(IPC::Message* message);
  virtual bool SendAndWaitForResponse(IPC::Message* request,
                                      IPC::Message** response,
                                      int response_type);
  virtual bool SendAndWaitForResponseWithTimeout(IPC::Message* request,
                                                 IPC::Message** response,
                                                 int response_type,
                                                 uint32 timeout_ms,
                                                 bool* is_timeout);

  // Returns the current AutomationRequest object.
  AutomationRequest* current_request() { return current_request_; }
  // Clears the current AutomationRequest object.
  void clear_current_request() { current_request_ = NULL; }

  // Wrapper over AutomationHandleTracker::InvalidateHandle. Receives the message
  // from AutomationProxy, unpacks the messages and routes that call to the
  // tracker.
  void InvalidateHandle(const IPC::Message& message);

  // Creates a tab that can hosted in an external process. The function
  // returns a TabProxy representing the tab as well as a window handle
  // that can be reparented in another process.
  TabProxy* CreateExternalTab(HWND* external_tab_container);

 private:
  DISALLOW_EVIL_CONSTRUCTORS(AutomationProxy);

  void InitializeEvents();
  void InitializeChannelID();
  void InitializeThread();
  void InitializeChannel();
  void InitializeHandleTracker();

  std::wstring channel_id_;
  scoped_ptr<Thread> thread_;
  scoped_ptr<IPC::ChannelProxy> channel_;
  scoped_ptr<AutomationHandleTracker> tracker_;

  HANDLE app_launched_;
  HANDLE initial_loads_complete_;
  HANDLE new_tab_ui_load_complete_;
  int new_tab_ui_load_time_;

  AutomationRequest* current_request_;

  static const int kMaxCommandExecutionTime;  // Delay to let the browser
                                              //  execute the command.;
};

#endif  // CHROME_TEST_AUTOMATION_AUTOMATION_PROXY_H__