/* SPDX-FileCopyrightText: 2024 Google LLC */
/* SPDX-License-Identifier: Apache-2.0 */

#pragma once

#include "click.h"

#include "applib/graphics/gtypes.h"
#include "applib/ui/layer.h"
#include "applib/ui/recognizer/recognizer.h"
#include "applib/ui/recognizer/recognizer_manager.h"

struct Window;
struct WindowStack;

//! @file window.h
//! @addtogroup UI
//! @{
//!   @addtogroup Window
//! \brief The basic building block of the user interface
//!
//! Windows are the top-level elements in the UI hierarchy and the basic building blocks for a Pebble
//! UI. A single window is always displayed at a time on Pebble, with the exception of when animating
//! from one window to the other, which, in that case, is managed by the window stack. You can stack
//! windows on top of each other, but only the topmost window will be visible.
//!
//! Users wearing a Pebble typically interact with the content and media displayed in a window, clicking
//! and pressing buttons on the watch, depending on what they see and wish to respond to in a window.
//!
//! Windows serve to display a hierarchy of layers on the screen and handle user input. When a window is
//! visible, its root Layer (and all its child layers) are drawn onto the screen automatically.
//!
//! You need a window, which always fills the entire screen, to display images, text, and graphics in
//! your Pebble app. A layer by itself doesn’t display on Pebble; it must be in the current window’s
//! layer hierarchy to be visible.
//!
//! The Window Stack serves as the global manager of what window is presented and makes sure that input
//! events are forwarded to the topmost window.
//!
//! Refer to the \htmlinclude UiFramework.html (chapter "Window") for a conceptual
//! overview of Window, the Window Stack and relevant code examples.
//!   @{


//! Function signature for a handler that deals with transition events of a window.
//! @see WindowHandlers
//! @see \ref window_set_window_handlers()
typedef void (*WindowHandler)(struct Window *window);

//! WindowHandlers
//! These handlers are called by the \ref WindowStack as windows get pushed on / popped.
//! All these handlers use \ref WindowHandler as their function signature.
//! @see \ref window_set_window_handlers()
//! @see \ref WindowStack
typedef struct WindowHandlers {
  //! Called when the window is pushed to the screen when it's not loaded.
  //! This is a good moment to do the layout of the window.
  WindowHandler load;

  //! Called when the window comes on the screen (again). E.g. when
  //! second-top-most window gets revealed (again) after popping the top-most
  //! window, but also when the window is pushed for the first time. This is a
  //! good moment to start timers related to the window, or reset the UI, etc.
  WindowHandler appear;

  //! Called when the window leaves the screen, e.g. when another window
  //! is pushed, or this window is popped. Good moment to stop timers related
  //! to the window.
  WindowHandler disappear;

  //! Called when the window is deinited, but could be used in the future to
  //! free resources bound to windows that are not on screen.
  WindowHandler unload;
} WindowHandlers;

//! Data structure of a window.
typedef struct Window {
  //! The root layer of the window.
  //! By default, the `.update_proc` will draw the background color of the window.
  Layer layer;

  //! The handlers that are called by the system whenever there are window transitions
  //! happening (load, appear, disappear and unload).
  //! @see \ref window_set_window_handlers()
  WindowHandlers window_handlers;

  //! The callback that will be called by the system to get the ClickRecognizer s
  //! set up for this window.
  //! @see \ref window_set_click_config_provider()
  //! @see \ref window_set_click_config_provider_with_context()
  ClickConfigProvider click_config_provider;

  //! Pointer to application specific data that will be passed into the
  //! `click_config_provider` callback.
  //! @see \ref window_set_click_config_provider_with_context()
  void *click_config_context;

  //! @internal
  //! Pointer to application specific data that the app can assign to a window.
  //! @see \ref window_set_user_data() and \ref window_get_user_data()
  void *user_data;

  //! The background color that will be used to fill the background of the window.
  //! @see \ref window_set_background_color()
  GColor8 background_color;

  bool is_render_scheduled:1;
  bool on_screen:1;
  bool is_loaded:1;
  bool overrides_back_button:1;
  bool is_fullscreen:1;
  bool in_click_config_provider:1;

  //! @internal
  //! If a click config provider was changed while the window was covered by a modal,
  //! this flag is used to indicate that it should be called when uncovered.
  bool is_waiting_for_click_config:1;

  //! @internal
  //! If the window has configured its click config provider. This flag is used to automatically
  //! click configure a window if a modal from above relinquishes focus either by going off screen
  //! or becoming unfocusable. This is necessary because windows can be unfocusable, thus whether
  //! they are on screen does not indicate whether their click has been configured.
  bool is_click_configured:1;

  //! @internal
  //! If the window can visually expose window stacks below it. This property decides whether the
  //! window stack would expose window stacks below it. A window stack is transparent if the top
  //! window in the window stack is transparent. Windows that are not the top window in their
  //! respective window stack are never shown, therefore transparent windows can only expose
  //! the top windows of window stacks below it, not windows within the same window stack.
  //! @note Currently, the app window stack is the lowest stack, is_transparent has no affect on
  //! app windows.
  bool is_transparent:1;

  //! @internal
  //! If the window passes input to the next window stack with a top focusable window. A window
  //! stack is unfocusable if the top window in the window stack is unfocusable. Windows that are
  //! not the top window in their respective window stack never receive input, therefore
  //! unfocusable windows can only pass input to the top windows of window stacks below it, not
  //! windows within the same window stack.
  bool is_unfocusable:1;

  //! @internal
  //! Back pointer to the window stack that this Window is residing on.
  //! @see \ref WindowStack
  struct WindowStack *parent_window_stack;

  const char* debug_name;
} Window;

#ifdef RELEASE
#define WINDOW_NAME(x) ""
#else
#define WINDOW_NAME(x) x
#endif

//! Initializes a window and resets its members to the default values:
//!
//! * Background color : `GColorWhite`
//! * Root layer's `update_proc` : function that fills the window's background using `background_color`.
//! * `click_config_provider` : `NULL`
//! * `window_handlers` : all `NULL`
//! @param window The window to initialize
//! @param debug_name The window's debug name
void window_init(Window *window, const char* debug_name);

//! Creates a new Window on the heap and initalizes it with the default values.
//!
//! * Background color : `GColorWhite`
//! * Root layer's `update_proc` : function that fills the window's background using `background_color`.
//! * `click_config_provider` : `NULL`
//! * `window_handlers` : all `NULL`
//! @return A pointer to the window. `NULL` if the window could not
//! be created
Window* window_create(void);

//! Destroys a Window previously created by window_create.
void window_destroy(Window* window);

//! Deinitializes the window.
//! Removes the window from the screen, removes its child layers from the root layer and
//! finally calls the `.unload` window handler.
//! @note This function removes the window from the screen, even though it might still
//! be presented. Normally, the window stack will call this function automatically
//! after a window has been popped (removed) from the window stack.
//! @param window The window to deinitialize
void window_deinit(Window *window);

//! Sets the click configuration provider callback function on the window.
//! This will automatically setup the input handlers of the window as well to use
//! the click recognizer subsystem.
//! @param window The window for which to set the click config provider
//! @param click_config_provider The callback that will be called to configure the click recognizers with the window
//! @see Clicks
//! @see ClickConfigProvider
void window_set_click_config_provider(Window *window, ClickConfigProvider click_config_provider);

//! Same as window_set_click_config_provider(), but will assign a custom context pointer
//! (instead of the window pointer) that will be passed into the ClickHandler click event handlers.
//! @param window The window for which to set the click config provider
//! @param click_config_provider The callback that will be called to configure the click recognizers with the window
//! @param context Pointer to application specific data that will be passed to the click configuration provider callback (defaults to the window).
//! @see Clicks
//! @see window_set_click_config_provider
void window_set_click_config_provider_with_context(Window *window, ClickConfigProvider click_config_provider, void *context);

//! Set the context that will be passed to handlers for the given button's events. By default the context passed to handlers
//! is equal to the \ref ClickConfigProvider context (defaults to the window).
//! @note Must be called from within the \ref ClickConfigProvider.
//! @param button_id The button to set the context for.
//! @param context Set the context that will be passed to handlers for the given button's events.
void window_set_click_context(ButtonId button_id, void *context);

//! Subscribe to single click events.
//! @note Must be called from the \ref ClickConfigProvider.
//! @note \ref window_single_click_subscribe() and \ref window_single_repeating_click_subscribe() conflict, and cannot both be used on the same button.
//! @note When there is a multi_click and/or long_click setup, there will be a delay before the single click
//! @param button_id The button events to subscribe to.
//! @param handler The \ref ClickHandler to fire on this event.
//! handler will get fired. On the other hand, when there is no multi_click nor long_click setup, the single click handler will fire directly on button down.
//! @see ButtonId
//! @see Clicks
//! @see window_single_repeating_click_subscribe
void window_single_click_subscribe(ButtonId button_id, ClickHandler handler);

//! Subscribe to single click event, with a repeat interval. A single click is detected every time "repeat_interval_ms" has been reached.
//! @note Must be called from the \ref ClickConfigProvider.
//! @note \ref window_single_click_subscribe() and \ref window_single_repeating_click_subscribe() conflict, and cannot both be used on the same button.
//! @note The back button cannot be overridden with a repeating click.
//! @param button_id The button events to subscribe to.
//! @param repeat_interval_ms When holding down, how many milliseconds before the handler is fired again.
//! A value of 0ms means "no repeat timer". The minimum is 30ms, and values below will be disregarded.
//! If there is a long-click handler subscribed on this button, `repeat_interval_ms` will not be used.
//! @param handler The \ref ClickHandler to fire on this event.
//! @see window_single_click_subscribe
void window_single_repeating_click_subscribe(ButtonId button_id, uint16_t repeat_interval_ms, ClickHandler handler);

//! Subscribe to multi click events.
//! @note Must be called from the \ref ClickConfigProvider.
//! @param button_id The button events to subscribe to.
//! @param min_clicks Minimum number of clicks before handler is fired. Defaults to 2.
//! @param max_clicks Maximum number of clicks after which the click counter is reset. A value of 0 means use "min" also as "max".
//! @param timeout The delay after which a sequence of clicks is considered finished, and the click counter is reset. A value of 0 means to use the system default 300ms.
//! @param last_click_only Defaults to false. When true, only the handler for the last multi-click is called.
//! @param handler The \ref ClickHandler to fire on this event. Fired for multi-clicks, as "filtered" by the `last_click_only`, `min`, and `max` parameters.
void window_multi_click_subscribe(ButtonId button_id, uint8_t min_clicks, uint8_t max_clicks, uint16_t timeout, bool last_click_only, ClickHandler handler);

//! Subscribe to long click events.
//! @note Must be called from the \ref ClickConfigProvider.
//! @note The back button cannot be overridden with a long click.
//! @param button_id The button events to subscribe to.
//! @param delay_ms Milliseconds after which "handler" is fired. A value of 0 means to use the system default 500ms.
//! @param down_handler The \ref ClickHandler to fire as soon as the button has been held for `delay_ms`. This may be NULL to have no down handler.
//! @param up_handler The \ref ClickHandler to fire on the release of a long click. This may be NULL to have no up handler.
void window_long_click_subscribe(ButtonId button_id, uint16_t delay_ms, ClickHandler down_handler, ClickHandler up_handler);

//! Subscribe to raw click events.
//! @note Must be called from within the \ref ClickConfigProvider.
//! @note The back button cannot be overridden with a raw click.
//! @param button_id The button events to subscribe to.
//! @param down_handler The \ref ClickHandler to fire as soon as the button has been pressed. This may be NULL to have no down handler.
//! @param up_handler The \ref ClickHandler to fire on the release of the button. This may be NULL to have no up handler.
//! @param context If this context is not NULL, it will override the general context.
void window_raw_click_subscribe(ButtonId button_id, ClickHandler down_handler, ClickHandler up_handler, void *context);

//! Gets the current click configuration provider of the window.
//! @param window The window for which to get the click config provider
ClickConfigProvider window_get_click_config_provider(const Window *window);

//! Gets the current click configuration provider context of the window.
//! @param window The window for which to get the click config provider context
void *window_get_click_config_context(Window *window);

//! @internal
//! This function replaces \ref window_set_window_handlers_by_value in order to change the handlers
//! parameter to be passed by a pointer instead of being passed by value. Callers consume much less
//! code space when passing a pointer compared to passing structs by value.
//! @see window_set_window_handlers_by_value
void window_set_window_handlers(Window *window, const WindowHandlers *handlers);

//! Sets the window handlers of the window.
//! These handlers get called e.g. when the user enters or leaves the window.
//! @param window The window for which to set the window handlers
//! @param handlers The handlers for the specified window
//! @see \ref WindowHandlers
void window_set_window_handlers_by_value(Window *window, WindowHandlers handlers);

//! Sets a pointer to developer-supplied data that the window uses, to
//! provide a means to access the data at later times in one of the window event handlers.
//! @see window_get_user_data
//! @param window The window for which to set the user data
//! @param data A pointer to user data.
void window_set_user_data(Window *window, void *data);

//! Gets the pointer to developer-supplied data that was previously
//! set using window_set_user_data().
//! @see window_set_user_data
//! @param window The window for which to get the user data
void* window_get_user_data(const Window *window);

//! Gets the root Layer of the window.
//! The root layer is the layer at the bottom of the layer hierarchy for this window.
//! It is the window's "canvas" if you will. By default, the root layer only draws
//! a solid fill with the window's background color.
//! @param window The window for which to get the root layer
//! @return The window's root layer
struct Layer* window_get_root_layer(const Window *window);

//! Sets the background color of the window, which is drawn automatically by the
//! root layer of the window.
//! @param window The window for which to set the background color
//! @param background_color The new background color
//! @see \ref window_get_root_layer()
void window_set_background_color(Window *window, GColor background_color);
void window_set_background_color_2bit(Window *window, GColor2 background_color);

//! Sets whether or not the window is fullscreen, consequently hiding the sytem status bar.
//! @note This needs to be called before pushing a window to the window stack.
//! @param window The window for which to set its full-screen property
//! @param enabled True to make the window full-screen or false to leave space for the system status bar.
//! @see \ref window_get_fullscreen()
void window_set_fullscreen(Window *window, bool enabled);

//! Gets whether the window is full-screen, consequently hiding the sytem status bar.
//! @param window The window for which to get its full-screen property
//! @return True if the window is marked as fullscreen, false if it is not marked as fullscreen.
bool window_get_fullscreen(const Window *window);

//! Assigns an icon (max. 16x16 pixels) that can be displayed in the system status bar.
//! When no icon is assigned, the icon of the previous window on the window stack is used.
//! @note This needs to be called before pushing a window to the window stack.
//! @param window The window for which to set the status bar icon
//! @param icon The new status bar icon
void window_set_status_bar_icon(Window *window, const GBitmap *icon);

//! @internal
//! Internal window layer update proc to be called in window subclass layer update procs
void window_do_layer_update_proc(Layer *window, GContext* ctx);

//! @internal
//! This function gets called by the default render event handler.
//! When implementing a custom render event handler, call this function
//! to render the window into the context that gets passed in.
//! @see PebbleAppRenderEventHandler
//! @param window The window to render
void window_render(Window *window, GContext *ctx);

//! @internal
//! Returns true if the window is currently on top of the window stack, thus on screen.
//! @param window The window for which to set the `on_screen` boolean
bool window_is_on_screen(Window *window);

//! Gets whether the window has been loaded.
//! If a window is loaded, its `.load` handler has been called (and the `.unload` handler
//! has not been called since).
//! @return true if the window is currently loaded or false if not.
//! @param window The window to query its loaded status
//! @see \ref WindowHandlers
bool window_is_loaded(Window *window) ;

//! @internal
//! Sets whether a window is transparent. Transparent windows that are at the top of their stack
//! allow the next visible window stack beneath to show through transparent areas.
//! @note This currently has no affect on App windows.
void window_set_transparent(Window *window, bool transparent);

//! @internal
//! @return true if the window is transparent, otherwise false
bool window_is_transparent(Window *window);

//! @internal
//! Sets whether a window is focusable (e.g. can receive button events). Windows that are not
//! focusable that are at the top of their stack allow the next window stack beneath to receive
//! focus instead.
//! @note This is not recommended for use on App windows as it results in undefined behavior.
void window_set_focusable(Window *window, bool focusable);

//! @internal
//! @return true if the window is focusable, otherwise false
bool window_is_focusable(Window *window);

//! @internal
//! @return A name used to identify the window. If RELEASE is defined will just return "?"
//! @param window The window for which to get the debug name
const char* window_get_debug_name(Window *window);

//! @internal
void window_call_click_config_provider(Window *window, void *context);

//! Attach a recognizer to the window
//! @param window \ref Window to which to attach the \ref Recognizer
//! @param recognizer \ref Recognizer to attach
void window_attach_recognizer(Window *window, Recognizer *recognizer);

//! Detach a recognizer from the window
//! @param window \ref Window from which to detach the \ref Recognizer
//! @param recognizer \ref Recognizer to detach
void window_detach_recognizer(Window *window, Recognizer *recognizer);

//! Get the recognizers attached to a window
//! @param window \ref Window from which to get recognizers
//! @return recognizer list
RecognizerList *window_get_recognizer_list(Window *window);

//! Get the recognizer manager that manages recognizers attached to this window and all layers
//! attached to the window
RecognizerManager *window_get_recognizer_manager(Window *window);

//!   @} // end addtogroup Window
//! @} // end addtogroup UI
