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

#pragma once

#include "attribute.h"

#include "applib/graphics/gtypes.h"
#include "applib/ui/animation.h"
#include "applib/ui/layer.h"
#include "util/uuid.h"
#include "util/time/time.h"

typedef enum {
  LayoutLayerAnchorTextDirectionUp, // for scrolling up, past mode
  LayoutLayerAnchorTextDirectionDown, // for scrolling down, future mode
} LayoutLayerAnchorTextDirection;

//! LayoutLayer is a type of Layer that is used to display templated 3.0 content
//! including \ref TimelineItem (pins, reminders, notifications) as well as AppFaces.
//! LayoutLayers depart from traditional Layers in a few meaningful way.
//! 1) LayoutLayers are modulated by a "mode", which is the context in which the LayoutLayer
//! is displayed. Examples of modes are the "card" mode which displays detailed pin info
//! and the "minimzed" mode which is used to display a "toast" like mode of a pin.
//! 2) LayoutLayers expose three more generic APIs:
//! \ref layout_get_size which returns the size of the
//! content within the layout as well as a generic constructor/destructor:
//!  \ref layout_create / \ref layout_destroy.
//! 3) LayoutLayers are constructed from a set of Attributes which they are meant to display.
//! 4) Sub-types of LayoutLayer are instanciated by summoning the correct type ID rather than by
//! calling a specialized constructor / destructor as per the Layer API.

//! LayoutIds identify the type of a LayoutLayer. They are passed to the constructor to
//! instantiate a specific sub-type of LayoutLayer. Simply stated, the LayoutLayer sub-type
//! informs what kinds of attributes to expect.
typedef enum {
  LayoutIdUnknown = 0, //!< Useful for catching error - 0 is not used as an id.
  LayoutIdGeneric, //!< Generic layout (probably only for testing)
  LayoutIdCalendar, //!< Calendar Pins
  LayoutIdReminder, //!< Generic Reminders
  LayoutIdNotification, //!< Generic Notifications
  LayoutIdCommNotification, //!< Communication Notification
  LayoutIdWeather, //!< Weather Pins
  LayoutIdSports, //!< Sports Pins
  LayoutIdAlarm, //!< Alarm Pins
  LayoutIdHealth, //!< Health Pins
  NumLayoutIds,
  LayoutIdTest, //!< Layout only for unit tests with no attribute requirements
} LayoutId;

typedef struct {
  GColor primary_color;
  GColor secondary_color;
  GColor bg_color;
} LayoutColors;

//! LayoutLayerModes modulate the layout. The mode defines the
//! context in which the layout is displayed.
typedef enum {
  LayoutLayerModeNone = 0,
  LayoutLayerModePeek, //!< Overlay-style mode shown similar to a partially obstructing HUD
  LayoutLayerModePinnedFat, //!< Menu-style mode in the Timeline app (fat, first item)
  LayoutLayerModePinnedThin, //!< Menu-style mode in the Timeline app (thin, second item)
  LayoutLayerModeCard, //!< Card mode, shows details of a TimelineItem
  NumLayoutLayerModes,
} LayoutLayerMode;

//! Forward defined to break define cycle.
struct LayoutLayer;

typedef struct LayoutLayerConfig LayoutLayerConfig;

//! A destructor for a LayoutLayer
//! @param layout the LayoutLayer to destroy
typedef void (*LayoutLayerDestructor)(struct LayoutLayer *layout);

//! A constructor for a LayoutLayer
//! @param frame the frame at which the LayoutLayer should be initialized.
//! @param attributes a pointer to the list of attributes to display in the layout layer.
//! Note that each LayoutLayer type expects a specific set of attributes
//! @param mode the LayoutLayerMode for the context in which the layout is displayed
//! @param context a context pointer
//! @return A pointer to the newly minted LayoutLayer
typedef struct LayoutLayer *(*LayoutLayerConstructor)(const LayoutLayerConfig *config);

//! A verifier for a layout
//! @param existing_attributes array of booleans indicating which attributes exist
//! @return true if the attribute list satisfies the layout requirements, false otherwise
typedef bool (*LayoutVerifier)(bool existing_attributes[]);

#pragma push_macro("GSize")
#undef GSize // [FBO] ugly work around for rogue macro
//! Get the size of the content of a layout. This is defined by the length of the text and
//! the size of the icons contained within the attributes.
//! @param ctx a pointer to the GContext in which the layout is rendered
//! @param layout a pointer to the LayoutLayer
//! @return A GSize describing the size occupied by the LayoutLayer's content
typedef struct GSize (*LayoutLayerSizeGetter)(GContext *ctx, struct LayoutLayer *layout);
#pragma pop_macro("GSize")

typedef void (*LayerLayerModeSetter)(struct LayoutLayer *layout, LayoutLayerMode final_mode);

#if PBL_COLOR
typedef const LayoutColors *(*LayoutLayerColorsGetter)(const struct LayoutLayer *layout);
#endif

typedef void* (*LayoutLayerContextGetter)(struct LayoutLayer *layout);

//! methods for the LayoutLayer type.
typedef struct {
  LayoutLayerSizeGetter size_getter;
  LayoutLayerDestructor destructor;
  LayerLayerModeSetter mode_setter;
#if PBL_COLOR
  LayoutLayerColorsGetter color_getter;
#endif
  LayoutLayerContextGetter context_getter;
} LayoutLayerImpl;

//! Data structure of a LayoutLayer.
typedef struct LayoutLayer {
  Layer layer; //!< The Layer underlying the LayoutLayer
  LayoutLayerMode mode; //!< The mode the LayoutLayer was created with
  AttributeList *attributes; //!< A pointer to the LayoutLayer's Attributes
  const LayoutLayerImpl *impl; //!< The implementation (constructor, destructor, methods)
} LayoutLayer;

struct LayoutLayerConfig {
  const GRect *frame;
  AttributeList *attributes;
  LayoutLayerMode mode;
  const Uuid *app_id;
  void *context;
};

//! Call the correct \ref LayoutLayerConstructor for a given \ref LayoutId
LayoutLayer *layout_create(LayoutId id, const LayoutLayerConfig *config);

//! Verify that the required attributes are there for the layout
bool layout_verify(bool existing_attributes[], LayoutId id);

//! Call the \ref LayoutLayerSizeGetter for a given layout
GSize layout_get_size(GContext *ctx, LayoutLayer *layout);

const LayoutColors *layout_get_colors(const LayoutLayer *layout);
const LayoutColors *layout_get_notification_colors(const LayoutLayer *layout);

Animation *layout_get_animation(LayoutLayer *layout, LayoutLayerMode final_mode);

void layout_set_mode(LayoutLayer *layout, LayoutLayerMode final_mode);

//! Call the \ref LayoutLayerDestructor for a given layout
void layout_destroy(LayoutLayer *layout);

void *layout_get_context(LayoutLayer *layout);
