#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/timerfd.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "osp.h"

uint64_t ospTicks = 0;
#define ARTCH_TIMER_SIZE    4096
#define ARTCH_TIMER_OFFSET  8


Node **gp_TimerList = NULL;


void *gOspTimerSemId; 
static int32_t timerfd;



OSP_STATUS osp_timer_pool_init(void)
{
    gOspTimerSemId = osp_get_init_mem(sizeof(lx_sem_t));
    gp_TimerList = (Node **)osp_get_init_mem(sizeof(Node));

    osp_semsm_create(gOspTimerSemId);
    timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
    return (OSP_OK);
}

OSP_STATUS osp_timer_start(void *Timer)
{
    OSP_timer_Tcb *timer = Timer;
    timer->State = TIMER_RUNNING;
    return 0;
}


void *osp_timer_create_sim(uint32_t DstTaskId, bool isRepeat, uint32_t Period, uint32_t Delay)
{
    OSP_timer_Tcb *timer;
    Node *TmpNode;

    osp_sem_take(gOspTimerSemId, -1);
    /*if first one*/
    if ((NULL == (*gp_TimerList)) || (BUFFER_BASE == (char *)(*gp_TimerList)))
    {
        (*gp_TimerList) = (Node *)osp_alloc_mem(sizeof(Node));
        (*gp_TimerList)->TimerOff = (uint64_t)osp_alloc_mem(sizeof(OSP_timer_Tcb)) - (uint64_t)BUFFER_BASE;
        (*gp_TimerList)->NextNodeOff = 0; 

        timer = (OSP_timer_Tcb *)((*gp_TimerList)->TimerOff + BUFFER_BASE);
        timer->Delay = Delay;
        timer->Repeat =isRepeat;
        timer->Period = Period;
        timer->TimeOut = Period + Delay;
        timer->DstTaskId = DstTaskId;
        timer->RunCnt = 0;
        timer->State = TIMER_READY;
        timer->Valid = 1;
    }
    /*if not first one*/
    else
    {
        TmpNode = (*gp_TimerList);
        while (TmpNode->NextNodeOff)
        {
           TmpNode = (Node *)(TmpNode->NextNodeOff + (uint64_t)BUFFER_BASE);
        }
        
        TmpNode->NextNodeOff = (uint64_t)osp_alloc_mem(sizeof(Node)) - (uint64_t)BUFFER_BASE;
        TmpNode = (Node *)(TmpNode->NextNodeOff + (uint64_t)BUFFER_BASE);
        TmpNode->TimerOff = (uint64_t)osp_alloc_mem(sizeof(OSP_timer_Tcb)) - (uint64_t)BUFFER_BASE;
        TmpNode->NextNodeOff = 0;
        
        timer = (OSP_timer_Tcb *)(TmpNode->TimerOff + (uint64_t)BUFFER_BASE);
        timer->Delay = Delay;
        timer->Repeat = isRepeat;
        timer->Period = Period;
        timer->TimeOut = Period + Delay;
        timer->DstTaskId = DstTaskId; 
        timer->RunCnt = 0;
        timer->State = TIMER_READY;
        timer->Valid = 1;
    }

    osp_sem_give(gOspTimerSemId);
    return timer;
}
void  osp_timer_del_sim(void *timer)
{
    Node *TmpNode;
    Node *Pre;
    OSP_timer_Tcb *ptimer;
    if ((NULL == (*gp_TimerList)) || (BUFFER_BASE == (char *)(*gp_TimerList)))
    {
        return;
    }
    if (NULL == timer)
    {
        return;
    }
    
    ptimer = timer;
    if (1 != ptimer->Valid)
    {
        return;
    }
        
    osp_sem_take(gOspTimerSemId, -1);
    TmpNode = (*gp_TimerList);

    /*if head is the one*/
    if (timer == (OSP_timer_Tcb *)(TmpNode->TimerOff + (uint64_t)BUFFER_BASE))
    {
        (*gp_TimerList) = (Node *)(TmpNode->NextNodeOff + (uint64_t)BUFFER_BASE);
        osp_free_mem((char *)(TmpNode->TimerOff + (uint64_t)BUFFER_BASE));
        osp_free_mem((char *)TmpNode);
        osp_sem_give(gOspTimerSemId);
        return;
    }

    /*middle or tail*/
    while (timer != (OSP_timer_Tcb *)(TmpNode->TimerOff + (uint64_t)BUFFER_BASE))
    {
        Pre = TmpNode;
        TmpNode = (Node *)(TmpNode->NextNodeOff + (uint64_t)BUFFER_BASE);
    }
    
    Pre->NextNodeOff = TmpNode->NextNodeOff;
    osp_free_mem((char *)(TmpNode->TimerOff + (uint64_t)BUFFER_BASE));
    osp_free_mem((char *)TmpNode); 
    osp_sem_give(gOspTimerSemId);

}

void  osp_timer_suspend_sim(void *timer)
{
    OSP_timer_Tcb *ptimer;

    ptimer = (OSP_timer_Tcb *)timer;
    
    osp_sem_take(gOspTimerSemId, -1);
    printf("timer id %p\n", timer);
    
     ptimer->State = TIMER_SUSPEND;
    osp_sem_give(gOspTimerSemId);



}




void osp_wait_timeout(int32_t ms)
{

    static int32_t lastms = 0;
    struct itimerspec ts1; 
    uint64_t tmp;

    if (lastms != ms)
    {
        lastms = ms;
        ts1.it_value.tv_sec = 0; 
        ts1.it_value.tv_nsec = ms * 1000000; //!1ms

        ts1.it_interval = ts1.it_value; 
        //-------------------create timer 
        timerfd_settime(timerfd, 0, &ts1, NULL);
    }
    read(timerfd, &tmp, sizeof(uint64_t));



}







void osp_pridel_time(void)
{
    static uint32_t usecondold;
    static uint32_t usecondcur;
    static uint32_t useconddel;
        
    OSP_RTCTIME osptime;
    osp_get_rtc_time(&osptime);

    usecondold = usecondcur;
    usecondcur = osptime.usecond;
    if (usecondcur < usecondold)
    {
        useconddel = usecondcur + 1000000 - usecondold;
    }
    else
    {
        useconddel = usecondcur - usecondold;
    }
 
    printf("***************************************************%d\r\n", useconddel);


}

void  osp_tick_less(void)
{
    Node *TmpNode;
    Node *Pre;
    
    OSP_timer_Tcb *timer;
    static uint32_t next_time = 1;
    uint32_t min_time = 0xffffffff;  
    /*if osp init not finish, we need keep 1S period Delay run. so that OSP_tick will not run always*/
    

    /*if has no timer, we need keep 1S period Delay run. so that OSP_tick will not run always*/
    if ((NULL == (*gp_TimerList)) || (BUFFER_BASE == (char *)(*gp_TimerList)))
    {
        osp_wait_timeout(999);
        return;
    }
        
  
    ospTicks += next_time;
    osp_wait_timeout(next_time);

    osp_sem_take(gOspTimerSemId, -1);
    TmpNode = (*gp_TimerList);
    Pre = TmpNode;
    
    while (TmpNode->TimerOff)
    {
        timer = (OSP_timer_Tcb *)(TmpNode->TimerOff + (uint64_t)BUFFER_BASE);
        
        if (timer->State == TIMER_RUNNING)
        {
            timer->TimeOut -= next_time;
            
            if (0 >= timer->TimeOut)
            {
                timer->RunCnt++;
                osp_timer_callback(timer->DstTaskId, (uint64_t)timer);
                if (timer->Repeat)
                {
                    timer->TimeOut = timer->Period;
                    min_time = (min_time >= timer->TimeOut) ? timer->TimeOut: min_time;
                }
                /*rm if not Repeat*/
                else
                {
                    /*if head is the one*/
                    if (TmpNode == (*gp_TimerList))
                    {
                        (*gp_TimerList) = (Node *)(TmpNode->NextNodeOff + (uint64_t)BUFFER_BASE);
                        osp_free_mem((char *)(TmpNode->TimerOff + (uint64_t)BUFFER_BASE));
                        osp_free_mem((char *)TmpNode);
                    }
                
                    /*middle or tail*/
                    else
                    {
                    
                        Pre->NextNodeOff = TmpNode->NextNodeOff;
                        osp_free_mem((char *)(TmpNode->TimerOff + (uint64_t)BUFFER_BASE));
                        osp_free_mem((char *)TmpNode); 
                    }
                }
            }
            else
            {
                min_time = (min_time >= timer->TimeOut) ? timer->TimeOut: min_time;
            }
        }
        /*tail*/
        if (0 == TmpNode->NextNodeOff)
        {
            osp_sem_give(gOspTimerSemId);
            next_time = min_time;

            return;
        }
 
        Pre = TmpNode;
        TmpNode = (Node *)(TmpNode->NextNodeOff + (uint64_t)BUFFER_BASE);
    }    
    osp_sem_give(gOspTimerSemId);
}

uint64_t osp_get_osp_ticks(void)
{
    return (ospTicks);
}

#ifdef X86
unsigned long rdtsc(void)
{
    uint32_t low,high;
    asm volatile("rdtsc" : "=a"(low), "=d"(high));
    return low | (unsigned long)high << 32;

}

#else
uint64_t osp_arch_counter_get_cntpct(void)
{
    static int32_t fd = -1;
    static uint32_t *base = NULL;

    uint64_t cval = 0;
    if (-1 == fd)
    {
        fd  = open("/dev/archtimer", O_RDWR);
        if (-1 == fd)
            return 0;
        base = (uint32_t *)mmap(NULL, ARTCH_TIMER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, 
            fd, 0);
    }
    if (base)
    {
        cval = *(base + 3);
        cval = cval << 32;
        cval |= *(base + 2);
    }
    return cval;
}
#endif
uint64_t osp_get_cycel(void)
{
#ifdef X86 

    return rdtsc();
#else
    return osp_arch_counter_get_cntpct();
#endif
}
/*
 * osp_set_ticks - set ospTicks
 * input:
 *    ticks
 * return
 *    none
 */

void osp_set_ticks(uint64_t ticks)
{
    ospTicks = ticks;
}

/*
 * osp_timer_callback - default OSP timer's callcack routine
 * input:
 *    ospTaskId
 *    timerId    timer ID
 *    param??¡ìoo    user specified param
 * return
 *    OSP_OK,or
 *    OSP_ERROR(execute fail)
 */

OSP_STATUS osp_timer_callback(uint64_t DstTaskId, uint64_t timer)
{
    Osp_Msg_Head *phead = osp_alloc_msgTimer();

    
    if (NULL != phead)
    {
        phead->MsgType = OSP_TIMER_TYPE;
        phead->DstId = DstTaskId;
        phead->SrcId = CURRENT_TASKID;
        *(uint64_t *)((uint64_t)phead + MSG_HEAD_SIZE) = timer;

        osp_send_msg(phead);
    }
    return (OSP_OK);

}

void *osp_get_timer_from_msg(Osp_Msg_Head *pmsg)
{
    Osp_Timer_Msg_Block *timerblock = (Osp_Timer_Msg_Block *)pmsg;
    return (void *)(timerblock->data);
}

/*
*osp_timer_callback - display timer count(started/stoped/deleted)
*input:
*   none
*return
*   none
*/
/*lint -e826*/
void osp_show_timer_count(void)
{}


/*
 * osp_clk_rate_get - query system clock frequency
 * input:
 *    none
 * return
 *    ticks/per second
 */

int32_t osp_clk_rate_get(void)
{
    return 100;
}



/*
 * OSP_getOneTimerInfo - get timer's information
 * input:
 *    pNode     : pointer to OSP timer
 * return
 *    OSP_ERROR
 */



/*
 * showRunTimerInfo - print run timer's information
 * input:
 *    none
 * return
 *    none
 */

void osp_show_timer_info(void)
{
    Node *TmpNode;
    
    OSP_timer_Tcb *timer;
    char *pbuf;
    int32_t len = 0;


    /*if has no timer, we need keep 1S period Delay run. so that OSP_tick will not run always*/
    if ((NULL == (*gp_TimerList)) || (BUFFER_BASE == (char *)(*gp_TimerList)))
    {
        osp_debug_out(CMD_DEBUG_LEVEL, "%s\r\n", "no timer");
        return;
    }
    
    pbuf = malloc(10 * 1024);
    if (pbuf == NULL)    
        return;        
    len = sprintf(pbuf + len, "%-8s%-16s%-16s%-12s%-12s%-12s%-12s%-12s%-12s\n", "TaskId", "TskName", 
        "point", "Repeat", "RunCnt", "Period", "TimeOut", "Delay", "Suspend/Running");
    
    

        
    
    osp_sem_take(gOspTimerSemId, -1);
    TmpNode = (*gp_TimerList);
    
    while (TmpNode->TimerOff)
    {
        timer = (OSP_timer_Tcb *)(TmpNode->TimerOff + (uint64_t)BUFFER_BASE);
        len += sprintf(pbuf + len, "%-8d%-16s%-16p%-12s%-12lu%-12d%-12d%-12d%s\n",
            timer->DstTaskId, gOspTask[timer->DstTaskId].Osp_TaskMsg_Reg.TaskName,
            timer,
            (timer->Period) ? "ture" : "faulse",
            timer->RunCnt,
            timer->Period,
            timer->TimeOut,
            timer->Delay,
            (timer->State == TIMER_RUNNING) ? "Running" : "Suspend");

        /*tail*/
        if (0 == TmpNode->NextNodeOff)
        {
            osp_sem_give(gOspTimerSemId);
            break;
        }
 
        TmpNode = (Node *)(TmpNode->NextNodeOff + (uint64_t)BUFFER_BASE);
    }
    pbuf[len] = '\0';
    osp_debug_out(CMD_DEBUG_LEVEL, "%s", pbuf);
    free(pbuf);
}







/*
 * osp_get_systick - get the value of the kernel's tick counter
 * input:
 *    none
 * return
 *    the current value of the tick counter(per 1ms)
 */

uint32_t osp_get_systick()
{
    uint32_t currentTime;


    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    currentTime = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;

    return currentTime;
}










