
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sched.h>
#include <pthread.h>
#include <mqueue.h>
#include <sys/msg.h>
#include <signal.h>
#include <time.h>
#include <errno.h>

#include "myLog.h"
#include "myTask.h"



typedef struct
{
    char name[64];
    uint32_t events;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int ref_count;
}stru_task_event;

typedef struct
{
    char name[64];
    uint8_t *buffer;
    uint32_t size;
    uint32_t max_msgs;
    uint32_t msg_count;
    uint32_t head;
    uint32_t tail;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    // int non_block;
    int ref_count;
}stru_task_msg_queue;

typedef struct
{
    char name[64];
    timer_t timerid;
    timer_func_cb fun;
    void *arg;
    uint32_t timeout_ms;
    int flags;
    int active;
    pthread_mutex_t mutex;
    int ref_count;
}stru_task_timer;



/*
    任务延时
    ms: 延时毫秒数
*/
void task_sleep_ms(uint32_t ms)
{
    struct timespec rqtp;
	struct timespec rmtp;

	rqtp.tv_sec = ms / 1000;
	rqtp.tv_nsec = ms % 1000 * 1000000;
	
	for(;nanosleep(&rqtp, &rmtp);)
	{
		rqtp = rmtp;
	}
}

////////////////////////////////////////////////////////////////////////////////////////

stru_task_event_t task_event_create(const char *name)
{
    if(!name)
    {
        LOG_E("task_event_create error, name is NULL");
        return NULL;
    }
    
    stru_task_event *p_event = (stru_task_event *)calloc(1, sizeof(stru_task_event));
    if(!p_event)
    {
        LOG_E("task_event_create error, malloc failed");
        return NULL;
    }

    strncpy(p_event->name, name, sizeof(p_event->name)-1);
    p_event->events = 0;
    p_event->ref_count = 1;

    pthread_mutex_init(&p_event->mutex, NULL);
    pthread_cond_init(&p_event->cond, NULL);

    // LOG_I("task_event_create success, name:%s", name);
    return (stru_task_event_t)p_event;
}

int task_event_destroy(stru_task_event_t p_event)
{
    if(!p_event)
    {
        LOG_E("task_event_destroy error, p_event is NULL");
        return -1;
    }

    stru_task_event *p = (stru_task_event *)p_event;

    pthread_mutex_lock(&p->mutex);
    p->ref_count--;
    if(p->ref_count > 0)
    {
        pthread_mutex_unlock(&p->mutex);
        return 0;
    }

    pthread_mutex_unlock(&p->mutex);
    pthread_mutex_destroy(&p->mutex);
    pthread_cond_destroy(&p->cond);
    free(p);

    LOG_I("task_event_destroy success, name:%s", p->name);
    return 0;
}

int task_event_send(stru_task_event_t p_event, uint32_t event)
{
    if(!p_event)
    {
        LOG_E("task_event_send error, p_event is NULL");
        return -1;  
    }

    stru_task_event *p = (stru_task_event *)p_event;

    pthread_mutex_lock(&p->mutex);
    p->events |= event;
    pthread_cond_signal(&p->cond);
    pthread_mutex_unlock(&p->mutex);

    // LOG_I("task_event_send success, name:%s, event:%d", p->name, event);
    return 0;
}

int task_event_recv(stru_task_event_t p_event, uint32_t set, uint32_t opt, uint32_t timeout_ms, uint32_t *recved)
{
    if(!p_event)
    {
        LOG_E("task_event_recv error, p_event is NULL");
        return -1;
    }

    stru_task_event *p = (stru_task_event *)p_event;

    uint32_t matched;
    int ret = 0;
    struct timespec ts;

    pthread_mutex_lock(&p->mutex);

    if(timeout_ms != TASK_EVENT_WAIT_FOREVER)
    {
        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_sec += timeout_ms / 1000;
        ts.tv_nsec += (timeout_ms % 1000) * 1000000;
        if(ts.tv_nsec >= 1000000000)
        {
            ts.tv_sec++;
            ts.tv_nsec -= 1000000000;
        }
    }

    while(1)
    {
        if(opt == TASK_EVENT_FLAG_AND)
        {
            matched = set == (p->events & set);
        }
        else
        {
            matched = p->events & set;
        }

        if(matched)
        {
            if(recved)
            {
                *recved = p->events & set;
            }

            if(opt & TASK_EVENT_FLAG_CLEAR)
            {
                p->events &= ~set;
            }

            break;
        }

        if(timeout_ms == 0)
        {
            ret = -1;
            break;
        }
        
        if(timeout_ms == TASK_EVENT_WAIT_FOREVER)
        {
            ret = pthread_cond_wait(&p->cond, &p->mutex);
        }
        else
        {
            if(ETIMEDOUT == (ret = pthread_cond_timedwait(&p->cond, &p->mutex, &ts)))
            {
                ret = -1;
                break;
            }
        }

        if(ret != 0)
        {
            break;
        }
    }

    pthread_mutex_unlock(&p->mutex);

    return ret;
}

int task_event_clear(stru_task_event_t p_event, uint32_t event)
{
    if(!p_event)
    {
        LOG_E("task_event_clear error, p_event is NULL");
        return -1;
    }

    stru_task_event *p = (stru_task_event *)p_event;

    pthread_mutex_lock(&p->mutex);
    p->events &= ~event;
    pthread_mutex_unlock(&p->mutex);

    LOG_I("task_event_clear success, name:%s, event:%d", p->name, event);
    return 0;
}



////////////////////////////////////////////////////////////////////////////////////////

stru_task_msg_queue_t task_msg_queue_create(const char *name, uint32_t msg_size, uint32_t msg_num)
{
    if(!name || msg_size == 0 || msg_num == 0)
    {
        LOG_E("task_msg_queue_create error, name:%s, msg_size:%d, msg_num:%d", name, msg_size, msg_num);
        return NULL;
    }
    
    uint32_t total_size = msg_size * msg_num;

    stru_task_msg_queue *p_queue = (stru_task_msg_queue *)calloc(1, sizeof(stru_task_msg_queue));
    if(!p_queue)
    {
        LOG_E("task_msg_queue_create error, malloc failed");
        return NULL;
    }

    p_queue->buffer = (uint8_t *)malloc(total_size);
    if(!p_queue->buffer)
    {
        LOG_E("task_msg_queue_create error, malloc failed");
        free(p_queue);
        return NULL;
    }

    strncpy(p_queue->name, name, sizeof(p_queue->name)-1);
    p_queue->size = msg_size;
    p_queue->max_msgs = msg_num;
    p_queue->msg_count = 0;
    p_queue->head = 0;
    p_queue->tail = 0;
    // p_queue->non_block = 0;
    p_queue->ref_count = 1;

    pthread_mutex_init(&p_queue->mutex, NULL);
    pthread_cond_init(&p_queue->cond, NULL);

    // LOG_I("task_msg_queue_create success, name:%s, msg_size:%d, msg_num:%d", name, msg_size, msg_num);

    return (stru_task_msg_queue_t)p_queue;
}

int task_msg_queue_destroy(stru_task_msg_queue_t p_queue)
{
    if(!p_queue)
    {
        LOG_E("task_msg_queue_destroy error, p_queue is NULL");
        return -1;
    }
    
    stru_task_msg_queue *p = (stru_task_msg_queue *)p_queue;

    pthread_mutex_lock(&p->mutex);
    p->ref_count--;
    if(p->ref_count > 0)
    {
        pthread_mutex_unlock(&p->mutex);
        return 0;
    }

    pthread_mutex_unlock(&p->mutex);
    pthread_mutex_destroy(&p->mutex);
    pthread_cond_destroy(&p->cond);
    free(p->buffer);
    free(p);

    LOG_I("task_msg_queue_destroy success, name:%s", p->name);
    return 0;
}

int task_msg_queue_send(stru_task_msg_queue_t p_queue, const void *msg, uint32_t size)
{
    if(!p_queue || !msg || size == 0)
    {
        LOG_E("task_msg_queue_send error, p_queue:%p, msg:%p, size:%d", p_queue, msg, size);
        return -1;
    }

    stru_task_msg_queue *p = (stru_task_msg_queue *)p_queue;

    pthread_mutex_lock(&p->mutex);

    if(p->msg_count >= p->max_msgs)
    {
        pthread_mutex_unlock(&p->mutex);
        LOG_E("task_msg_queue_send error, p_queue:%p, msg:%p, size:%d, queue is full", p_queue, msg, size);
        return -1;
    }

    uint8_t *p_data = p->buffer + (p->tail * p->size);  

    memcpy(p_data, &size, sizeof(size));
    p_data += sizeof(size);

    memcpy(p_data, msg, size);



    p->tail = (p->tail + 1) % p->max_msgs;
    p->msg_count++;

    pthread_cond_signal(&p->cond);
    pthread_mutex_unlock(&p->mutex);

    // LOG_I("task_msg_queue_send success, p_queue:%p, msg:%p, size:%d", p_queue, msg, size);

    return 0;
}

int task_msg_queue_recv(stru_task_msg_queue_t p_queue, void *msg, uint32_t size, uint32_t timeout_ms)
{
    if(!p_queue || !msg || size == 0)
    {
        LOG_E("task_msg_queue_recv error, p_queue:%p, msg:%p, size:%d", p_queue, msg, size);
        return -1;
    }

    stru_task_msg_queue *p = (stru_task_msg_queue *)p_queue;

    struct timespec ts;
    uint8_t *p_data;
    uint32_t msg_len;
    int ret = 0;

    pthread_mutex_lock(&p->mutex);

    if(timeout_ms != TASK_EVENT_WAIT_FOREVER)
    {
        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_sec += timeout_ms / 1000;
        ts.tv_nsec += (timeout_ms % 1000) * 1000000;
        if(ts.tv_nsec >= 1000000000)
        {
            ts.tv_sec++;
            ts.tv_nsec -= 1000000000;
        }
    }

    while(p->msg_count == 0)
    {
        if(timeout_ms == 0)
        {
            ret = -1;
            goto out;
        }

        if(timeout_ms == TASK_EVENT_WAIT_FOREVER)
        {
            ret = pthread_cond_wait(&p->cond, &p->mutex);
        }
        else
        {
            if(ETIMEDOUT == (ret = pthread_cond_timedwait(&p->cond, &p->mutex, &ts)))
            {
                ret = -1;
                goto out;
            }
        }

        if(ret != 0)
        {
            goto out;
        }
    }

    p_data = p->buffer + (p->head * p->size);

    memcpy(&msg_len, p_data, sizeof(msg_len));
    p_data += sizeof(msg_len);

    if(msg_len > size)
    {
        msg_len = size;
    }

    memcpy(msg, p_data, msg_len);

    p->head = (p->head + 1) % p->max_msgs;
    p->msg_count--;

    ret = 0;

out:
    pthread_mutex_unlock(&p->mutex);

    // LOG_I("task_msg_queue_recv success, p_queue:%p, msg:%p, size:%d, ret:%d", p_queue, msg, size, ret);

    return ret;
}

int task_msg_queue_try_recv(stru_task_msg_queue_t p_queue, void *msg, uint32_t size)
{
    return task_msg_queue_recv(p_queue, msg, size, 0);
}

uint32_t task_msg_queue_get_count(stru_task_msg_queue_t p_queue)
{
    if(!p_queue)
    {
        LOG_E("task_msg_queue_get_count error, p_queue is NULL");
        return 0;
    }

    stru_task_msg_queue *p = (stru_task_msg_queue *)p_queue;

    uint32_t count;

    pthread_mutex_lock(&p->mutex);
    count = p->msg_count;
    pthread_mutex_unlock(&p->mutex);

    LOG_I("task_msg_queue_get_count success, p_queue:%p, count:%d", p_queue, count);

    return count;
}

uint32_t task_msg_queue_space(stru_task_msg_queue_t p_queue)
{
    if(!p_queue)
    {
        LOG_E("task_msg_queue_space error, p_queue is NULL");
        return 0;
    }

    stru_task_msg_queue *p = (stru_task_msg_queue *)p_queue;

    uint32_t space;

    pthread_mutex_lock(&p->mutex);
    space = p->max_msgs - p->msg_count;
    pthread_mutex_unlock(&p->mutex);

    LOG_I("task_msg_queue_space success, p_queue:%p, space:%d", p_queue, space);

    return space;
}


////////////////////////////////////////////////////////////////////////////////////////

static void task_timer_sig_handler(union sigval sv)
{
    stru_task_timer *p_timer = (stru_task_timer *)sv.sival_ptr;

    if(p_timer && p_timer->fun)
    {
        pthread_mutex_lock(&p_timer->mutex);
        if(p_timer->active)
        {
            p_timer->fun(p_timer->arg);

            if(!(p_timer->flags & TASK_TIMER_FLAG_PERIODIC))
            {
                p_timer->active = 0;
            }
        }
        pthread_mutex_unlock(&p_timer->mutex);
    }
}

stru_task_timer_t task_timer_create(const char *name, timer_func_cb fun_cb, void *arg, uint32_t timeout_ms, int flags)
{
    if(!name || !fun_cb || timeout_ms == 0)
    {
        LOG_E("task_timer_create error, name:%s, fun_cb:%p, timeout_ms:%d", name, fun_cb, timeout_ms);
        return NULL;
    }

    stru_task_timer *p_timer = (stru_task_timer *)calloc(1, sizeof(stru_task_timer));
    if(!p_timer)
    {
        LOG_E("task_timer_create error, malloc failed");
        return NULL;
    }

    strncpy(p_timer->name, name, sizeof(p_timer->name)-1);
    p_timer->fun = fun_cb;
    p_timer->arg = arg;
    p_timer->timeout_ms = timeout_ms;
    p_timer->flags = flags;
    p_timer->active = 0;
    p_timer->ref_count = 1;

    pthread_mutex_init(&p_timer->mutex, NULL);

    struct sigevent sev;
    memset(&sev, 0, sizeof(sev));

    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_value.sival_ptr = p_timer;
    sev.sigev_notify_function = task_timer_sig_handler;
    sev.sigev_notify_attributes = NULL;

    if(timer_create(CLOCK_REALTIME, &sev, &p_timer->timerid) != 0)
    {
        pthread_mutex_destroy(&p_timer->mutex);
        free(p_timer);
        LOG_E("task_timer_create error, timer_create failed");
        return NULL;
    }

    // LOG_I("task_timer_create success, name:%s, fun_cb:%p, arg:%p, timeout_ms:%d, flags:%d", name, fun_cb, arg, timeout_ms, flags);

    return (stru_task_timer_t)p_timer;
}

int task_timer_start(stru_task_timer_t p_timer)
{
    if(!p_timer)
    {
        LOG_E("task_timer_start error, p_timer is NULL");
        return -1;
    }

    stru_task_timer *p = (stru_task_timer *)p_timer;

    struct itimerspec its;

    pthread_mutex_lock(&p->mutex);

    its.it_value.tv_sec = p->timeout_ms / 1000;
    its.it_value.tv_nsec = (p->timeout_ms % 1000) * 1000000;

    if(p->flags & TASK_TIMER_FLAG_PERIODIC)
    {
        its.it_interval.tv_sec = its.it_value.tv_sec;
        its.it_interval.tv_nsec = its.it_value.tv_nsec;
    }
    else
    {
        its.it_interval.tv_sec = 0;
        its.it_interval.tv_nsec = 0;
    }

    if(timer_settime(p->timerid, 0, &its, NULL) != 0)
    {
        pthread_mutex_unlock(&p->mutex);
        LOG_E("task_timer_start error, timer_settime failed");
        return -1;
    }

    p->active = 1;

    pthread_mutex_unlock(&p->mutex);

    // LOG_I("task_timer_start success, p_timer:%p", p_timer);

    return 0;
}

int task_timer_stop(stru_task_timer_t p_timer)
{
    if(!p_timer)
    {   
        LOG_E("task_timer_stop error, p_timer is NULL");    
        return -1;
    }

    stru_task_timer *p = (stru_task_timer *)p_timer;

    struct itimerspec its;

    pthread_mutex_lock(&p->mutex);

    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = 0;

    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 0;

    if(timer_settime(p->timerid, 0, &its, NULL) != 0)
    {
        pthread_mutex_unlock(&p->mutex);
        LOG_E("task_timer_stop error, timer_settime failed");
        return -1;
    }

    p->active = 0;

    pthread_mutex_unlock(&p->mutex);

    LOG_I("task_timer_stop success, p_timer:%p", p_timer);

    return 0;
}

int task_timer_restart(stru_task_timer_t p_timer, uint32_t timeout_ms)
{
    if(!p_timer)
    {
        LOG_E("task_timer_restart error, p_timer is NULL");
        return -1;
    }

    stru_task_timer *p = (stru_task_timer *)p_timer;

    task_timer_stop(p_timer);

    pthread_mutex_lock(&p->mutex);
    p->timeout_ms = timeout_ms;
    pthread_mutex_unlock(&p->mutex);

    return task_timer_start(p_timer);
}

int task_timer_destroy(stru_task_timer_t p_timer)
{
    if(!p_timer)
    {
        LOG_E("task_timer_destroy error, p_timer is NULL");
        return -1;
    }

    stru_task_timer *p = (stru_task_timer *)p_timer;

    pthread_mutex_lock(&p->mutex);
    p->ref_count--;
    if(p->ref_count > 0)
    {
        pthread_mutex_unlock(&p->mutex);
        return 0;
    }

    pthread_mutex_unlock(&p->mutex);
    timer_delete(p->timerid);
    pthread_mutex_destroy(&p->mutex);
    free(p);

    LOG_I("task_timer_destroy success, p_timer:%p", p_timer);

    return 0;
}

int task_timer_is_active(stru_task_timer_t p_timer)
{
    if(!p_timer)
    {
        LOG_E("task_timer_is_active error, p_timer is NULL");
        return -1;
    }

    stru_task_timer *p = (stru_task_timer *)p_timer;

    int active;

    pthread_mutex_lock(&p->mutex);
    active = p->active;
    pthread_mutex_unlock(&p->mutex);

    return active;
}