#include <stddef.h>
#include <string.h>

#include "page_manager.h"
#include "../key.h"

/* 默认全局页面管理器实例（便于主流程直接使用） */
static page_manager_t s_pageManager;

/* -------------------------------------------------------------------------
 * 函数名: PageManager_GlobalEventHandle
 * 功能:
 *   页面管理器的全局事件兜底处理入口，用于处理未被页面消费的通用按键逻辑。
 *
 * 参数:
 *   event - 输入事件指针
 *
 * 边界处理:
 *   - 若 event 为 NULL，函数直接返回。
 *   - 若事件类型不是 PAGE_EVENT_KEY，函数直接返回，不做处理。
 *   - 仅当 keyVal 为 KEY_ESC 时触发返回上一页，其它按键忽略。
 *
 * 说明:
 *   - 该函数通常由 PageManager_DispatchEvent 在页面返回
 *     EVENT_UNHANDLED 时调用。
 *   - 当前全局策略为“ESC = 返回”：内部通过 PageManager_Pop()
 *     执行页面栈回退。
 *   - 设计目标是在不侵入页面私有逻辑的前提下，提供统一系统级快捷行为。
 *
 * 返回值:
 *   - 无
 * ------------------------------------------------------------------------- */
static void PageManager_GlobalEventHandle(input_event_t *event)
{
    ASSERT((event == NULL) || (event->type != PAGE_EVENT_KEY));
    if ((event == NULL) || (event->type != PAGE_EVENT_KEY))
    {
        return;
    }

    if (event->keyVal == KEY_ESC)
    {
        (void)PageManager_Pop();
    }
}

/* -------------------------------------------------------------------------
 * 函数名: PageManager_Init
 * 功能:
 *   初始化单例页面管理器 s_pageManager，使其进入“空栈、可注册”的初始状态。
 *
 * 参数:
 *   无
 *
 * 边界处理:
 *   - 本函数无外部入参，不涉及空指针校验。
 *   - 通过 memset 全量清零，确保历史运行残留状态（栈、注册表、计数）被彻底复位。
 *
 * 说明:
 *   - stack_top 采用 -1 表示页面栈为空，这是后续 Push/GetTop/Depth 的统一判定基准。
 *   - 该函数通常在系统启动阶段调用一次；若重复调用，将清空当前页面管理上下文。
 *
 * 返回值:
 *   - 无
 * ------------------------------------------------------------------------- */
void PageManager_Init(void)
{
    memset(&s_pageManager, 0, sizeof(s_pageManager));
    s_pageManager.stack_top = -1;
}

/* -------------------------------------------------------------------------
 * 函数名: PageManager_Register
 * 功能:
 *   将页面对象注册到页面管理器的注册表中，供后续按 page_id 查找与导航。
 *
 * 参数:
 *   page    - 待注册页面指针
 *
 * 边界处理:
 *   - 若 page 为 NULL，返回 PAGE_MANAGER_ERR_NULL_PARAM，不执行注册。
 *   - 若 page_id 非法（PAGE_ID_NONE 或超出 PAGE_ID_MAX），返回 PAGE_MANAGER_ERR_INVALID_ID。
 *   - 若注册表已满（page_count >= MAX_PAGE_COUNT），返回 PAGE_MANAGER_ERR_REGISTRY_FULL。
 *
 * 说明:
 *   - 若检测到相同 page_id 已存在，不新增条目，而是覆盖旧指针（热更新语义）。
 *   - 新页面按 page_count 尾插，成功后 page_count 自增 1。
 *
 * 返回值:
 *   - PAGE_MANAGER_OK                : 注册成功（含覆盖更新成功）
 *   - PAGE_MANAGER_ERR_NULL_PARAM    : 参数无效
 *   - PAGE_MANAGER_ERR_INVALID_ID    : page_id 非法
 *   - PAGE_MANAGER_ERR_REGISTRY_FULL : 注册表容量不足
 * ------------------------------------------------------------------------- */
page_manager_result_t PageManager_Register(page_t *page)
{
    ASSERT(page == NULL);
    if (page == NULL)
    {
        return PAGE_MANAGER_ERR_NULL_PARAM;
    }
    ASSERT((page->page_id == PAGE_ID_NONE) || (page->page_id >= PAGE_ID_MAX));
    if ((page->page_id == PAGE_ID_NONE) || (page->page_id >= PAGE_ID_MAX))
    {
        return PAGE_MANAGER_ERR_INVALID_ID;
    }

    for (uint8_t i = 0; i < s_pageManager.page_count; i++)
    {
        /* 支持重复注册：同 page_id 视为更新页面指针 */
        if (s_pageManager.page_registry[i]->page_id == page->page_id)
        {
            s_pageManager.page_registry[i] = page;
            return PAGE_MANAGER_OK;
        }
    }

    ASSERT(s_pageManager.page_count >= MAX_PAGE_COUNT);
    if (s_pageManager.page_count >= MAX_PAGE_COUNT)
    {
        return PAGE_MANAGER_ERR_REGISTRY_FULL;
    }

    s_pageManager.page_registry[s_pageManager.page_count] = page;
    s_pageManager.page_count++;
    return PAGE_MANAGER_OK;
}

/* -------------------------------------------------------------------------
 * 函数名: PageManager_Find
 * 功能:
 *   在管理器的页面注册表中按 page_id 线性查找，返回对应的 page_t 指针。
 *
 * 参数:
 *   pageId - 待查找的页面标识
 *
 * 边界处理:
 *   - 若注册表为空（page_count 为 0），循环不执行，返回 NULL。
 *   - 若不存在匹配的 page_id，返回 NULL。
 *
 * 说明:
 *   - 查找顺序与注册顺序一致；同 id 在 Register 中已保证唯一或覆盖更新。
 *   - 常与 PageManager_Navigate 配合：先 Find 再 Push，上层也可直接 Find 获取页面对象。
 *
 * 返回值:
 *   - 命中时返回注册表中的 page_t 指针
 *   - 未命中返回 NULL
 * ------------------------------------------------------------------------- */
page_t *PageManager_Find(page_id_t pageId)
{
    for (uint8_t i = 0; i < s_pageManager.page_count; i++)
    {
        if (s_pageManager.page_registry[i]->page_id == pageId)
        {
            return s_pageManager.page_registry[i];
        }
    }
    return NULL;
}

/* -------------------------------------------------------------------------
 * 函数名: PageManager_GetTop
 * 功能:
 *   返回当前页面栈栈顶元素，即当前“前台/可见”页面对应的 page_t 指针。
 *
 * 参数:
 *   无
 *
 * 边界处理:
 *   - stack_top < 0 表示空栈，返回 NULL。
 *   - 正常栈顶索引由 Push/Pop 维护，假定栈槽位与 stack_top 一致。
 *
 * 说明:
 *   - DispatchEvent、Loop、Navigate 等逻辑均以栈顶页为“当前页”。
 *   - 不修改栈状态，仅查询。
 *
 * 返回值:
 *   - 非空栈时返回栈顶 page_t 指针
 *   - 空栈时返回 NULL
 * ------------------------------------------------------------------------- */
page_t *PageManager_GetTop(void)
{
    if (s_pageManager.stack_top < 0)
    {
        return NULL;
    }
    return s_pageManager.page_stack[s_pageManager.stack_top];
}

/* -------------------------------------------------------------------------
 * 函数名: PageManager_Push
 * 功能:
 *   将指定页面压入页面栈并切换为当前页：必要时触发旧页 on_exit、新页 on_create/on_enter。
 *
 * 参数:
 *   newPage - 要入栈并成为栈顶的目标页面指针
 *
 * 边界处理:
 *   - 若 newPage 为 NULL，返回 PAGE_MANAGER_ERR_NULL_PARAM。
 *   - 若栈已满（stack_top >= MAX_PAGE_STACK_DEPTH - 1），返回 PAGE_MANAGER_ERR_STACK_FULL。
 *   - 若当前栈顶与 newPage 的 page_id 相同，视为重复导航，直接返回 PAGE_MANAGER_OK，
 *     不再次调用 on_exit/on_enter（幂等）。
 *
 * 说明:
 *   - 切换顺序：旧栈顶 on_exit（若存在）-> 新页入栈 ->
 *     若 is_created 为 0 则 on_create 并置 is_created，再 on_enter。
 *   - 页面“创建一次”语义由 is_created 控制；非缓存页在 Pop 时可销毁并重置 is_created。
 *
 * 返回值:
 *   - PAGE_MANAGER_OK             : 成功（含幂等重复入同一逻辑页）
 *   - PAGE_MANAGER_ERR_NULL_PARAM : 参数无效
 *   - PAGE_MANAGER_ERR_STACK_FULL : 栈满
 * ------------------------------------------------------------------------- */
page_manager_result_t PageManager_Push(page_t *newPage)
{
    page_t *currentTop;
    ASSERT(newPage == NULL);
    if (newPage == NULL)
    {
        return PAGE_MANAGER_ERR_NULL_PARAM;
    }
    ASSERT(s_pageManager.stack_top >= (MAX_PAGE_STACK_DEPTH - 1));
    if (s_pageManager.stack_top >= (MAX_PAGE_STACK_DEPTH - 1))
    {
        return PAGE_MANAGER_ERR_STACK_FULL;
    }

    currentTop = PageManager_GetTop();
    ASSERT((currentTop != NULL) && (currentTop->page_id == newPage->page_id));
    if ((currentTop != NULL) && (currentTop->page_id == newPage->page_id))
    {
        /* 重复导航到当前页面：按幂等处理 */
        return PAGE_MANAGER_OK;
    }
    /* 调用旧页的 on_exit 回调 */
    if ((currentTop != NULL) && (currentTop->on_exit != NULL))
    {
        currentTop->on_exit(currentTop);
    }

    /* 将新页压入栈 */
    s_pageManager.stack_top++;
    s_pageManager.page_stack[s_pageManager.stack_top] = newPage;

    if (!newPage->is_created)
    {
        /* 页面只创建一次（除非在 Pop 时被销毁并重置） */
        if (newPage->on_create != NULL)
        {
            newPage->on_create(newPage);
        }
        newPage->is_created = 1;
    }

    if (newPage->on_enter != NULL)
    {
        newPage->on_enter(newPage);
    }
    return PAGE_MANAGER_OK;
}

/* -------------------------------------------------------------------------
 * 函数名: PageManager_Pop
 * 功能:
 *   弹出当前栈顶页面并回到下一层：触发当前页 on_exit，非缓存页可 on_destroy，
 *   再对新栈顶调用 on_enter。
 *
 * 参数:
 *   无
 *
 * 边界处理:
 *   - 若 stack_top <= 0（仅余底页或空栈），不允许继续弹出，
 *     返回 PAGE_MANAGER_ERR_STACK_BOTTOM。
 *   - 当前页指针为 NULL 时跳过对应回调（防御性，正常不应出现）。
 *
 * 说明:
 *   - 设计为至少保留栈底一页：不能无限 Pop 到空栈，故 stack_top <= 0 即失败。
 *   - 非缓存页（is_cached == 0）在 Pop 后会 on_destroy 且 is_created 清零，下次 Push 会重新 on_create。
 *   - 缓存页仅 on_exit，不销毁，便于保留状态。
 *   - 全局 ESC 兜底通过本函数回退上一页。
 *
 * 返回值:
 *   - PAGE_MANAGER_OK               : 出栈成功，新栈顶已 on_enter（若实现）
 *   - PAGE_MANAGER_ERR_STACK_BOTTOM : 无法弹出（已到底或空栈）
 * ------------------------------------------------------------------------- */
page_manager_result_t PageManager_Pop(void)
{
    page_t *currentPage;
    page_t *newTop;

    ASSERT(s_pageManager.stack_top <= 0);
    if (s_pageManager.stack_top <= 0)
    {
        return PAGE_MANAGER_ERR_STACK_BOTTOM;
    }

    currentPage = s_pageManager.page_stack[s_pageManager.stack_top];
    if ((currentPage != NULL) && (currentPage->on_exit != NULL))
    {
        currentPage->on_exit(currentPage);
    }

    if ((currentPage != NULL) && !currentPage->is_cached)
    {
        /* 非缓存页离开即销毁，下次进入会重新 on_create */
        if (currentPage->on_destroy != NULL)
        {
            currentPage->on_destroy(currentPage);
        }
        currentPage->is_created = 0;
    }

    s_pageManager.page_stack[s_pageManager.stack_top] = NULL;
    s_pageManager.stack_top--;

    newTop = s_pageManager.page_stack[s_pageManager.stack_top];
    if ((newTop != NULL) && (newTop->on_enter != NULL))
    {
        newTop->on_enter(newTop);
    }
    return PAGE_MANAGER_OK;
}
/* -------------------------------------------------------------------------
 * 函数名: PageManager_Navigate
 * 功能:
 *   根据目标 page_id 执行页面导航：先在注册表中查找页面，再将其压入页面栈。
 *
 * 参数:
 *   pageId - 目标页面标识
 *
 * 边界处理:
 *   - 若注册表中未找到对应 pageId，返回 PAGE_MANAGER_ERR_NOT_FOUND，
 *     不执行页面切换。
 *   - 目标页找到后，Push 过程中的边界条件（如栈满、重复入栈等）
 *     由 PageManager_Push 统一处理并返回对应错误码。
 *
 * 说明:
 *   - 本函数是“按 ID 导航”的统一入口，避免上层直接操作 page_t 指针。
 *   - 导航成功后将触发标准生命周期链路：
 *       旧页 on_exit -> 新页（必要时 on_create）-> 新页 on_enter。
 *
 * 返回值:
 *   - PAGE_MANAGER_OK            : 导航成功
 *   - PAGE_MANAGER_ERR_NOT_FOUND : 未找到目标页面
 *   - 其他                       : 透传 PageManager_Push 的返回码（如栈满等）
 * ------------------------------------------------------------------------- */
page_manager_result_t PageManager_Navigate(page_id_t pageId)
{
    page_t *target;
    target = PageManager_Find(pageId);
    ASSERT(target == NULL);
    if (target == NULL)
    {
        return PAGE_MANAGER_ERR_NOT_FOUND;
    }
    return PageManager_Push(target);
}

/* -------------------------------------------------------------------------
 * 函数名: PageManager_Replace
 * 功能:
 *   页面替换接口：用目标页面替换当前栈顶页面，不增加栈深。
 *
 * 参数:
 *   pageId - 目标页面标识
 *
 * 边界处理:
 *   - 若目标页未注册，返回 PAGE_MANAGER_ERR_NOT_FOUND。
 *   - 若当前无活动页（空栈），退化为 Push 目标页。
 *   - 若目标页与当前栈顶同一 page_id，按幂等处理返回 PAGE_MANAGER_OK。
 *
 * 说明:
 *   - 生命周期顺序：
 *       1) 当前页 on_exit
 *       2) 若当前页非缓存且实现 on_destroy，则销毁并清 is_created
 *       3) 栈顶指针替换为目标页
 *       4) 目标页必要时 on_create（首次）+ on_enter
 *
 * 返回值:
 *   - PAGE_MANAGER_OK             : 替换成功
 *   - PAGE_MANAGER_ERR_NOT_FOUND  : 未找到目标页面
 *   - PAGE_MANAGER_ERR_STACK_FULL : 空栈退化 Push 且栈满（透传）
 * ------------------------------------------------------------------------- */
page_manager_result_t PageManager_Replace(page_id_t pageId)
{
    page_t *target;
    page_t *currentTop;

    target = PageManager_Find(pageId);
    ASSERT(target == NULL);
    if (target == NULL)
    {
        return PAGE_MANAGER_ERR_NOT_FOUND;
    }

    currentTop = PageManager_GetTop();
    if (currentTop == NULL)
    {
        /* 空栈场景：替换退化为压栈导航 */
        return PageManager_Push(target);
    }

    if (currentTop->page_id == target->page_id)
    {
        return PAGE_MANAGER_OK;
    }

    if (currentTop->on_exit != NULL)
    {
        currentTop->on_exit(currentTop);
    }

    if (!currentTop->is_cached)
    {
        if (currentTop->on_destroy != NULL)
        {
            currentTop->on_destroy(currentTop);
        }
        currentTop->is_created = 0;
    }

    s_pageManager.page_stack[s_pageManager.stack_top] = target;

    if (!target->is_created)
    {
        if (target->on_create != NULL)
        {
            target->on_create(target);
        }
        target->is_created = 1;
    }

    if (target->on_enter != NULL)
    {
        target->on_enter(target);
    }

    return PAGE_MANAGER_OK;
}

/* -------------------------------------------------------------------------
 * 函数名: PageManager_DispatchEvent
 * 功能:
 *   将输入事件分发到当前栈顶页面，并在页面未处理时执行全局兜底处理。
 *
 * 参数:
 *   event - 待分发的输入事件指针（键值、事件类型等）
 *
 * 边界处理:
 *   - 若 event 为 NULL，返回 PAGE_MANAGER_ERR_NULL_PARAM，不进行任何处理。
 *   - 若当前页面栈为空（GetTop 返回 NULL），返回 PAGE_MANAGER_ERR_NO_ACTIVE_PAGE。
 *   - 若栈顶页面未实现 on_event 回调，则视为未处理并进入兜底判定流程。
 *
 * 说明:
 *   - 分发优先级：页面私有事件处理 > 管理器全局兜底处理。
 *   - top->on_event 返回 EVENT_UNHANDLED 时，才调用
 *     PageManager_GlobalEventHandle(event)。
 *   - 该机制允许页面覆盖默认按键行为，同时保留统一的系统级快捷逻辑
 *     （例如 ESC 返回上页）。
 *
 * 返回值:
 *   - PAGE_MANAGER_OK             : 分发流程执行完成
 *   - PAGE_MANAGER_ERR_NULL_PARAM : 事件参数为空
 *   - PAGE_MANAGER_ERR_NO_ACTIVE_PAGE : 当前无活动页面
 * ------------------------------------------------------------------------- */
page_manager_result_t PageManager_DispatchEvent(input_event_t *event)
{
    page_t *top;
    event_result_t result;

    ASSERT(event == NULL);
    if (event == NULL)
    {
        return PAGE_MANAGER_ERR_NULL_PARAM;
    }

    top = PageManager_GetTop();
    ASSERT(top == NULL);
    if (top == NULL)
    {
        return PAGE_MANAGER_ERR_NO_ACTIVE_PAGE;
    }

    result = EVENT_UNHANDLED;
    if (top->on_event != NULL)
    {
        result = top->on_event(top, event);
    }

    /* 页面未处理时交给管理器处理全局快捷行为 */
    if (result == EVENT_UNHANDLED)
    {
        PageManager_GlobalEventHandle(event);
    }
    return PAGE_MANAGER_OK;
}

/* -------------------------------------------------------------------------
 * 函数名: PageManager_Loop
 * 功能:
 *   驱动当前栈顶页面的循环回调（on_loop），用于执行周期性任务。
 *
 * 参数:
 *   无
 *
 * 边界处理:
 *   - 若当前页面栈为空（GetTop 返回 NULL），函数直接返回。
 *   - 若栈顶页面未实现 on_loop 回调，函数直接返回。
 *   - 通过双重判空避免空指针调用导致崩溃。
 *
 * 说明:
 *   - 本函数只处理“当前可见页面”（栈顶页），不遍历其它页面。
 *   - 典型调用位置是主循环中的固定节拍点（例如每 N ms 调用一次）。
 *   - 页面切换、事件分发不在本函数处理范围内，分别由 Navigate/Push/Pop、
 *     DispatchEvent 负责。
 *
 * 返回值:
 *   - 无
 * ------------------------------------------------------------------------- */
void PageManager_Loop(void)
{
    page_t *top = PageManager_GetTop();
    ASSERT((top == NULL) || (top->on_loop == NULL));
    if ((top != NULL) && (top->on_loop != NULL))
    {
        top->on_loop(top);
    }
}
