// 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 SERVICES_UI_PUBLIC_CPP_WINDOW_H_
#define SERVICES_UI_PUBLIC_CPP_WINDOW_H_

#include <stdint.h>
#include <vector>

#include "base/callback.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "mojo/public/cpp/bindings/array.h"
#include "services/service_manager/public/interfaces/interface_provider.mojom.h"
#include "services/ui/common/types.h"
#include "services/ui/public/cpp/surface_id_handler.h"
#include "services/ui/public/cpp/window_compositor_frame_sink.h"
#include "services/ui/public/interfaces/mus_constants.mojom.h"
#include "services/ui/public/interfaces/window_tree.mojom.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect.h"

namespace gfx {
class Size;
}

namespace gpu {
class GpuMemoryBufferManager;
}

namespace ui {

class InputEventHandler;
class ServiceProviderImpl;
class SurfaceIdHandler;
class WindowCompositorFrameSinkBinding;
class WindowObserver;
class WindowSurface;
class WindowDropTarget;
class WindowTreeClient;
class WindowTreeClientPrivate;

namespace {
class OrderChangedNotifier;
}

// Defined in window_property.h (which we do not include)
template <typename T>
struct WindowProperty;

// Windows are owned by the WindowTreeClient. See WindowTreeClientDelegate for
// details on ownership.
//
// TODO(beng): Right now, you'll have to implement a WindowObserver to track
//             destruction and NULL any pointers you have.
//             Investigate some kind of smart pointer or weak pointer for these.
class Window {
 public:
  using Children = std::vector<Window*>;
  using EmbedCallback = base::Callback<void(bool)>;
  using PropertyDeallocator = void (*)(int64_t value);
  using SharedProperties = std::map<std::string, std::vector<uint8_t>>;

  // Destroys this window and all its children. Destruction is allowed for
  // windows that were created by this connection, or the roots. For windows
  // from other connections (except the roots), Destroy() does nothing. If the
  // destruction is allowed observers are notified and the Window is
  // immediately deleted.
  void Destroy();

  // Returns true if this client created and owns this window.
  bool WasCreatedByThisClient() const;

  WindowTreeClient* window_tree() { return client_; }

  // The local_id is provided for client code. The local_id is not set or
  // manipulated by mus. The default value is -1.
  void set_local_id(int id) { local_id_ = id; }
  int local_id() const { return local_id_; }

  int64_t display_id() const { return display_id_; }

  // Geometric disposition relative to parent window.
  const gfx::Rect& bounds() const { return bounds_; }
  void SetBounds(const gfx::Rect& bounds);

  // Geometric disposition relative to root window.
  gfx::Rect GetBoundsInRoot() const;

  const gfx::Insets& client_area() const { return client_area_; }
  const std::vector<gfx::Rect>& additional_client_areas() {
    return additional_client_areas_;
  }
  void SetClientArea(const gfx::Insets& new_client_area) {
    SetClientArea(new_client_area, std::vector<gfx::Rect>());
  }
  void SetClientArea(const gfx::Insets& new_client_area,
                     const std::vector<gfx::Rect>& additional_client_areas);

  // Mouse events outside the hit test mask do not hit the window. Returns null
  // if there is no mask.
  const gfx::Rect* hit_test_mask() const { return hit_test_mask_.get(); }
  void SetHitTestMask(const gfx::Rect& mask_rect);
  void ClearHitTestMask();

  // Visibility (also see IsDrawn()). When created windows are hidden.
  bool visible() const { return visible_; }
  void SetVisible(bool value);

  float opacity() const { return opacity_; }
  void SetOpacity(float opacity);

  // Cursors
  mojom::Cursor predefined_cursor() const { return cursor_id_; }
  void SetPredefinedCursor(ui::mojom::Cursor cursor_id);

  // A Window is drawn if the Window and all its ancestors are visible and the
  // Window is attached to the root.
  bool IsDrawn() const;

  std::unique_ptr<WindowCompositorFrameSink> RequestCompositorFrameSink(
      mojom::CompositorFrameSinkType type,
      scoped_refptr<cc::ContextProvider> context_provider,
      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);

  void AttachCompositorFrameSink(
      mojom::CompositorFrameSinkType type,
      std::unique_ptr<WindowCompositorFrameSinkBinding>
          compositor_frame_sink_binding);

  // The template-ized versions of the following methods rely on the presence
  // of a mojo::TypeConverter<const std::vector<uint8_t>, T>.
  // Sets a shared property on the window which is sent to the window server and
  // shared with other clients that can view this window.
  template <typename T>
  void SetSharedProperty(const std::string& name, const T& data);
  // Gets a shared property set on the window. The property must exist. Call
  // HasSharedProperty() before calling.
  template <typename T>
  T GetSharedProperty(const std::string& name) const;
  // Removes the shared property.
  void ClearSharedProperty(const std::string& name);
  bool HasSharedProperty(const std::string& name) const;

  // TODO(beng): Test only, should move to a helper.
  const SharedProperties& shared_properties() { return properties_; }

  // Sets the |value| of the given window |property|. Setting to the default
  // value (e.g., NULL) removes the property. The caller is responsible for the
  // lifetime of any object set as a property on the Window.
  //
  // These properties are not visible to the window server.
  template <typename T>
  void SetLocalProperty(const WindowProperty<T>* property, T value);

  // Returns the value of the given window |property|.  Returns the
  // property-specific default value if the property was not previously set.
  //
  // These properties are only visible in the current process and are not
  // shared with other mojo services.
  template <typename T>
  T GetLocalProperty(const WindowProperty<T>* property) const;

  // Sets the |property| to its default value. Useful for avoiding a cast when
  // setting to NULL.
  //
  // These properties are only visible in the current process and are not
  // shared with other mojo services.
  template <typename T>
  void ClearLocalProperty(const WindowProperty<T>* property);

  void set_input_event_handler(InputEventHandler* input_event_handler) {
    input_event_handler_ = input_event_handler;
  }

  void set_surface_id_handler(SurfaceIdHandler* surface_id_handler) {
    surface_id_handler_ = surface_id_handler;
  }

  // Observation.
  void AddObserver(WindowObserver* observer);
  void RemoveObserver(WindowObserver* observer);

  // Tree.
  Window* parent() { return parent_; }
  const Window* parent() const { return parent_; }

  Window* GetRoot() {
    return const_cast<Window*>(const_cast<const Window*>(this)->GetRoot());
  }
  const Window* GetRoot() const;

  void AddChild(Window* child);
  void RemoveChild(Window* child);
  const Children& children() const { return children_; }

  void Reorder(Window* relative, mojom::OrderDirection direction);
  void MoveToFront();
  void MoveToBack();

  // Returns true if |child| is this or a descendant of this.
  bool Contains(const Window* child) const;

  void AddTransientWindow(Window* transient_window);
  void RemoveTransientWindow(Window* transient_window);

  // TODO(fsamuel): Figure out if we want to refactor transient window
  // management into a separate class.
  // Transient tree.
  Window* transient_parent() { return transient_parent_; }
  const Window* transient_parent() const { return transient_parent_; }
  const Children& transient_children() const { return transient_children_; }

  void SetModal();
  bool is_modal() const { return is_modal_; }

  Window* GetChildByLocalId(int id);

  void SetTextInputState(mojo::TextInputStatePtr state);
  void SetImeVisibility(bool visible, mojo::TextInputStatePtr state);

  bool HasCapture() const;
  void SetCapture();
  void ReleaseCapture();

  // Focus. See WindowTreeClient::ClearFocus() to reset focus.
  void SetFocus();
  bool HasFocus() const;
  void SetCanFocus(bool can_focus);

  // Sets whether this window accepts drags. Passing a non-null |drop_target|
  // will enable acceptance of drops. Passing null will disable it.
  void SetCanAcceptDrops(WindowDropTarget* drop_target);
  WindowDropTarget* drop_target() { return drop_target_; }

  // Sets whether this window accepts events.
  void SetCanAcceptEvents(bool can_accept_events);

  // Embedding. See window_tree.mojom for details.
  void Embed(ui::mojom::WindowTreeClientPtr client, uint32_t flags = 0);

  // NOTE: callback is run synchronously if Embed() is not allowed on this
  // Window.
  void Embed(ui::mojom::WindowTreeClientPtr client,
             const EmbedCallback& callback,
             uint32_t flags = 0);

  // TODO(sky): this API is only applicable to the WindowManager. Move it
  // to a better place.
  void RequestClose();

  // Starts an inter-process drag and drop operation. When this finishes, will
  // return the tuple [success, action_taken] to |callback|, where action_taken
  // is one of the ui::mojom::kDropEffect constants in
  // window_tree_constants.mojom.
  void PerformDragDrop(
      const std::map<std::string, std::vector<uint8_t>>& drag_data,
      int drag_operation,
      const gfx::Point& cursor_location,
      const SkBitmap& bitmap,
      const base::Callback<void(bool, uint32_t)>& callback);

  // Cancels the in progress drag started with PerformDragDrop().
  void CancelDragDrop();

  // Tells the window manager to take control of moving the window. Returns
  // true if the move wasn't canceled.
  void PerformWindowMove(mojom::MoveLoopSource source,
                         const gfx::Point& cursor_location,
                         const base::Callback<void(bool)>& callback);

  // Tells the window manager to abort any current move initiated by
  // PerformWindowMove().
  void CancelWindowMove();

  // Returns an internal name, set by a client app when it creates a window.
  std::string GetName() const;

 protected:
  // This class is subclassed only by test classes that provide a public ctor.
  Window();
  ~Window();

 private:
  friend class WindowPrivate;
  friend class WindowTreeClient;
  friend class WindowTreeClientPrivate;

  Window(WindowTreeClient* client, Id id);

  // Used to identify this Window on the server. Clients can not change this
  // value.
  Id server_id() const { return server_id_; }

  // Applies a shared property change locally and forwards to the server. If
  // |data| is null, this property is deleted.
  void SetSharedPropertyInternal(const std::string& name,
                                 const std::vector<uint8_t>* data);
  // Called by the public {Set,Get,Clear}Property functions.
  int64_t SetLocalPropertyInternal(const void* key,
                                   const char* name,
                                   PropertyDeallocator deallocator,
                                   int64_t value,
                                   int64_t default_value);
  int64_t GetLocalPropertyInternal(const void* key,
                                   int64_t default_value) const;

  void LocalDestroy();
  void LocalAddChild(Window* child);
  void LocalRemoveChild(Window* child);
  void LocalAddTransientWindow(Window* transient_window);
  void LocalRemoveTransientWindow(Window* transient_window);
  void LocalSetModal();
  // Returns true if the order actually changed.
  bool LocalReorder(Window* relative, mojom::OrderDirection direction);
  void LocalSetBounds(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds);
  void LocalSetClientArea(
      const gfx::Insets& new_client_area,
      const std::vector<gfx::Rect>& additional_client_areas);
  void LocalSetParentDrawn(bool drawn);
  void LocalSetDisplay(int64_t display_id);
  void LocalSetVisible(bool visible);
  void LocalSetOpacity(float opacity);
  void LocalSetPredefinedCursor(mojom::Cursor cursor_id);
  void LocalSetSharedProperty(const std::string& name,
                              const std::vector<uint8_t>* data);
  void LocalSetSurfaceId(std::unique_ptr<SurfaceInfo> surface_info);

  // Notifies this winodw that its stacking position has changed.
  void NotifyWindowStackingChanged();
  // Methods implementing visibility change notifications. See WindowObserver
  // for more details.
  void NotifyWindowVisibilityChanged(Window* target, bool visible);
  // Notifies this window's observers. Returns false if |this| was deleted
  // during the call (by an observer), otherwise true.
  bool NotifyWindowVisibilityChangedAtReceiver(Window* target, bool visible);
  // Notifies this window and its child hierarchy. Returns false if |this| was
  // deleted during the call (by an observer), otherwise true.
  bool NotifyWindowVisibilityChangedDown(Window* target, bool visible);
  // Notifies this window and its parent hierarchy.
  void NotifyWindowVisibilityChangedUp(Window* target, bool visible);

  // Returns true if embed is allowed for this node. If embedding is allowed all
  // the children are removed.
  bool PrepareForEmbed();

  void RemoveTransientWindowImpl(Window* child);
  static void ReorderWithoutNotification(Window* window,
                                         Window* relative,
                                         mojom::OrderDirection direction);
  // Returns true if the order actually changed.
  static bool ReorderImpl(Window* window,
                          Window* relative,
                          mojom::OrderDirection direction,
                          OrderChangedNotifier* notifier);

  // Returns a pointer to the stacking target that can be used by
  // RestackTransientDescendants.
  static Window** GetStackingTarget(Window* window);

  WindowTreeClient* client_;
  Id server_id_;
  int local_id_ = -1;
  Window* parent_;
  Children children_;

  Window* stacking_target_;
  Window* transient_parent_;
  Children transient_children_;

  bool is_modal_;

  base::ObserverList<WindowObserver> observers_;
  InputEventHandler* input_event_handler_;
  SurfaceIdHandler* surface_id_handler_;

  gfx::Rect bounds_;
  gfx::Insets client_area_;
  std::vector<gfx::Rect> additional_client_areas_;
  std::unique_ptr<gfx::Rect> hit_test_mask_;

  bool visible_;
  float opacity_;
  int64_t display_id_;

  // The client supplied delegate that receives drag events for this
  // window (weak ptr).
  WindowDropTarget* drop_target_ = nullptr;

  // Whether this window can accept events. Initialized to true to
  // match ServerWindow.
  bool can_accept_events_ = true;

  mojom::Cursor cursor_id_;

  SharedProperties properties_;

  // Drawn state of our parent. This is only meaningful for root Windows, in
  // which the parent Window isn't exposed to the client.
  bool parent_drawn_;

  // Value struct to keep the name and deallocator for this property.
  // Key cannot be used for this purpose because it can be char* or
  // WindowProperty<>.
  struct Value {
    const char* name;
    int64_t value;
    PropertyDeallocator deallocator;
  };

  std::map<const void*, Value> prop_map_;

  std::unique_ptr<SurfaceInfo> surface_info_;

  DISALLOW_COPY_AND_ASSIGN(Window);
};

}  // namespace ui

#endif  // SERVICES_UI_PUBLIC_CPP_WINDOW_H_
