// Copyright 2013 The Flutter 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 FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_H_

#include <string>
#include <vector>

#include "flutter/fml/macros.h"
#include "flutter/shell/platform/common/alert_platform_node_delegate.h"
#include "flutter/shell/platform/common/geometry.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/direct_manipulation.h"
#include "flutter/shell/platform/windows/flutter_windows_view.h"
#include "flutter/shell/platform/windows/keyboard_manager.h"
#include "flutter/shell/platform/windows/sequential_id_generator.h"
#include "flutter/shell/platform/windows/text_input_manager.h"
#include "flutter/shell/platform/windows/window_binding_handler.h"
#include "flutter/shell/platform/windows/windows_lifecycle_manager.h"
#include "flutter/shell/platform/windows/windows_proc_table.h"
#include "flutter/shell/platform/windows/windowsx_shim.h"
#include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h"
#include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_win.h"
#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
#include "flutter/third_party/accessibility/gfx/native_widget_types.h"

namespace flutter {

// A win32 flutter child window used as implementations for flutter view.  In
// the future, there will likely be a CoreWindow-based FlutterWindow as well.
// At the point may make sense to dependency inject the native window rather
// than inherit.
class FlutterWindow : public KeyboardManager::WindowDelegate,
                      public WindowBindingHandler {
 public:
  // Create flutter Window for use as child window
  FlutterWindow(int width,
                int height,
                std::shared_ptr<WindowsProcTable> windows_proc_table = nullptr,
                std::unique_ptr<TextInputManager> text_input_manager = nullptr);

  virtual ~FlutterWindow();

  // Initializes as a child window with size using |width| and |height| and
  // |title| to identify the windowclass.  Does not show window, window must be
  // parented into window hierarchy by caller.
  void InitializeChild(const char* title,
                       unsigned int width,
                       unsigned int height);

  // |KeyboardManager::WindowDelegate|
  virtual BOOL Win32PeekMessage(LPMSG lpMsg,
                                UINT wMsgFilterMin,
                                UINT wMsgFilterMax,
                                UINT wRemoveMsg) override;

  // |KeyboardManager::WindowDelegate|
  virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) override;

  // |KeyboardManager::WindowDelegate|
  virtual UINT Win32DispatchMessage(UINT Msg,
                                    WPARAM wParam,
                                    LPARAM lParam) override;

  // Called when the DPI changes either when a
  // user drags the window between monitors of differing DPI or when the user
  // manually changes the scale factor.
  virtual void OnDpiScale(unsigned int dpi);

  // Called when a resize occurs.
  virtual void OnResize(unsigned int width, unsigned int height);

  // Called when a paint is requested.
  virtual void OnPaint();

  // Called when the pointer moves within the
  // window bounds.
  virtual void OnPointerMove(double x,
                             double y,
                             FlutterPointerDeviceKind device_kind,
                             int32_t device_id,
                             int modifiers_state);

  // Called when the a mouse button, determined by |button|, goes down.
  virtual void OnPointerDown(double x,
                             double y,
                             FlutterPointerDeviceKind device_kind,
                             int32_t device_id,
                             UINT button);

  // Called when the a mouse button, determined by |button|, goes from
  // down to up
  virtual void OnPointerUp(double x,
                           double y,
                           FlutterPointerDeviceKind device_kind,
                           int32_t device_id,
                           UINT button);

  // Called when the mouse leaves the window.
  virtual void OnPointerLeave(double x,
                              double y,
                              FlutterPointerDeviceKind device_kind,
                              int32_t device_id);

  // |WindowBindingHandlerDelegate|
  virtual void OnText(const std::u16string& text) override;

  // |WindowBindingHandlerDelegate|
  virtual void OnKey(int key,
                     int scancode,
                     int action,
                     char32_t character,
                     bool extended,
                     bool was_down,
                     KeyEventCallback callback) override;

  // Called when IME composing begins.
  virtual void OnComposeBegin();

  // Called when IME composing text is committed.
  virtual void OnComposeCommit();

  // Called when IME composing ends.
  virtual void OnComposeEnd();

  // Called when IME composing text or cursor position changes.
  virtual void OnComposeChange(const std::u16string& text, int cursor_pos);

  // |FlutterWindowBindingHandler|
  virtual void OnCursorRectUpdated(const Rect& rect) override;

  // |FlutterWindowBindingHandler|
  virtual void OnResetImeComposing() override;

  // Called when accessibility support is enabled or disabled.
  virtual void OnUpdateSemanticsEnabled(bool enabled);

  // Called when mouse scrollwheel input occurs.
  virtual void OnScroll(double delta_x,
                        double delta_y,
                        FlutterPointerDeviceKind device_kind,
                        int32_t device_id);

  // Returns the root view accessibility node, or nullptr if none.
  virtual gfx::NativeViewAccessible GetNativeViewAccessible();

  // |FlutterWindowBindingHandler|
  virtual void SetView(WindowBindingHandlerDelegate* view) override;

  // |FlutterWindowBindingHandler|
  virtual HWND GetWindowHandle() override;

  // |FlutterWindowBindingHandler|
  virtual float GetDpiScale() override;

  // |FlutterWindowBindingHandler|
  virtual PhysicalWindowBounds GetPhysicalWindowBounds() override;

  // |FlutterWindowBindingHandler|
  virtual bool OnBitmapSurfaceCleared() override;

  // |FlutterWindowBindingHandler|
  virtual bool OnBitmapSurfaceUpdated(const void* allocation,
                                      size_t row_bytes,
                                      size_t height) override;

  // |FlutterWindowBindingHandler|
  virtual PointerLocation GetPrimaryPointerLocation() override;

  // Called when a theme change message is issued.
  virtual void OnThemeChange();

  // |WindowBindingHandler|
  virtual AlertPlatformNodeDelegate* GetAlertDelegate() override;

  // |WindowBindingHandler|
  virtual ui::AXPlatformNodeWin* GetAlert() override;

  // [WindowBindingHandler]
  virtual bool Focus() override;

  // Called to obtain a pointer to the fragment root delegate.
  virtual ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate();

  // Called on a resize or focus event.
  virtual void OnWindowStateEvent(WindowStateEvent event);

 protected:
  // Base constructor for mocks.
  FlutterWindow();

  // Win32's DefWindowProc.
  //
  // Used as the fallback behavior of HandleMessage. Exposed for dependency
  // injection.
  virtual LRESULT Win32DefWindowProc(HWND hWnd,
                                     UINT Msg,
                                     WPARAM wParam,
                                     LPARAM lParam);

  // Converts a c string to a wide unicode string.
  std::wstring NarrowToWide(const char* source);

  // Processes and route salient window messages for mouse handling,
  // size change and DPI.  Delegates handling of these to member overloads that
  // inheriting classes can handle.
  LRESULT HandleMessage(UINT const message,
                        WPARAM const wparam,
                        LPARAM const lparam) noexcept;

  // Called when the OS requests a COM object.
  //
  // The primary use of this function is to supply Windows with wrapped
  // semantics objects for use by Windows accessibility.
  virtual LRESULT OnGetObject(UINT const message,
                              WPARAM const wparam,
                              LPARAM const lparam);

  // Called when a window is activated in order to configure IME support for
  // multi-step text input.
  virtual void OnImeSetContext(UINT const message,
                               WPARAM const wparam,
                               LPARAM const lparam);

  // Called when multi-step text input begins when using an IME.
  virtual void OnImeStartComposition(UINT const message,
                                     WPARAM const wparam,
                                     LPARAM const lparam);

  // Called when edits/commit of multi-step text input occurs when using an IME.
  virtual void OnImeComposition(UINT const message,
                                WPARAM const wparam,
                                LPARAM const lparam);

  // Called when multi-step text input ends when using an IME.
  virtual void OnImeEndComposition(UINT const message,
                                   WPARAM const wparam,
                                   LPARAM const lparam);

  // Called when the user triggers an IME-specific request such as input
  // reconversion, where an existing input sequence is returned to composing
  // mode to select an alternative candidate conversion.
  virtual void OnImeRequest(UINT const message,
                            WPARAM const wparam,
                            LPARAM const lparam);

  // Called when the app ends IME composing, such as when the text input client
  // is cleared or changed.
  virtual void AbortImeComposing();

  // Called when the cursor rect has been updated.
  //
  // |rect| is in Win32 window coordinates.
  virtual void UpdateCursorRect(const Rect& rect);

  UINT GetCurrentDPI();

  UINT GetCurrentWidth();

  UINT GetCurrentHeight();

  // Returns the current pixel per scroll tick value.
  virtual float GetScrollOffsetMultiplier();

  // Delegate to a alert_node_ used to set the announcement text.
  std::unique_ptr<AlertPlatformNodeDelegate> alert_delegate_;

  // Accessibility node that represents an alert.
  std::unique_ptr<ui::AXPlatformNodeWin> alert_node_;

  // Handles running DirectManipulation on the window to receive trackpad
  // gestures.
  std::unique_ptr<DirectManipulationOwner> direct_manipulation_owner_;

 private:
  // OS callback called by message pump.  Handles the WM_NCCREATE message which
  // is passed when the non-client area is being created and enables automatic
  // non-client DPI scaling so that the non-client area automatically
  // responsponds to changes in DPI.  All other messages are handled by
  // MessageHandler.
  static LRESULT CALLBACK WndProc(HWND const window,
                                  UINT const message,
                                  WPARAM const wparam,
                                  LPARAM const lparam) noexcept;

  // WM_DPICHANGED_BEFOREPARENT defined in more recent Windows
  // SDK
  static const long kWmDpiChangedBeforeParent = 0x02E2;

  // Timer identifier for DirectManipulation gesture polling.
  static const int kDirectManipulationTimer = 1;

  // Release OS resources associated with the window.
  void Destroy();

  // Registers a window class with default style attributes, cursor and
  // icon.
  WNDCLASS RegisterWindowClass(std::wstring& title);

  // Retrieves a class instance pointer for |window|
  static FlutterWindow* GetThisFromHandle(HWND const window) noexcept;

  // Activates tracking for a "mouse leave" event.
  void TrackMouseLeaveEvent(HWND hwnd);

  // Stores new width and height and calls |OnResize| to notify inheritors
  void HandleResize(UINT width, UINT height);

  // Updates the cached scroll_offset_multiplier_ value based off OS settings.
  void UpdateScrollOffsetMultiplier();

  // Creates the ax_fragment_root_, alert_delegate_ and alert_node_ if they do
  // not yet exist.
  // Once set, they are not reset to nullptr.
  void CreateAxFragmentRoot();

  // A pointer to a FlutterWindowsView that can be used to update engine
  // windowing and input state.
  WindowBindingHandlerDelegate* binding_handler_delegate_ = nullptr;

  // The cursor rect set by Flutter.
  RECT cursor_rect_;

  // The window receives resize and focus messages before its view is set, so
  // these values cache the state of the window in the meantime so that the
  // proper application lifecycle state can be updated once the view is set.
  bool restored_ = false;
  bool focused_ = false;

  int current_dpi_ = 0;
  int current_width_ = 0;
  int current_height_ = 0;

  // Holds the conversion factor from lines scrolled to pixels scrolled.
  float scroll_offset_multiplier_;

  // Member variable to hold window handle.
  HWND window_handle_ = nullptr;

  // Member variable to hold the window title.
  std::wstring window_class_name_;

  // Set to true to be notified when the mouse leaves the window.
  bool tracking_mouse_leave_ = false;

  // Keeps track of the last key code produced by a WM_KEYDOWN or WM_SYSKEYDOWN
  // message.
  int keycode_for_char_message_ = 0;

  // Keeps track of the last mouse coordinates by a WM_MOUSEMOVE message.
  double mouse_x_ = 0;
  double mouse_y_ = 0;

  // Generates touch point IDs for touch events.
  SequentialIdGenerator touch_id_generator_;

  // Abstracts Windows APIs that may not be available on all supported versions
  // of Windows.
  std::shared_ptr<WindowsProcTable> windows_proc_table_;

  // Manages IME state.
  std::unique_ptr<TextInputManager> text_input_manager_;

  // Manages IME state.
  std::unique_ptr<KeyboardManager> keyboard_manager_;

  // Used for temporarily storing the WM_TOUCH-provided touch points.
  std::vector<TOUCHINPUT> touch_points_;

  // Implements IRawElementProviderFragmentRoot when UIA is enabled.
  std::unique_ptr<ui::AXFragmentRootWin> ax_fragment_root_;

  // Allow WindowAXFragmentRootDelegate to access protected method.
  friend class WindowAXFragmentRootDelegate;

  FML_DISALLOW_COPY_AND_ASSIGN(FlutterWindow);
};

}  // namespace flutter

#endif  // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_H_
