/* ========================================================================= */
/**
 * @file window.h
 *
 * @copyright
 * Copyright (c) 2026 Philipp Kaeser (kaeser@gubbe.ch)
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef __WLMTK_WINDOW2_H__
#define __WLMTK_WINDOW2_H__

#include <libbase/libbase.h>
#include <libbase/plist.h>
#include <stdbool.h>
#include <stdint.h>
#include <wayland-server-core.h>

struct wlr_output;

/** Forward declaration: Window. */
typedef struct _wlmtk_window_t wlmtk_window_t;

/** Reference to the window's style. */
typedef struct _wlmtk_window_style_ref_t wlmtk_window_style_ref_t;

#include "element.h"
#include "menu.h"
#include "resizebar.h"  // IWYU pragma: keep
#include "style.h"
#include "util.h"
#include "workspace.h"  // IWYU pragma: keep

#ifdef __cplusplus
extern "C" {
#endif  // __cplusplus

/** Signals available for the @ref wlmtk_window_t class. */
typedef struct {
    /**
     * Signals that the window state (maximize, iconify, ...) changed.
     *
     * Window state can be retrieved from:
     * - @ref wlmtk_window_is_maximized
     * - @ref wlmtk_window_is_fullscreen
     * - @ref wlmtk_window_is_shaded
     *
     * The signal is also raised when the window's workspace is changed.
     * Retrieve through @ref wlmtk_window_get_workspace.
     *
     * data_ptr points to the window state (@ref wlmtk_window_t).
     */
    struct wl_signal          state_changed;

    /** Signals that @ref wlmtk_window_t::activated changed. */
    struct wl_signal          set_activated;

    /**
     * Signals that the window was requested to be closed.
     *
     * Applies only to windows with @ref WLMTK_WINDOW_PROPERTY_CLOSABLE.
     */
    struct wl_signal          request_close;

    /**
     * Signals that the window's size is requested to change.
     *
     * Takes a `struct wlr_box*` as argument.
     */
    struct wl_signal          request_size;

    /**
     * Signals that the window is desiring to switch to fullscreen.
     *
     * Takes a `bool` as argument, specifying whether to enable fullscreen.
     */
    struct wl_signal          request_fullscreen;

    /**
     * Signals that the window is desiring to switch to maximized.
     *
     * Takes a `bool` as argument, specifying whether to enable maximized.
     */
    struct wl_signal          request_maximized;
} wlmtk_window_events_t;

/** Style options for the window. */
struct wlmtk_window_style {
    /** The titlebar's style. */
    struct wlmtk_titlebar_style titlebar;
    /** The resizebar's style. */
    struct wlmtk_resizebar_style resizebar;
    /** Style of the window border. */
    struct wlmtk_margin_style border;
    /** Style of the margins between titlebar, window and resizebar. */
    struct wlmtk_margin_style margin;
};

/** Window properties. */
typedef enum {
    /** Can be resized. Server-side decorations will show resize-bar. */
    WLMTK_WINDOW_PROPERTY_RESIZABLE = UINT32_C(1) << 0,
    /** Can be iconified. Server-side decorations include icnonify button. */
    WLMTK_WINDOW_PROPERTY_ICONIFIABLE = UINT32_C(1) << 1,
    /** Can be closed. Server-side decorations include close button. */
    WLMTK_WINDOW_PROPERTY_CLOSABLE = UINT32_C(1) << 2,

    /**
     * Kludge: a window that closes on right-click-release.
     * The window's element must pointer_grab.
     * TODO(kaeser@gubbe.ch): This should be... better.
     */
    WLMTK_WINDOW_PROPERTY_RIGHTCLICK = UINT32_C(1) << 3
} wlmtk_window_property_t;

/**
 * Creates a window.
 *
 * @param content_element_ptr
 * @param style_ref_ptr
 * @param menu_style_ref_ptr
 *
 * @return The window handle, or NULL on error.
 */
wlmtk_window_t *wlmtk_window_create(
    wlmtk_element_t *content_element_ptr,
    wlmtk_window_style_ref_t *style_ref_ptr,
    wlmtk_menu_style_ref_t *menu_style_ref_ptr);

/**
 * Destroys the window.
 *
 * @param window_ptr
 */
void wlmtk_window_destroy(wlmtk_window_t *window_ptr);

/**
 * Gets the set of events available to a window, for binding listeners.
 *
 * @param window_ptr
 *
 * @return Pointer to this window's @ref wlmtk_window_t::events.
 */
wlmtk_window_events_t *wlmtk_window_events(wlmtk_window_t *window_ptr);

/**
 * Returns the element for adding the window into a container.
 *
 * @param window_ptr
 *
 * @return Pointer to the super element of @ref wlmtk_window_t::bordered.
 */
wlmtk_element_t *wlmtk_window_element(wlmtk_window_t *window_ptr);

/**
 * Returns the window, where `element_ptr` is @ref wlmtk_window_element.
 *
 * @param element_ptr
 *
 * @return the @ref wlmtk_window_t from the element pointer.
 */
wlmtk_window_t *wlmtk_window_from_element(wlmtk_element_t *element_ptr);

/**
 * Returns the bounding box for the window.
 *
 * @param window_ptr
 *
 * @return the box.
 */
struct wlr_box wlmtk_window_get_bounding_box(wlmtk_window_t *window_ptr);

/** Helper: Indicates that the position has changed. */
void wlmtk_window_position_changed(wlmtk_window_t *window_ptr);

/**
 * Sets properties for the window.
 *
 * @param window_ptr
 * @param properties          @see wlmtk_window_property_t.
 */
void wlmtk_window_set_properties(
    wlmtk_window_t *window_ptr,
    uint32_t properties);

/**
 * Sets client information for the window.
 *
 * @param window_ptr
 * @param client_ptr          The client's information will be copied into
 *                            @ref wlmtk_window_t::client, and does not need
 *                            to outlive this call.
 */
void wlmtk_window_set_client(
    wlmtk_window_t *window_ptr,
    const wlmtk_util_client_t *client_ptr);

/**
 * Returns a pointer to @ref wlmtk_window_t::client.
 *
 * @param window_ptr
 *
 * @return Client information. Remains walid until `window_ptr` is destroyed.
 */
const wlmtk_util_client_t *wlmtk_window_get_client_ptr(
    wlmtk_window_t *window_ptr);

/**
 * Sets the WLR output for the window. Used for fullscreen requests.
 *
 * @param window_ptr
 * @param wlr_output_ptr      Output to consider when requesting a window as
 *                            fullscreen. Can be NULL to indicate no preference.
 */
void wlmtk_window_set_wlr_output(
    wlmtk_window_t *window_ptr,
    struct wlr_output *wlr_output_ptr);

/**
 * Gets the struct wlr_output that the window prefers, or is on.
 *
 * @param window_ptr
 *
 * @return Pointer to the struct wlr_output the center of the window is placed
 *     on, or NULL if none is available or the window is not mapped.
 */
struct wlr_output *wlmtk_window_get_wlr_output(
    wlmtk_window_t *window_ptr);

/**
 * Sets the title for the window.
 *
 * If `title_ptr` is NULL, a generic name is set.
 *
 * @param window_ptr
 * @param title_ptr           May be NULL.
 *
 * @return true on success.
 */
bool wlmtk_window_set_title(
    wlmtk_window_t *window_ptr,
    const char *title_ptr);

/**
 * Returns the title of the window.
 *
 * @param window_ptr
 *
 * @returns Pointer to the window title. Will remain valid until the next call
 *     to @ref wlmtk_window_set_title, or until the window is destroyed. Will
 *     never be NULL.
 */
const char *wlmtk_window_get_title(wlmtk_window_t *window_ptr);

/**
 * Sets the window as activated, depending on the argument's value.
 *
 * An activated window will have keyboard focus and would have distinct
 * decorations to indicate state.
 *
 * @param window_ptr
 * @param activated
 */
void wlmtk_window_set_activated(
    wlmtk_window_t *window_ptr,
    bool activated);

/** @return whether the window is currently activated. */
bool wlmtk_window_is_activated(wlmtk_window_t *window_ptr);

/**
 * Requests the window's size to be updated.
 *
 * @param window_ptr
 * @param box_ptr             Only the `width` and `height` are considered.
 */
void wlmtk_window_request_size(
    wlmtk_window_t *window_ptr,
    const struct wlr_box *box_ptr);

/**
 * Sets the window's committed size. Should be called when the content size
 * is changed, eg. on surface commit.
 *
 * @param window_ptr
 * @param width
 * @param height
 */
void wlmtk_window_commit_size(
    wlmtk_window_t *window_ptr,
    int width, int height);

/**
 * Returns the window's (committed) size. That may be different than the
 * dimensions of the window's @ref wlmtk_window_t::content_element_ptr.
 *
 * @param window_ptr
 *
 * @return The size box. Only `width` and `height` will be used.
 */
struct wlr_box wlmtk_window_get_size(wlmtk_window_t *window_ptr);

/**
 * Sets the resizing edges. Keeps the opposite edges firm when resizing.
 *
 * @param window_ptr
 * @param edges
 */
void wlmtk_window_set_resize_edges(
    wlmtk_window_t *window_ptr,
    uint32_t edges);

/** @return resizing edges. */
uint32_t wlmtk_window_get_resize_edges(wlmtk_window_t *window_ptr);

/**
 * Requests to close the window.
 *
 * @param window_ptr
 */
void wlmtk_window_request_close(wlmtk_window_t *window_ptr);

/**
 * Requests to minimize (iconify) the window.
 *
 * @param window_ptr
 */
void wlmtk_window_request_minimize(wlmtk_window_t *window_ptr);

/**
 * Requests the window to be fullscreen (or end fullscreen).
 *
 * @param window_ptr
 * @param fullscreen
 */
void wlmtk_window_request_fullscreen(wlmtk_window_t *window_ptr, bool fullscreen);

/** @return whether the window currently is in fullscreen mode. */
bool wlmtk_window_is_fullscreen(wlmtk_window_t *window_ptr);

/**
 * Commits the window as fullscreen: Client has comitted the surface in
 * fullscreen state.
 *
 * @param window_ptr
 * @param fullscreen
 */
void wlmtk_window_commit_fullscreen(
    wlmtk_window_t *window_ptr,
    bool fullscreen);

/**
 * Requests the window to be maximized (or end maximized).
 *
 * Guarantees to emit a @ref wlmtk_window_events_t::request_maximized signal,
 * either with the provided `maximized` value, or with what the compositor
 * deems acceptable.
 *
 * @param window_ptr
 * @param maximized
 */
void wlmtk_window_request_maximized(
    wlmtk_window_t *window_ptr,
    bool maximized);

/** @return whether the window currently is in maximized mode. */
bool wlmtk_window_is_maximized(wlmtk_window_t *window_ptr);

/**
 * Commits the `maximized` mode for the window.
 *
 * The call is idempotent: Once the window is committed, further calls with
 * the same `maximized` value will return straight away.
 *
 * @param window_ptr
 * @param maximized
 */
void wlmtk_window_commit_maximized(
    wlmtk_window_t *window_ptr,
    bool maximized);

/**
 * Requests the window to be shaded (rolled up) or not.
 *
 * @param window_ptr
 * @param shaded
 */
void wlmtk_window_request_shaded(wlmtk_window_t *window_ptr, bool shaded);

/** @return whether the window currently is shaded. */
bool wlmtk_window_is_shaded(wlmtk_window_t *window_ptr);

/**
 * En-/Disables the window menu.
 *
 * @param window_ptr
 * @param enabled
 */
void wlmtk_window_menu_set_enabled(wlmtk_window_t *window_ptr, bool enabled);

/**
 * Returns a pointer to the window menu's state.
 *
 * @param window_ptr
 *
 * @return A pointer to the @ref wlmtk_menu_t of the window menu.
 */
wlmtk_menu_t *wlmtk_window_menu(wlmtk_window_t *window_ptr);

/**
 * Sets whether to have server-side decorations for this window.
 *
 * @param window_ptr
 * @param decorated
 */
void wlmtk_window_set_server_side_decorated(
    wlmtk_window_t *window_ptr,
    bool decorated);

/**
 * Sets window options (currently: the modifier to move when dragging).
 *
 * @param window_ptr
 * @param move_modifier
 */
void wlmtk_window_set_options(
    wlmtk_window_t *window_ptr,
    uint32_t move_modifier);

/**
 * Sets @ref wlmtk_window_t::workspace_ptr.
 *
 * Protected method, to be called only from @ref wlmtk_workspace_t.
 *
 * @param window_ptr
 * @param workspace_ptr
 */
void wlmtk_window_set_workspace(
    wlmtk_window_t *window_ptr,
    wlmtk_workspace_t *workspace_ptr);

/** @return The value of @ref wlmtk_window_t::workspace_ptr. */
wlmtk_workspace_t *wlmtk_window_get_workspace(wlmtk_window_t *window_ptr);

/** @return pointer to @ref wlmtk_window_t::dlnode. */
bs_dllist_node_t *wlmtk_dlnode_from_window(wlmtk_window_t *window_ptr);
/** @return the @ref wlmtk_window_t for @ref wlmtk_window_t::dlnode. */
wlmtk_window_t *wlmtk_window_from_dlnode(bs_dllist_node_t *dlnode_ptr);

/**
 * Applies the style held by the reference.
 *
 * @param window_ptr
 * @param style_ref_ptr
 * @param menu_style_ref_ptr
 *
 * @return true on success.
 */
bool wlmtk_window_set_style(
    wlmtk_window_t *window_ptr,
    wlmtk_window_style_ref_t *style_ref_ptr,
    wlmtk_menu_style_ref_t *menu_style_ref_ptr);

/** Creates a holder for the window style, with initialized reference.
 *
 * @return Pointer to @ref wlmtk_window_style. To destroy, call
 *     @ref wlmtk_window_style_ref_release on @ref wlmtk_window_style_to_ref.
 */
struct wlmtk_window_style *wlmtk_window_style_create(void);

/** @return the @ref wlmtk_window_style_ref_t for the style. */
wlmtk_window_style_ref_t *wlmtk_window_style_to_ref(
    struct wlmtk_window_style *window_style_ptr);

/** Retains a reference. @return a read-only style. */
const struct wlmtk_window_style *wlmtk_window_style_ref_retain(
    wlmtk_window_style_ref_t *ref_ptr);
/** Releases a reference. */
void wlmtk_window_style_ref_release(wlmtk_window_style_ref_t *ref_ptr);

/** Decodes dict at `object_ptr` into @ref wlmtk_window_style at `value_ptr` */
bool wlmtk_window_style_decode(
    bspl_object_t *object_ptr,
   const union bspl_desc_value *desc_value_ptr,
    void *value_ptr);
/** Initializer. */
bool wlmtk_window_style_decode_init(void *dst_ptr);
/** Fini. */
void wlmtk_window_style_decode_fini(void *dst_ptr);

/** Window unit test cases. */
extern const bs_test_set_t wlmtk_window_test_set;

/**
 * Creates a window, with default styles, for testing.
 *
 * @param content_element_ptr
 *
 * @return See @ref wlmtk_window_create.
 */
wlmtk_window_t *wlmtk_test_window_create(wlmtk_element_t *content_element_ptr);

#ifdef __cplusplus
}  // extern "C"
#endif  // __cplusplus

#endif /* __WLMTK_WINDOW2_H__ */
/* == End of window.h ===================================================== */
