#include <stdlib.h>

#include "isil_frame.h"
#include "isil_sdk_config.h"
#include "isil_debug.h"

static struct ISIL_FramePool glbFramePool;


static void frame_reset(frame_t *frame)
{
    frame->len = 0;
    frame->ref = 0;
    frame->chanId = 0;
    frame->Type = 0;
}



static frame_t * try_get_frame_from_pool(FramePool* frame_pool)
{
    frame_t *frame = NULL;
    pthread_mutex_lock(&frame_pool->lock);
    if(!list_empty(&frame_pool->framesList)) {

        frame = list_entry(frame_pool->framesList.next,frame_t,entry);
        list_del_init(&frame->entry);
        add_frame_ref(frame);
        --frame_pool->framesNum;
    }
    pthread_mutex_unlock(&frame_pool->lock);
    return frame;
}


static frame_t * get_frame_from_pool(FramePool* frame_pool)
{
    frame_t *frame = NULL;

    pthread_mutex_lock(&frame_pool->lock);

     while(!frame_pool->framesNum ) {
         pthread_cond_wait(&frame_pool->cond,&frame_pool->lock);
     }

     frame = list_entry(frame_pool->framesList.next,frame_t,entry);
     list_del_init(&frame->entry);
     add_frame_ref(frame);
     --frame_pool->framesNum;
     pthread_mutex_unlock(&frame_pool->lock);
     return frame;
}


static void put_frame_to_pool(FramePool* frame_pool,frame_t *frame)
{
    

    pthread_mutex_lock(&frame_pool->lock);
    
    frame_reset(frame);
    list_add_tail(&frame->entry, &frame_pool->framesList);
    ++frame_pool->framesNum;

    if(frame_pool->framesNum == 1) {

        pthread_cond_signal(&frame_pool->cond);
    }

    pthread_mutex_unlock(&frame_pool->lock);
}

static void frame_pool_release(FramePool* frame_pool)
{
    frame_t* frame;
    struct list_head *ln1, *ln2;

    DEBUG_FUNCTION();

    pthread_mutex_lock(&frame_pool->lock);
    
    while( frame_pool->framesNum != frame_pool->max_buff_num ) {
         pthread_cond_wait(&frame_pool->cond,&frame_pool->lock);
     }

    pthread_mutex_unlock(&frame_pool->lock);

    pthread_mutex_lock(&frame_pool->lock);

    list_for_each_safe(ln1, ln2, &frame_pool->framesList){

        frame = list_entry(ln1,frame_t ,entry);
        if( frame ) {

            frame_reset(frame);
            frame->root = NULL;
            INIT_LIST_HEAD(&frame->entry);
            free(frame);
            frame = NULL;

        }

    }

    pthread_mutex_unlock(&frame_pool->lock);
    
    pthread_mutex_destroy(&frame_pool->lock);
    pthread_cond_destroy(&frame_pool->cond);
    INIT_LIST_HEAD(&frame_pool->framesList);

    

    frame_pool->framesNum = 0;

    if(frame_pool->frames_buff) {
        free(frame_pool->frames_buff);
        frame_pool->frames_buff = NULL;
    }

}



static void release_frame(frame_t *frame)
{
    if( !frame ) {
        return;
    }

    minus_frame_ref(frame);

    if( frame->ref ) {
        return;
    }

    if( frame->root ) {
        put_frame_to_pool(frame->root,frame);
    }
}


int init_frame_pool(FramePool* frame_pool , unsigned int frame_num , unsigned int frame_size)
{

    unsigned int i ,len;
    frame_t *frame;

    unsigned char *tmp;
    

    if( !frame_num || !frame_size) {
        /*TODO:notify user set num and frame_size*/
        return -1;
    }

    if( frame_size < MINFRAMEBUFFSIZE) {
        return -1;
    }

    frame_pool->max_buff_num = frame_num ;
    frame_pool->max_buff_size  = frame_size;

    frame_pool->frames_buff = NULL;
    
     
    pthread_mutex_init(&frame_pool->lock,NULL);
	INIT_LIST_HEAD(&frame_pool->framesList);
    pthread_cond_init(&frame_pool->cond,NULL);
    
    frame_pool->framesNum = 0;
    
    frame_pool->release = frame_pool_release;

    len = sizeof(frame_t);
    tmp = (unsigned char *)malloc( len*frame_num);
    if(tmp == NULL) {
        //TODO:notify user buff not enough
        return -1;
    }

    frame_pool->frames_buff = (unsigned char *)malloc(frame_num *frame_size);
    if( !frame_pool->frames_buff ) {
        //TODO:notify user buff not enough
        free(tmp);
        return -1;
    }

    for(i = 0 ; i < frame_num ; i++) {
        frame = (frame_t *)(tmp+i*len);
        frame->root = frame_pool;
        frame->release = release_frame;

        INIT_LIST_HEAD(&frame->entry);
        frame_reset(frame);
        frame->buff = (frame_pool->frames_buff + i *frame_size);
        add_frame_ref(frame);
        put_frame_to_pool(frame_pool,frame);
    }

    return 0;
}


int init_glb_frame_pool( unsigned int frame_num , unsigned int frame_size)
{
   
    int ret = 0;

    if( !glbFramePool.is_init ) {

        ret = init_frame_pool(&glbFramePool,frame_num,frame_size);
        if( ret == 0) {
            glbFramePool.is_init  = 1;
        }
    }
    return ret;
}

int init_glb_frame_pool_ex( void )
{
    int ret = 0;

    if( !glbFramePool.is_init ){
        
        if( glbFramePool.max_buff_num && glbFramePool.max_buff_size) {
            ret = init_frame_pool(&glbFramePool,glbFramePool.max_buff_num,glbFramePool.max_buff_size);
        }
        else{
            ret = init_frame_pool(&glbFramePool,DEFAULT_FRAME_NUMS,DEFAULT_FRAME_BUFF_SIZE);
        }

        if( ret == 0 ) {
            glbFramePool.is_init  = 1;
        }

    }

    return ret;
}


struct ISIL_FramePool * get_glb_frame_pool( void )
{
    return &glbFramePool;
}



static frame_t* get_frame_from_date_mgt_header(struct ISIL_DATE_MGT *date_mgt)
{
    frame_t *frame;
    pthread_mutex_lock(&date_mgt->lock);

    while(!date_mgt->framesNum ) {

         pthread_cond_wait(&date_mgt->cond,&date_mgt->lock);
    }

     frame = list_entry(date_mgt->framesList.next,frame_t,entry);
     list_del_init(&frame->entry);
//     minus_frame_ref(frame);
     --date_mgt->framesNum;
     pthread_mutex_unlock(&date_mgt->lock);
     return frame;
}


static frame_t* try_get_frame_from_date_mgt_header(struct ISIL_DATE_MGT *date_mgt)
{
    frame_t *frame;
    pthread_mutex_lock(&date_mgt->lock);

    if(list_empty(&date_mgt->framesList)) {

        pthread_mutex_unlock(&date_mgt->lock);
        return NULL;

    }

     frame = list_entry(date_mgt->framesList.next,frame_t,entry);
     list_del_init(&frame->entry);
//     minus_frame_ref(frame);
     --date_mgt->framesNum;
     pthread_mutex_unlock(&date_mgt->lock);
     return frame;
}





static void put_frame_to_date_mgt_tail(struct ISIL_DATE_MGT *date_mgt ,frame_t *frame)
{
    if(date_mgt && frame) {
        
        
    
        pthread_mutex_lock(&date_mgt->lock);
        
        
        list_add_tail(&frame->entry, &date_mgt->framesList);
        ++date_mgt->framesNum;
//        add_frame_ref(frame);
            
        pthread_mutex_unlock(&date_mgt->lock);
    }
}

static void release_date_mgt(struct ISIL_DATE_MGT *date_mgt)
{

    frame_t *frame;
    
    struct list_head *ln1, *ln2;
    
    if( !date_mgt ) {
        return;
    }
    
    pthread_mutex_lock(&date_mgt->lock);

     if( !list_empty(&date_mgt->framesList)) {

        list_for_each_safe(ln1, ln2, &date_mgt->framesList){

            frame = list_entry(ln1,frame_t ,entry);
            if( frame ) {
                frame->release(frame);
            }
 

        }
     }


    pthread_mutex_unlock(&date_mgt->lock);


    pthread_mutex_destroy(&date_mgt->lock);
    pthread_cond_destroy(&date_mgt->cond);
    INIT_LIST_HEAD(&date_mgt->framesList);

    if(date_mgt->alloc_type == ISIL_DYNAMIC_ALLOC_TYPE) {

        free(date_mgt);
        date_mgt = NULL;
    }
}



static void date_mgt_init(struct ISIL_DATE_MGT *date_mgt)
{

    if(date_mgt) {

        pthread_mutex_init(&date_mgt->lock,NULL);
        INIT_LIST_HEAD(&date_mgt->framesList);
        pthread_cond_init(&date_mgt->cond,NULL);
        date_mgt->framesNum = 0;
        date_mgt->get_frame_header = get_frame_from_date_mgt_header;
        date_mgt->try_get_frame_header = try_get_frame_from_date_mgt_header;
        date_mgt->put_frame_tail   = put_frame_to_date_mgt_tail;
        date_mgt->release          = release_date_mgt;
    }
}

void date_mgt_sinit(struct ISIL_DATE_MGT *date_mgt)
{
    date_mgt_init(date_mgt);
    date_mgt->alloc_type = ISIL_STATIC_ALLOC_TYPE;

}


struct ISIL_DATE_MGT* date_mgt_minit( void )
{
    struct ISIL_DATE_MGT *date_mgt;
    date_mgt = (struct ISIL_DATE_MGT *)calloc(1,sizeof(struct ISIL_DATE_MGT));
    if(!date_mgt) {
        return NULL;
    }

    date_mgt_init(date_mgt);
    date_mgt->alloc_type = ISIL_DYNAMIC_ALLOC_TYPE;

    return date_mgt;
}


frame_t * get_frame_from_glb_pool(void)
{
    return get_frame_from_pool(&glbFramePool);
}
