// 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_WINDOWS_LIFECYCLE_MANAGER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_WINDOWS_LIFECYCLE_MANAGER_H_

#include <Windows.h>

#include <cstdint>
#include <map>
#include <mutex>
#include <optional>
#include <set>

#include "flutter/shell/platform/common/app_lifecycle_state.h"

namespace flutter {

class FlutterWindowsEngine;

/// An event representing a change in window state that may update the
// application lifecycle state.
enum class WindowStateEvent {
  kShow,
  kHide,
  kFocus,
  kUnfocus,
};

/// A manager for lifecycle events of the top-level windows.
///
/// WndProc is called for window messages of the top-level Flutter window.
/// ExternalWindowMessage is called for non-flutter top-level window messages.
/// OnWindowStateEvent is called when the visibility or focus state of a window
///   is changed, including the FlutterView window.
class WindowsLifecycleManager {
 public:
  WindowsLifecycleManager(FlutterWindowsEngine* engine);
  virtual ~WindowsLifecycleManager();

  // Called when the engine is notified it should quit, e.g. by an application
  // call to `exitApplication`. When window is std::nullopt, this quits the
  // application. Otherwise, it holds the HWND of the window that initiated the
  // request, and exit_code is unused.
  virtual void Quit(std::optional<HWND> window,
                    std::optional<WPARAM> wparam,
                    std::optional<LPARAM> lparam,
                    UINT exit_code);

  // Intercept top level window WM_CLOSE message and listen to events that may
  // update the application lifecycle.
  bool WindowProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l, LRESULT* result);

  // Signal to start  sending lifecycle state update messages.
  virtual void BeginProcessingLifecycle();

  // Signal to start consuming WM_CLOSE messages.
  virtual void BeginProcessingExit();

  // Update the app lifecycle state in response to a change in window state.
  // When the app lifecycle state actually changes, this sends a platform
  // message to the framework notifying it of the state change.
  virtual void SetLifecycleState(AppLifecycleState state);

  // Respond to a change in window state.
  // Saves the state for the HWND and schedules UpdateState to be called
  // if it is not already scheduled.
  virtual void OnWindowStateEvent(HWND hwnd, WindowStateEvent event);

  AppLifecycleState GetLifecycleState() { return state_; }

  // Used in tests to wait until the state is updated.
  bool IsUpdateStateScheduled() const { return update_state_scheduled_; }

  // Called by the engine when a non-Flutter window receives an event that may
  // alter the lifecycle state. The logic for external windows must differ from
  // that used for FlutterWindow instances, because:
  // - FlutterWindow does not receive WM_SHOW messages,
  // - When FlutterWindow receives WM_SIZE messages, wparam stores no meaningful
  //   information, whereas it usually indicates the action which changed the
  //   window size.
  // When this returns a result, the message has been consumed and should not be
  // processed further. Currently, it will always return nullopt.
  std::optional<LRESULT> ExternalWindowMessage(HWND hwnd,
                                               UINT message,
                                               WPARAM wparam,
                                               LPARAM lparam);

 protected:
  // Check the number of top-level windows associated with this process, and
  // return true only if there are 1 or fewer.
  virtual bool IsLastWindowOfProcess();

  virtual void DispatchMessage(HWND window,
                               UINT msg,
                               WPARAM wparam,
                               LPARAM lparam);

 private:
  // Pass top-level window close notifications to the application lifecycle
  // logic. If the last window of the process receives WM_CLOSE and a listener
  // is registered for WidgetsBindingObserver.didRequestAppExit, the message is
  // sent to the framework to query whether the application should be allowed
  // to quit.
  bool HandleCloseMessage(HWND hwnd, WPARAM wparam, LPARAM lparam);

  FlutterWindowsEngine* engine_;

  std::map<std::tuple<HWND, WPARAM, LPARAM>, int> sent_close_messages_;

  bool process_lifecycle_ = false;
  bool process_exit_ = false;

  std::set<HWND> visible_windows_;
  std::set<HWND> focused_windows_;

  // Transitions the application state. If any windows are focused,
  // the application is considered resumed. If no windows are focused
  // but there are visible windows, application is considered inactive.
  // Otherwise, if there are no visible window, application is considered
  // hidden.
  void UpdateState();

  // Whether update state is scheduled to be called in next run loop turn.
  // This is needed to provide atomic updates of the state.
  bool update_state_scheduled_ = false;

  AppLifecycleState state_ = AppLifecycleState::kDetached;
};

}  // namespace flutter

#endif  // FLUTTER_SHELL_PLATFORM_WINDOWS_WINDOWS_LIFECYCLE_MANAGER_H_
