#include "Drv/pages/YC/model.h"
#include "../global/global_state.h"
#include <string.h>
#include "data.h"

/* -------------------------------------------------------------------------
 * 函数名：Get_RealData_Num
 * 功能:
 *   统计指定类型中需要显示的数据项数量（根据 DB3 标志判断）。
 *
 * 参数:
 *   wTabType - 数据类型标识：EN_MEA_AC(交流)、EN_MEA_DC(直流) 或 EN_INPUT_RLY_ALL(遥信)
 *
 * 边界处理:
 *   - 若 wTabType 无效（非上述三种类型），返回 0。
 *   - 遍历范围受通道索引上限限制，不会越界访问数组。
 *
 * 说明:
 *   - 遍历对应数据表，检查 dwCommAttr 的 DB3 位（显示上送标志）。
 *   - 对于交流类型：从 EN_YCMEA_ORI_START 到 EN_YCMEA_ANA_END
 *   - 对于直流类型：从 EN_YCMEA_DC_START 到 EN_YCMEA_DC_END
 *   - 对于遥信类型：从 EN_KI_USER_END-1 开始遍历 g_tInputModelTab 表
 *   - 统计 DB3 置位的条目数量作为有效显示项数
 *   - 该函数用于计算总页数和初始化模型时获取数据总数
 *
 * 返回值:
 *   - 需要显示的数据项数量（uint16_t）
 * ------------------------------------------------------------------------- */
uint16_t Get_RealData_Num(uint16_t wTabType)
{
    uint16_t wNum = 0;
    
    if (wTabType == EN_MEA_AC)
    {
        /* 统计交流模拟量通道中 DB3=1 的条目数 */
        uint16_t wLoopMax = EN_YCMEA_ANA_END;
        for(uint16_t index = 0; index < wLoopMax; index++)
        {
            if( (g_tYcIndexTab[index].dwCommAttr & DB3) == DB3 )
            {
                wNum++;
            }
        }	
    }
    else if (wTabType == EN_MEA_DC)
    {
        /* 统计直流通道中 DB3=1 的条目数 */
        uint16_t wLoopMax = EN_YCMEA_DC_END;
        for(uint16_t index = EN_YCMEA_DC_START; index < wLoopMax; index++)
        {
            if( (g_tYcIndexTab[index].dwCommAttr & DB3) == DB3 )
            {
                wNum++;
            }
        }	
    }
    else if (wTabType == EN_INPUT_RLY_ALL)
    {
        /* 统计遥信输入中 DB3=1 的条目数 */
        uint16_t wLoopMax = EN_KI_USER_END;
        for(uint16_t index = 0; index < wLoopMax; index++)
        {
            if( (g_tInputModelTab[index].dwCommAttr & DB3) == DB3 )
            {
                wNum++;
            }
        }	
    }
    return wNum;
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Model_RowsOnCurrentPage
 * 功能:
 *   计算当前页需要显示的行数。
 *
 * 参数:
 *   model - 模型对象指针，包含总项数和当前页码信息
 *
 * 边界处理:
 *   - 若 model 为 NULL 或无数据项（itemNum=0），返回 0。
 *   - 最后一页可能不满一屏，需特殊计算。
 *
 * 说明:
 *   - 计算公式：剩余项数 = 总项数 - (当前页 -1) × 每页行数
 *   - 若剩余项数超过每页容量，返回 YC_ITEMS_PER_PAGE（满屏）
 *   - 否则返回实际剩余项数（最后一页不满的情况）
 *   - 该函数用于确定页面布局和内容绘制范围
 *
 * 返回值:
 *   - 当前页应显示的行数（uint16_t，不会超过 YC_ITEMS_PER_PAGE）
 * ------------------------------------------------------------------------- */
uint16_t YC_Model_RowsOnCurrentPage(const model_t *model)
{
    uint16_t remain;
    if ((model == NULL) || (model->itemNum == 0))
    {
        return 0;
    }
    /* 计算当前页剩余项数 */
    remain = model->itemNum - (model->page - 1u) * YC_ITEMS_PER_PAGE;
    return (remain > YC_ITEMS_PER_PAGE) ? YC_ITEMS_PER_PAGE : remain;
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Model_TableIndexForOrdinal
 * 功能:
 *   根据序号（ordinal）查找对应的通道索引号。
 *
 * 参数:
 *   attrib    - 数据类型标识：EN_MEA_AC(交流)、EN_MEA_DC(直流) 或 EN_INPUT_RLY_ALL(遥信)
 *   ordinal   - 显示序号（从 0 开始的当前页内行号，仅统计 DB3=1 的项）
 *
 * 边界处理:
 *   - 若未找到对应索引，返回 0xFFFFu。
 *   - 遍历范围受通道上限限制，不会越界访问数组。
 *
 * 说明:
 *   - 仅统计 dwCommAttr 的 DB3 位（显示上送标志）置位的条目。
 *   - 对于交流类型：从 EN_YCMEA_ORI_START 到 EN_YCMEA_ANA_END
 *   - 对于直流类型：从 EN_YCMEA_DC_START 到 EN_YCMEA_DC_END
 *   - 对于遥信类型：从 0 到 EN_KI_USER_END遍历 g_tInputModelTab
 *   - 按顺序计数，当计数器等于 ordinal 时返回当前索引
 *   - 该函数用于将页面上的行号映射到实际通道索引
 *
 * 返回值:
 *   - 对应的表索引值（uint16_t），未找到返回 0xFFFFu
 * ------------------------------------------------------------------------- */
uint16_t YC_Model_TableIndexForOrdinal(uint16_t attrib, uint16_t ordinal)
{
    uint16_t n = 0;

    if (attrib == EN_MEA_AC)
    {
        /* 遍历交流通道，查找第 ordinal 个 DB3=1 的条目 */
        for (uint16_t i = 0; i < EN_YCMEA_ANA_END; i++)
        {
            if ((g_tYcIndexTab[i].dwCommAttr & DB3) == DB3)
            {
                if (n == ordinal)
                {
                    return i;
                }
                n++;
            }
        }
    }
    else if (attrib == EN_MEA_DC)
    {
        /* 遍历直流通道，查找第 ordinal 个 DB3=1 的条目 */
        for (uint16_t i = EN_YCMEA_DC_START; i < EN_YCMEA_DC_END; i++)
        {
            if ((g_tYcIndexTab[i].dwCommAttr & DB3) == DB3)
            {
                if (n == ordinal)
                {
                    return i;
                }
                n++;
            }
        }
    }
    else if (attrib == EN_INPUT_RLY_ALL)
    {
        /* 遍历遥信通道，查找第 ordinal 个 DB3=1 的条目 */
        uint16_t i;
        for (i = 0; i < EN_KI_USER_END; i++)
        {
            if ((g_tInputModelTab[i].dwCommAttr & DB3) == DB3)
            {
                if (n == ordinal)
                {
                    return i;
                }
                n++;
            }
        }
    }
    return 0xFFFFu;     /* 未找到有效索引 */
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Model_OrdinalForRow
 * 功能:
 *   根据当前页码和行号计算全局显示序号。
 *
 * 参数:
 *   model     - Model 对象指针，包含当前页码信息
 *   rowOnPage - 当前页内的行号（从 0 开始）
 *
 * 边界处理:
 *   - 本函数不做参数校验，调用方应保证 rowOnPage 有效。
 *
 * 说明:
 *   - 计算公式：全局序号 = (页码 -1) × 每页行数 + 行号
 *   - 将页面内的相对行号转换为全局显示序号（从 0 开始计数）
 *   - 用于在翻页时保持数据项的连续编号
 *
 * 返回值:
 *   - 对应的全局显示序号（uint16_t）
 * ------------------------------------------------------------------------- */
uint16_t YC_Model_OrdinalForRow(const model_t *model, uint8_t rowOnPage)
{
    return (model->page - 1u) * YC_ITEMS_PER_PAGE + rowOnPage;
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Table_EntryCount（静态）
 * 功能:
 *   计算遥测量常量表的条目总数。
 *
 * 参数:
 *   无
 *
 * 边界处理:
 *   - 使用 sizeof 计算，不会越界。
 *
 * 说明:
 *   - 通过总字节数除以单个条目大小得到条目数量
 *   - 该函数为静态私有函数，仅在 model.c 内部使用
 *   - 用于数组边界检查等场景
 *
 * 返回值:
 *   - 表条目总数（uint16_t）
 * ------------------------------------------------------------------------- */
static uint16_t YC_Table_EntryCount(void)
{
    return (uint16_t)(sizeof(g_tYcIndexTab) / sizeof(g_tYcIndexTab[0]));
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Input_EntryCount（静态）
 * 功能:
 *   计算遥信输入通道的条目总数。
 *
 * 参数:
 *   无
 *
 * 边界处理:
 *   - 使用 sizeof 计算，不会越界。
 *
 * 说明:
 *   - 通过总字节数除以单个条目大小得到条目数量
 *   - 该函数为静态私有函数，仅在 model.c 内部使用
 *   - 用于遥信输入相关操作的边界检查
 *
 * 返回值:
 *   - 表条目总数（uint16_t）
 * ------------------------------------------------------------------------- */
static uint16_t YC_Input_EntryCount(void)
{
    return (uint16_t)(sizeof(g_tInputModelTab) / sizeof(g_tInputModelTab[0]));
}

/* -------------------------------------------------------------------------
 * 函数名：YC_GetNamePtr
 * 功能:
 *   根据模型类型获取指定输入项的名称指针。
 *
 * 参数:
 *   model      - Model 对象指针，提供当前数据类型标识（attrib）
 *   inputIndex - 输入项索引号
 *
 * 边界处理:
 *   - 若为遥信类型且索引越界，返回 NULL。
 *   - 若为遥测类型且索引越界，返回 NULL。
 *   - 自动进行边界检查，不会造成数组越界访问。
 *
 * 说明:
 *   - 根据 model->attrib 判断数据类型：
 *       - EN_INPUT_RLY_ALL：遥信输入类型，从 g_tInputModelTab 获取名称
 *       - 其他类型（交流/直流）：遥测类型，从 g_tYcIndexTab 获取名称
 *   - 返回指向名称字符串的指针，供视图层显示使用
 *   - 该函数实现了不同类型数据源的统一访问接口
 *
 * 返回值:
 *   - 名称字符串指针（const uint8_t*），无效索引或类型返回 NULL
 * ------------------------------------------------------------------------- */
const uint8_t *YC_GetNamePtr(const model_t *model, uint16_t inputIndex)
{
    /* 遥信输入类型：从 g_tInputModelTab 获取名称 */
    if (model->attrib == EN_INPUT_RLY_ALL)
    {
        if (inputIndex >= YC_Input_EntryCount())
        {
            return NULL;
        }
        return g_tInputModelTab[inputIndex].byName;
    }
    /* 遥测类型：从 g_tYcIndexTab 获取名称 */
    else
    {
        if (inputIndex >= YC_Table_EntryCount())
        {
            return NULL;
        }
        return g_tYcIndexTab[inputIndex].byName;
    }
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Input_GetType
 * 功能:
 *   根据输入索引获取输入项的类型标识。
 *
 * 参数:
 *   inputIndex - 输入项索引号
 *
 * 边界处理:
 *   - 若索引超出用户定义范围（>= EN_KI_USER_END），返回 YC_INPUT_TYPE_KI。
 *   - 通过多级条件判断确定具体类型，确保不会越界访问。
 *
 * 说明:
 *   - 输入类型判定逻辑：
 *       1) >= EN_KI_USER_END：用户自定义遥信（默认 KI 类型）
 *       2) >= EN_KI_INT8U_YX_START：8 位无符号整数型遥信
 *       3) >= EN_KI_TWIN_YX_START：双位遥信（DPYX）
 *       4) 其他情况：默认返回 KI 类型
 *   - 该函数用于确定输入项的数据格式和显示方式
 *   - 返回值对应 YC_INPUT_TYPE_*系列枚举定义
 *
 * 返回值:
 *   - 输入类型标识（uint8_t），见 YC_INPUT_TYPE_*枚举定义
 * ------------------------------------------------------------------------- */
uint8_t YC_Input_GetType(uint16_t inputIndex)
{
    /* 超出用户定义范围，返回默认 KI 类型 */
    if (inputIndex >= EN_KI_USER_END)
    {
        return YC_INPUT_TYPE_KI;
    }
    /* 8 位无符号整数型遥信 */
    if (inputIndex >= EN_KI_INT8U_YX_START)
    {
        return YC_INPUT_TYPE_UIN8;
    }
    /* 双位遥信 */
    if (inputIndex >= EN_KI_TWIN_YX_START)
    {
        return YC_INPUT_TYPE_DPYX;
    }
    /* 默认返回 KI 类型 */
    return YC_INPUT_TYPE_KI;
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Input_GetState
 * 功能:
 *   根据 CPU 参数和输入索引获取当前输入项的状态值。
 *
 * 参数:
 *   cpuPara    - CPU 参数，不同 CPU 可能对应不同的状态数据源
 *   inputIndex - 输入项索引号
 *
 * 边界处理:
 *   - 若索引超出用户定义范围（>= EN_KI_USER_END），返回 0。
 *   - 通过位运算提取低 4 位作为状态值，确保返回值在有效范围内。
 *
 * 说明:
 *   - 状态计算逻辑：state = (inputIndex + cpuPara) & 0x0F
 *     - inputIndex 与 cpuPara 相加模拟不同 CPU 的数据偏移
 *     - 与 0x0F 确保结果在 0-15 范围内（4 位状态值）
 *   - 该函数用于获取输入项的实时状态，供显示或逻辑判断使用
 *   - 返回值被转换为 uint8_t，实际有效位为低 4 位
 *
 * 返回值:
 *   - 输入项状态值（uint8_t），无效索引返回 0
 * ------------------------------------------------------------------------- */
uint8_t YC_Input_GetState(uint16_t cpuPara, uint16_t inputIndex)
{
    /* 超出范围返回默认值 */
    if (inputIndex >= EN_KI_USER_END)
    {
        return 0u;
    }
    /* 计算状态值并提取低 4 位 */
    return (uint8_t)((inputIndex + cpuPara) & 0x0Fu);
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Input_FormatState
 * 功能:
 *   根据输入类型和状态值格式化输出状态字符串。
 *
 * 参数:
 *   inputType - 输入项类型标识（YC_INPUT_TYPE_KI/SINYX/DPYX/UIN8）
 *   state     - 当前状态值
 *   buf       - 输出缓冲区指针，存放格式化后的状态字符串
 *   bufSize   - 输出缓冲区大小（字节）
 *
 * 边界处理:
 *   - 若 buf 为 NULL 或 bufSize 为 0，返回 YC_ERR_NULL。
 *   - 未知类型时填充"EN_OVER"并返回 YC_ERR_RANGE。
 *   - 自动清空缓冲区确保字符串正确结束。
 *
 * 说明:
 *   - 状态格式化规则：
 *       - KI 类型："SET"（闭合）或"ESC"（断开），根据 state&0x01 判断
 *       - SINYX/DPYX 类型："0"或"1"，显示单比特状态
 *       - UIN8 类型：两位十六进制数（如"3F"）
 *       - 未知类型：填充"EN_OVER"并返回错误码
 *   - 使用 snprintf() 确保字符串安全格式化，避免缓冲区溢出
 *   - 该函数用于将内部状态值转换为人类可读的显示文本
 *
 * 返回值:
 *   - YC_OK：成功
 *   - YC_ERR_NULL：参数无效（buf 为空或 bufSize 为 0）
 *   - YC_ERR_RANGE：未知输入类型
 * ------------------------------------------------------------------------- */
YC_result_t YC_Input_FormatState(uint8_t inputType, uint8_t state, uint8_t *buf, size_t bufSize)
{
    /* 参数有效性检查 */
    if ((buf == NULL) || (bufSize == 0u))
    {
        return YC_ERR_NULL;
    }
    /* 清空缓冲区确保字符串正确结束 */
    (void)memset(buf, 0, bufSize);
    
    /* 根据输入类型格式化状态字符串 */
    switch (inputType)
    {
        case YC_INPUT_TYPE_KI:
            /* KI 类型：SET（闭合）/ESC（断开）*/
            (void)snprintf((char *)buf, bufSize, "%s", ((state & 0x01u) != 0u) ? "SET" : "ESC");
            break;
        case YC_INPUT_TYPE_SINYX:
        case YC_INPUT_TYPE_DPYX:
            /* 单/双位遥信："0"或"1"*/
            (void)snprintf((char *)buf, bufSize, "%u", (unsigned int)(state & 0x01u));
            break;
        case YC_INPUT_TYPE_UIN8:
            /* 8 位无符号整数：两位十六进制数 */
            (void)snprintf((char *)buf, bufSize, "%02X", (unsigned int)state);
            break;
        default:
            /* 未知类型返回错误标识 */
            (void)snprintf((char *)buf, bufSize, "EN_OVER");
            return YC_ERR_RANGE;
    }
    return YC_OK;
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Table_GetWidth
 * 功能:
 *   获取指定通道的数据显示宽度。
 *
 * 参数:
 *   tableIndex - 通道索引号（g_tYcIndexTab 数组下标）
 *
 * 边界处理:
 *   - 若索引越界，返回 0。
 *   - 自动进行边界检查，不会造成数组越界访问。
 *
 * 说明:
 *   - 从 g_tYcIndexTab[tableIndex].byWidth 字段读取显示宽度
 *   - 该值包含小数点占用的字符位置
 *   - 用于视图层格式化显示时的宽度控制
 *
 * 返回值:
 *   - 数据显示宽度（uint8_t），无效索引返回 0
 * ------------------------------------------------------------------------- */
uint8_t YC_Table_GetWidth(uint16_t tableIndex)
{
    if (tableIndex >= YC_Table_EntryCount())
    {
        return 0;
    }
    return g_tYcIndexTab[tableIndex].byWidth;
}

/* -------------------------------------------------------------------------
 * 函数名：YC_Table_GetDotBits
 * 功能:
 *   获取指定通道的小数位数。
 *
 * 参数:
 *   tableIndex - 通道索引号（g_tYcIndexTab 数组下标）
 *
 * 边界处理:
 *   - 若索引越界，返回 0。
 *   - 自动进行边界检查，不会造成数组越界访问。
 *
 * 说明:
 *   - 从 g_tYcIndexTab[tableIndex].byDotBit 字段读取小数位数
 *   - 用于数值格式化时确定小数点位置
 *   - 例如：值 12345，小数位 2 → 显示"123.45"
 *
 * 返回值:
 *   - 小数位数（uint8_t），无效索引返回 0
 * ------------------------------------------------------------------------- */
uint8_t YC_Table_GetDotBits(uint16_t tableIndex)
{
    if (tableIndex >= YC_Table_EntryCount())
    {
        return 0;
    }
    return g_tYcIndexTab[tableIndex].byDotBit;
}

/* -------------------------------------------------------------------------
 * 函数名：Model_Init
 * 功能:
 *   初始化遥测页面模型对象。
 *
 * 参数:
 *   model - Model 对象指针，待初始化的结构体
 *
 * 边界处理:
 *   - 若 model 为 NULL，直接返回不做任何操作。
 *   - 所有成员字段都会被显式初始化到安全默认值。
 *
 * 说明:
 *   - 初始化流程：
 *       1) 清空整个模型结构体（memset）
 *       2) 从全局状态获取菜单动作名称、属性标识等参数
 *       3) 调用 Get_RealData_Num() 统计有效数据项总数
 *       4) 根据总项数计算总页数（group），并设置 empty 标志
 *       5) 初始化当前页码为 1，光标位置为第 0 行
 *   - 初始化后模型对象处于就绪状态，可进行页面显示操作
 *   - 该函数在页面首次加载或数据刷新时调用
 *
 * 返回值:
 *   - 无
 * ------------------------------------------------------------------------- */
void Model_Init(model_t *model)
{
    if (model == NULL)
    {
        return;
    }

    /* 清空模型结构体到零值 */
    memset(model, 0, sizeof(*model));
    
    /* 从全局状态获取菜单相关参数 */
    model->topName = GlobalModel_GetMenuActionName();
    model->attrib = GlobalModel_GetMenuActionAttrib();
    model->cpuPara = GlobalModel_GetMenuActionPara();
    
    /* 统计有效数据项数量（DB3=1 的条目）*/
    model->itemNum = Get_RealData_Num(model->attrib);
    
    /* 设置无数据标志 */
    model->empty = (model->itemNum == 0) ? 1u : 0u;
    
    if (model->itemNum == 0)
    {
        model->group = 1;     /* 无数据时总页数为 1（显示空页面）*/
    }
    else
    {
        /* 计算总页数：向上取整 (itemNum + perPage - 1) / perPage */
        model->group = (model->itemNum + YC_ITEMS_PER_PAGE - 1u) / YC_ITEMS_PER_PAGE;
    }
    
    /* 初始化页码 */
    model->page = 1;            /* 从第一页开始 */
}
