#include "w55mh32_wztoe.h"
#include "w55mh32.h"
#include "w55mh32_gpio.h"
#include "w55mh32_spi.h"
#include "w55mh32_rcc.h"
#include "w55mh32_dma.h"
#include <string.h>

#define _W55MH32_WZTOE_READ_  (0x00 << 2)
#define _W55MH32_WZTOE_WRITE_ (0x01 << 2)

#define DMA_BUFFER_SIZE (4096)  
static __attribute__((aligned(4))) uint8_t spi_dma_tx_buffer[DMA_BUFFER_SIZE];
static __attribute__((aligned(4))) uint8_t spi_dma_rx_buffer[DMA_BUFFER_SIZE];

static uint8_t dma_initialized = 0;
static volatile uint8_t dma_transfer_complete = 0;

/**
 * @brief DMA传输完成/错误中断回调（RX）
 */
void DMA1_Channel4_IRQHandler(void)
{
    
    if (DMA_GetITStatus(DMA1_IT_TC4))
    {
        DMA_ClearITPendingBit(DMA1_IT_TC4);
        dma_transfer_complete = 1;
    }
    
    if (DMA_GetITStatus(DMA1_IT_TE4))
    {
        DMA_ClearITPendingBit(DMA1_IT_TE4);
        dma_transfer_complete = 1; 
    }
}

/**
 * @brief DMA传输完成/错误中断回调（TX）
 */
void DMA1_Channel5_IRQHandler(void)
{
    // 处理传输完成中断
    if (DMA_GetITStatus(DMA1_IT_TC5))
    {
        DMA_ClearITPendingBit(DMA1_IT_TC5);
    }
    // 处理传输错误中断（新增）
    if (DMA_GetITStatus(DMA1_IT_TE5))
    {
        DMA_ClearITPendingBit(DMA1_IT_TE5);
    }
}

/**
 * @brief 延时函数（优化）
 */
static void wiztoe_delay(uint32_t count)
{
    volatile uint32_t i;
    for (i = 0; i < count * 1000; i++)
    {
        __NOP();
    }
}

/**
 * @brief 初始化SPI DMA（带中断）
 */
static void spi_dma_init(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    
    if (dma_initialized) return;
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    DMA_DeInit(DMA1_Channel4); // SPI2 RX
    DMA_DeInit(DMA1_Channel5); // SPI2 TX
    
    DMA_Cmd(DMA1_Channel4, DISABLE);
    DMA_Cmd(DMA1_Channel5, DISABLE);
    
    // 提高中断优先级，减少中断延迟
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  // 最高优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
    NVIC_Init(&NVIC_InitStructure);
    
   
    DMA_ITConfig(DMA1_Channel4, DMA_IT_TC | DMA_IT_TE, ENABLE);
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC | DMA_IT_TE, ENABLE);
    
    dma_initialized = 1;
}

/**
 * @brief 优化的DMA传输函数（修复卡死问题）
 */
static void spi_dma_transfer(uint8_t *tx_data, uint8_t *rx_data, uint32_t size)
{
    DMA_InitTypeDef dma_tx_init, dma_rx_init;
    
    if (size == 0 || !dma_initialized) return;
    
    dma_transfer_complete = 0;
    
    // 快速检查SPI状态，减少等待时间
    uint32_t timeout = 100;
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET && timeout--) {
        __asm__ volatile("nop");
    }
    
    // 清空接收缓冲区残留数据
    if (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == SET)
    {
        SPI_I2S_ReceiveData(SPI2);
    }
    
    // 配置TX DMA - 使用寄存器操作提高速度
    DMA_Cmd(DMA1_Channel5, DISABLE); // 先禁用
    dma_tx_init.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
    dma_tx_init.DMA_MemoryBaseAddr = (uint32_t)tx_data;
    dma_tx_init.DMA_DIR = DMA_DIR_PeripheralDST;
    dma_tx_init.DMA_BufferSize = size;
    dma_tx_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma_tx_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma_tx_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    dma_tx_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    dma_tx_init.DMA_Mode = DMA_Mode_Normal;
    dma_tx_init.DMA_Priority = DMA_Priority_VeryHigh;
    dma_tx_init.DMA_M2M = DMA_M2M_Disable;
    
    DMA_Init(DMA1_Channel5, &dma_tx_init);
    
    // 配置RX DMA
    DMA_Cmd(DMA1_Channel4, DISABLE); // 先禁用
    dma_rx_init.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
    dma_rx_init.DMA_MemoryBaseAddr = (uint32_t)rx_data;
    dma_rx_init.DMA_DIR = DMA_DIR_PeripheralSRC;
    dma_rx_init.DMA_BufferSize = size;
    dma_rx_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma_rx_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma_rx_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    dma_rx_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    dma_rx_init.DMA_Mode = DMA_Mode_Normal;
    dma_rx_init.DMA_Priority = DMA_Priority_VeryHigh;
    dma_rx_init.DMA_M2M = DMA_M2M_Disable;
    
    DMA_Init(DMA1_Channel4, &dma_rx_init);
    
    // 清除DMA所有标志 - 避免残留中断
    DMA1->IFCR = DMA_IFCR_CGIF4 | DMA_IFCR_CGIF5;
    
    // 关键修复：先使能SPI DMA请求，再启动DMA通道
    SPI2->CR2 |= SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx;
    
    // 先使能RX DMA，再使能TX DMA，确保接收缓冲区就绪
    DMA_Cmd(DMA1_Channel4, ENABLE);
    DMA_Cmd(DMA1_Channel5, ENABLE);
    
    // 等待DMA传输完成，使用超时机制
    timeout = 100000;  // 增加超时值
    while (!dma_transfer_complete && timeout--) {
        __asm__ volatile("nop");
    }
    
    // 等待SPI硬件传输完成
    timeout = 1000;
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET && timeout--) {
        __asm__ volatile("nop");
    }
    
    // 禁用SPI DMA请求
    SPI2->CR2 &= ~(SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx);
    
    // 禁用DMA通道
    DMA_Cmd(DMA1_Channel5, DISABLE);
    DMA_Cmd(DMA1_Channel4, DISABLE);
    
    // 清除DMA标志（在禁用通道后）
    DMA1->IFCR = DMA_IFCR_CGIF4 | DMA_IFCR_CGIF5;
    
    // 错误恢复：超时后重置DMA
    if (timeout == 0)
    {
        DMA_DeInit(DMA1_Channel4);
        DMA_DeInit(DMA1_Channel5);
        dma_initialized = 0;
        spi_dma_init(); // 重新初始化DMA
    }
}

/**
 * @brief SPI字节写函数
 */
static void wiz_write_byte(uint8_t data)
{
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI2, data);
    
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
    SPI_I2S_ReceiveData(SPI2);
}

/**
 * @brief SPI字节读函数
 */
static uint8_t wiz_read_byte(void)
{
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);
    
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI2, 0xFF);
    
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
    return SPI_I2S_ReceiveData(SPI2);
}

/**
 * @brief SPI缓冲区写函数（优化版）
 */
static void wiz_write_buff(uint8_t *buff, uint16_t len)
{
    // 调整DMA阈值为64，减少DMA启动频率
    if (len <= 64)
    {
        for (uint16_t i = 0; i < len; i++)
        {
            wiz_write_byte(buff[i]);
        }
    }
    else
    {
        // 分段传输，避免超过DMA缓冲区大小
        uint16_t offset = 0;
        while (len > 0)
        {
            uint16_t chunk = (len > DMA_BUFFER_SIZE) ? DMA_BUFFER_SIZE : len;
            
            // 复制数据到DMA缓冲区
            memcpy(spi_dma_tx_buffer, buff + offset, chunk);
            
            // 使用DMA传输
            spi_dma_transfer(spi_dma_tx_buffer, spi_dma_rx_buffer, chunk);
            
            offset += chunk;
            len -= chunk;
        }
    }
}

/**
 * @brief SPI缓冲区读函数（优化版）
 */
static void wiz_read_buff(uint8_t *buff, uint16_t len)
{
    // 调整DMA阈值为64，减少DMA启动频率
    if (len <= 64)
    {
        for (uint16_t i = 0; i < len; i++)
        {
            buff[i] = wiz_read_byte();
        }
    }
    else
    {
        // 分段传输，避免超过DMA缓冲区大小
        uint16_t offset = 0;
        while (len > 0)
        {
            uint16_t chunk = (len > DMA_BUFFER_SIZE) ? DMA_BUFFER_SIZE : len;
            
            // 填充发送缓冲区为0xFF（读取时需要发送dummy字节）
            memset(spi_dma_tx_buffer, 0xFF, chunk);
            
            // 使用DMA传输
            spi_dma_transfer(spi_dma_tx_buffer, spi_dma_rx_buffer, chunk);
            
            // 复制接收数据到输出缓冲区
            memcpy(buff + offset, spi_dma_rx_buffer, chunk);
            
            offset += chunk;
            len -= chunk;
        }
    }
}

/**
 * @brief CS选择函数
 */
static void wiz_select(void)
{
   
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);
    
    GPIO_ResetBits(GPIOB, GPIO_Pin_12);
    
    // 短暂延时确保CS稳定
    for (volatile int i = 0; i < 10; i++) __NOP();
}

/**
 * @brief CS取消选择函数
 */
static void wiz_deselect(void)
{
    // 确保SPI不忙
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);
    
    GPIO_SetBits(GPIOB, GPIO_Pin_12);
    
    // 短暂延时确保CS稳定
    for (volatile int i = 0; i < 10; i++) __NOP();
}

static void wiztoe_bus_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef SPI_InitStructure;
    
    // 初始化GPIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    
    // 配置SPI引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // 配置CS引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_SetBits(GPIOB, GPIO_Pin_12);
    
    // 配置SPI
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    
    // 保持2分频以获得最高速度
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    
    SPI_Init(SPI2, &SPI_InitStructure);
    
    // 清空SPI状态
    SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_RXNE);
    SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_TXE);
    
    SPI_Cmd(SPI2, ENABLE);
    
    // 初始化DMA
    spi_dma_init();
}

/**
 * @brief 硬件复位
 */
static void wiztoe_hw_reset(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
    
    // 配置RSTn引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    // 复位序列
    GPIO_SetBits(GPIOD, GPIO_Pin_9);
    wiztoe_delay(100);
    
    GPIO_ResetBits(GPIOD, GPIO_Pin_9);
    wiztoe_delay(100);
    
    GPIO_SetBits(GPIOD, GPIO_Pin_9);
    wiztoe_delay(200);
}

/**
 * @brief 完整的W5500初始化函数
 * @return 0-成功，-1-失败
 */
int wiz_toe_init(void)
{
    uint8_t default_mac[6] = {0};
    
    // 读取MCU ID作为MAC地址
    default_mac[0] = *(volatile uint8_t *)(0x1ffff7e8 + 5);
    default_mac[1] = *(volatile uint8_t *)(0x1ffff7e8 + 4);
    default_mac[2] = *(volatile uint8_t *)(0x1ffff7e8 + 11);
    default_mac[3] = *(volatile uint8_t *)(0x1ffff7e8 + 10);
    default_mac[4] = *(volatile uint8_t *)(0x1ffff7e8 + 9);
    default_mac[5] = *(volatile uint8_t *)(0x1ffff7e8 + 8);
    
    // 硬件复位
    wiztoe_hw_reset();
    
    // SPI和DMA初始化
    wiztoe_bus_init();
    
    // 注册回调函数
    reg_wizchip_cs_cbfunc(wiz_select, wiz_deselect);
    reg_wizchip_spi_cbfunc(wiz_read_byte, wiz_write_byte);
    reg_wizchip_spiburst_cbfunc(wiz_read_buff, wiz_write_buff);
    
    // 设置MAC地址
    setSHAR(default_mac);
    
    // 检查芯片版本
    wiztoe_delay(50);
    if (getVERSIONR() != 0x04)
    {
        return -1;
    }
    
    return 0;
}

// WIZCHIP读写函数
uint8_t WIZCHIP_READ(uint32_t AddrSel)
{
    uint8_t ret;
    uint8_t spi_data[3];

    WIZCHIP.CRIS._enter();
    WIZCHIP.CS._select();

    AddrSel |= _W55MH32_WZTOE_READ_;

    if (!WIZCHIP.IF.SPI._read_burst || !WIZCHIP.IF.SPI._write_burst) // byte operation
    {
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16);
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >> 8);
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >> 0);
    }
    else // burst operation
    {
        spi_data[0] = (AddrSel & 0x00FF0000) >> 16;
        spi_data[1] = (AddrSel & 0x0000FF00) >> 8;
        spi_data[2] = (AddrSel & 0x000000FF) >> 0;
        WIZCHIP.IF.SPI._write_burst(spi_data, 3);
    }
    ret = WIZCHIP.IF.SPI._read_byte();

    WIZCHIP.CS._deselect();
    WIZCHIP.CRIS._exit();
    return ret;
}

void WIZCHIP_WRITE(uint32_t AddrSel, uint8_t wb)
{
    uint8_t spi_data[4];

    WIZCHIP.CRIS._enter();
    WIZCHIP.CS._select();

    AddrSel |= _W55MH32_WZTOE_WRITE_;

    if (!WIZCHIP.IF.SPI._write_burst) // byte operation
    {
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16);
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >> 8);
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >> 0);
        WIZCHIP.IF.SPI._write_byte(wb);
    }
    else // burst operation
    {
        spi_data[0] = (AddrSel & 0x00FF0000) >> 16;
        spi_data[1] = (AddrSel & 0x0000FF00) >> 8;
        spi_data[2] = (AddrSel & 0x000000FF) >> 0;
        spi_data[3] = wb;
        WIZCHIP.IF.SPI._write_burst(spi_data, 4);
    }

    WIZCHIP.CS._deselect();
    WIZCHIP.CRIS._exit();
}

void WIZCHIP_READ_BUF(uint32_t AddrSel, uint8_t *pBuf, uint16_t len)
{
    uint8_t  spi_data[3];
    uint16_t i;

    WIZCHIP.CRIS._enter();
    WIZCHIP.CS._select();

    AddrSel |= _W55MH32_WZTOE_READ_;

    if (!WIZCHIP.IF.SPI._read_burst || !WIZCHIP.IF.SPI._write_burst) // byte operation
    {
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16);
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >> 8);
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >> 0);
        for (i = 0; i < len; i++)
        {
            pBuf[i] = WIZCHIP.IF.SPI._read_byte();
        }
    }
    else // burst operation
    {
        spi_data[0] = (AddrSel & 0x00FF0000) >> 16;
        spi_data[1] = (AddrSel & 0x0000FF00) >> 8;
        spi_data[2] = (AddrSel & 0x000000FF) >> 0;
        WIZCHIP.IF.SPI._write_burst(spi_data, 3);
        WIZCHIP.IF.SPI._read_burst(pBuf, len);
    }

    WIZCHIP.CS._deselect();
    WIZCHIP.CRIS._exit();
}

void WIZCHIP_WRITE_BUF(uint32_t AddrSel, uint8_t *pBuf, uint16_t len)
{
    uint8_t  spi_data[3];
    uint16_t i;

    WIZCHIP.CRIS._enter();
    WIZCHIP.CS._select();

    AddrSel |= _W55MH32_WZTOE_WRITE_;

    if (!WIZCHIP.IF.SPI._write_burst) // byte operation
    {
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16);
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >> 8);
        WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >> 0);
        for (i = 0; i < len; i++)
        {
            WIZCHIP.IF.SPI._write_byte(pBuf[i]);
        }
    }
    else // burst operation
    {
        spi_data[0] = (AddrSel & 0x00FF0000) >> 16;
        spi_data[1] = (AddrSel & 0x0000FF00) >> 8;
        spi_data[2] = (AddrSel & 0x000000FF) >> 0;
        WIZCHIP.IF.SPI._write_burst(spi_data, 3);
        WIZCHIP.IF.SPI._write_burst(pBuf, len);
    }

    WIZCHIP.CS._deselect();
    WIZCHIP.CRIS._exit();
}

// 缓冲区状态读取函数
uint16_t getSn_TX_FSR(uint8_t sn)
{
    uint16_t val = 0, val1 = 0;

    do
    {
        val1 = WIZCHIP_READ(WZTOE_Sn_TX_FSR(sn));
        val1 = (val1 << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(WZTOE_Sn_TX_FSR(sn), 1));
        if (val1 != 0)
        {
            val = WIZCHIP_READ(WZTOE_Sn_TX_FSR(sn));
            val = (val << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(WZTOE_Sn_TX_FSR(sn), 1));
        }
    } while (val != val1);
    return val;
}

uint16_t getSn_RX_RSR(uint8_t sn)
{
    uint16_t val = 0, val1 = 0;

    do
    {
        val1 = WIZCHIP_READ(WZTOE_Sn_RX_RSR(sn));
        val1 = (val1 << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(WZTOE_Sn_RX_RSR(sn), 1));
        if (val1 != 0)
        {
            val = WIZCHIP_READ(WZTOE_Sn_RX_RSR(sn));
            val = (val << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(WZTOE_Sn_RX_RSR(sn), 1));
        }
    } while (val != val1);
    return val;
}

// 数据发送/接收函数
void wiz_send_data(uint8_t sn, uint8_t *wizdata, uint16_t len)
{
    uint16_t ptr     = 0;
    uint32_t addrsel = 0;

    if (len == 0)
        return;
    ptr     = getSn_TX_WR(sn);
    addrsel = ((uint32_t)ptr << 8) + (WIZCHIP_TXBUF_BLOCK(sn) << 3);
    WIZCHIP_WRITE_BUF(addrsel, wizdata, len);

    ptr += len;
    setSn_TX_WR(sn, ptr);
}

void wiz_recv_data(uint8_t sn, uint8_t *wizdata, uint16_t len)
{
    uint16_t ptr     = 0;
    uint32_t addrsel = 0;

    if (len == 0)
        return;
    ptr     = getSn_RX_RD(sn);
    addrsel = ((uint32_t)ptr << 8) + (WIZCHIP_RXBUF_BLOCK(sn) << 3);
    WIZCHIP_READ_BUF(addrsel, wizdata, len);
    ptr += len;

    setSn_RX_RD(sn, ptr);
}

void wiz_recv_ignore(uint8_t sn, uint16_t len)
{
    uint16_t ptr = 0;

    ptr  = getSn_RX_RD(sn);
    ptr += len;
    setSn_RX_RD(sn, ptr);
}
