/*************************************************  
  File name     : ringbuffer.c
  Module        : audio and video library functions
  Author        : amir  
  Created on    : 2022-01-25    
  Description   :
      Function implement required by the mutex interface
    
  Modify History:   
  1. Date:        Author:         Modification:
    202405        罗启宏             增加lock需求
*************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "util.h"
#include "ringbuffer.h"
#include "iot_math.h"
#include "hlog.h"

/**
 * @brief 
 *
 * @param size
 *
 * @return 
 */
ringbuffer *ringbuffer_init(uint32_t capacity/*, gfp_t gfp_mask*/)
{
#if 0
    /* 检查时候是2的指数倍  */
    if (size & (size-1))
    {
//        size = 1UL << (roundup_pow_of_two(size) + 1);
        size = roundup_pow_of_two(size);
    }
#endif
	
	ringbuffer *rb  = (ringbuffer*)calloc(1, sizeof(ringbuffer));
    int8_t *buffer  = (int8_t *)malloc(capacity);
	if (rb && buffer){
        hmutex_init(&rb->mutex);
	    if (!buffer)
	    {
			free(rb);
			rb = NULL;
	    }else{
			rb->capacity = capacity;
			rb->buffer   = buffer;
            INIT_LIST_HEAD(&rb->list[RING_LIST_USING]);
        	INIT_LIST_HEAD(&rb->list[RING_LIST_FREE]);
            ringbuffer_reset(rb);
            hsem_init(&rb->sem, 0);
	    }
	}else{
        free(rb);
        free(buffer);
        rb     = NULL;
        buffer = NULL;
	}
    return rb;
}

void ringbuffer_deinit(ringbuffer *rb){
    hmutex_t mutex = rb->mutex;
	ringbuffer_reset(rb);
    hmutex_lock(&mutex);
    hsem_destroy(&rb->sem);
    free(rb->buffer);
    free(rb);
    hmutex_unlock(&mutex);
	hmutex_destroy(&mutex);
}

void ringbuffer_reset(ringbuffer *rb)
{
    hmutex_lock(&rb->mutex);
    uint16_t i = 0;
	for(i=0; i<RING_LIST_MAX; i++){
		struct list_head *pos, *n;
	    list_for_each_safe(pos, n, &rb->list[i]) {
	       ring_item *item = list_entry(rb->list[i].next, ring_item, link);
		   list_del(&item->link);
		   free(item);
	    }
	}
    rb->last_drop_seq = UINT32_MAX;
    rb->latest_item   = NULL;
    rb->write  = rb->read = rb->buffer;
    hmutex_unlock(&rb->mutex);
}

int32_t ringbuffer_size(ringbuffer *rb){
	//hloge("rb->write:%u rb->read:%u\n", rb->write, rb->read);
	uint32_t size;
	hmutex_lock(&rb->mutex);
	if (rb->write >= rb->read){
		size = (rb->write - rb->read);
	}else{
		size = rb->capacity - (rb->read - rb->write);
	}
	hmutex_unlock(&rb->mutex);
	return size;
}

int32_t ringbuffer_get_freesize(ringbuffer *rb){
	hmutex_lock(&rb->mutex);
	uint32_t freesize = rb->write >= rb->read ?  rb->capacity -  (rb->write - rb->read) : ( rb->read - rb->write);
	hmutex_unlock(&rb->mutex);
	return freesize;
}


uint32_t ringbuffer_get_lastseq_locked(ringbuffer *rb){
    uint32_t seq = rb->latest_item ? rb->latest_item->seq : UINT32_MAX;
    return seq;
}

#define ringbuffer_freesize_locked(rb)(\
	rb->write >= rb->read ?  rb->capacity -  (rb->write - rb->read) : ( rb->read - rb->write)\
)

//get the count of items in ringbuffer
int32_t ringbuffer_item_count(ringbuffer *rb){
	uint32_t count = 0;
	hmutex_lock(&rb->mutex);
	struct list_head *pos, *n;
    list_for_each_safe(pos, n, &rb->list[RING_LIST_USING]) {
       count++; 
    }
	hmutex_unlock(&rb->mutex);
	return count;
}


//put in data
ring_item *ringbuffer_put2(ringbuffer *rb, const char *data1, uint32_t data1_size, const char *data2, uint32_t data2_size){
	ring_item *item = NULL;
	uint32_t total_size = data1_size + data2_size;
	if (total_size > rb->capacity){
		return item;
	}
	hmutex_lock(&rb->mutex);
	
	if (list_empty(&rb->list[RING_LIST_FREE])){//if no free item then to malloc one
		item = (ring_item *)malloc(sizeof(ring_item));
	}else{//get free item
		item = list_entry(rb->list[RING_LIST_FREE].next, ring_item, link);
		list_del(&item->link);
	}
	if (item){
        //hloge("ringbuffer_put2:%u:%u:%u:%u\n", rb->write,  rb->write + total_size, rb->read, rb->buffer + rb->capacity);
        if (rb->write == rb->read && rb->read == rb->buffer + rb->capacity ){
            rb->write  = rb->read = rb->buffer;
            //hloge("reset ringbuffer_put2:%u:%u:%u\n", rb->write, rb->read, rb->buffer + rb->capacity);
            if (!list_empty(&rb->list[RING_LIST_USING])){
                hloge("error RING_LIST_USING should be empty \n\n");
            }
        }
		/*round to remove oldest item until there is enought space to add new one*/
		while(!list_empty(&rb->list[RING_LIST_USING]) && ringbuffer_freesize_locked(rb) < total_size){
			ring_item *item_oldest = list_entry(rb->list[RING_LIST_USING].next, ring_item, link);
			list_del(&item_oldest->link);
			list_add_tail(&item_oldest->link, &rb->list[RING_LIST_FREE]);
            rb->last_drop_seq = item_oldest->seq;

			/*move read to next item.pos*/
			ring_item *next = list_entry(rb->list[RING_LIST_USING].next, ring_item, link);
			rb->read = next->pos;
		}
		
		item->pos  = rb->write;//data write here
		item->size = total_size;
		item->seq  = rb->latest_item ? rb->latest_item->seq + 1 : 0; /* +1 */
        rb->latest_item = item; /*记录最新的一个, 且不会被删除, 同时seq已是最新的*/
		list_add_tail(&item->link, &rb->list[RING_LIST_USING]);

		/*buffer---read---write----end*/
		/*|---------|-------|--------|*/
		/*tail_left = end - write     */
		uint32_t tail_left = (rb->capacity - (rb->write - rb->buffer));
		if (tail_left >= total_size){
			memcpy(item->pos, data1, data1_size);
			if (data2 && data2_size){
				memcpy(item->pos + data1_size, data2, data2_size);
			}
			rb->write += total_size;
		}else{
			if (tail_left >= data1_size){
				memcpy(item->pos, data1, data1_size);//write to tail
				if (data2 && data2_size){
					memcpy(item->pos + data1_size, data2, tail_left - data1_size);
					memcpy(rb->buffer, data2 + (tail_left - data1_size), data2_size - (tail_left - data1_size));
				}
			}else{
				memcpy(item->pos, data1, tail_left);//write to tail
				memcpy(rb->buffer, data1 + tail_left, data1_size - tail_left);
				if (data2 && data2_size){
					memcpy(rb->buffer + (data1_size - tail_left), data2, data2_size);
				}
			}
			rb->write = rb->buffer + total_size - tail_left;// write = buffer + left
		}
        hsem_post(&rb->sem);
	}
	hmutex_unlock(&rb->mutex);
	//hloge("rb->write:%p item->size:%u read:%p\n", rb->write, item->size, rb->read);
	return item;
}


/**
 * @brief 
 *
 * @param rb
 * @param to    
 * @param len
 *
 * @return success(0) or fail(-1)
 */
int32_t ringbuffer_pop(ringbuffer *rb, char *to, uint32_t *len)
{
	int32_t  ret = -1;
	hmutex_lock(&rb->mutex);
	if (!list_empty(&rb->list[RING_LIST_USING])){
		ring_item *item = list_entry(rb->list[RING_LIST_USING].next, ring_item, link);
        list_del(&item->link);
		/*buffer-----read-----write----end*/
		/*|---------|---------|----------|*/
		/*item->pos + item->size > end ?  */
		/*then end_pos = item->pos + item->size - end */
		const int32_t end_pos = (item->pos + item->size) - (rb->buffer + rb->capacity);/* 如果 end 没到尾 */
		if (end_pos <=0){
			rb->read = item->pos + item->size;
			memcpy(to, item->pos, item->size);
		}else{
			memcpy(to, item->pos, item->size - end_pos);
			memcpy(to + item->size - end_pos, rb->buffer, end_pos);
			rb->read = rb->buffer + end_pos;
		}
        //hloge("ringbuffer_pop end_pos:%d item->size:%u pos:%u, buffer:%u, read:%u\n", end_pos, item->size,item->pos, rb->capacity,rb->read);
		
		if (len){
			*len = item->size;
		}	
        list_add_tail(&item->link, &rb->list[RING_LIST_FREE]);
		ret = 0;
	}
	hmutex_unlock(&rb->mutex);
	return ret;
}

void ringbuffer_dump(ringbuffer *rb){
	uint32_t i = 0;
	struct list_head *pos, *n;
	hmutex_lock(&rb->mutex);
    list_for_each_safe(pos, n, &rb->list[RING_LIST_USING]) {
       ring_item *item = list_entry(pos, ring_item, link);
	   hlogw("%s %02u item:%p size:%u pos:%p seq:%u\n", __func__, i++, item, item->size, item->pos, item->pos);
    }
	hmutex_unlock(&rb->mutex);
}

/*
 * ===  FUNCTION  ======================================================================
 *         Name:  main
 *  Description:
 * =====================================================================================
 */


int32_t ringbuffer_test()
{
    #define BUF_SIZE (64)
    #define TEST_STR "1234567891234567891234567891234567891234567891234567544656"
    char *ssss = malloc(64);
	ringbuffer * rb = ringbuffer_init(600);
	if (rb == NULL )
	{
		return -1;
	}
    
	int i;
	for(i=0; i<10;i++){
		ringbuffer_put(rb, TEST_STR, 60);
	}

    for(i=0; i<10;i++){
        uint32_t len;
		ringbuffer_pop(rb, ssss, &len);
        hlogd("%02d ssss:%s", i, ssss);
	}
	ringbuffer_dump(rb);
    hlogd("1 ringbuffer_test ringbuffer_size:%d\n\n", ringbuffer_size(rb));

    for(i=0; i<10;i++){
		ringbuffer_put(rb, TEST_STR, 60);
	}

    hlogd("11 ringbuffer_tes ringbuffer_size:%d\n\n", ringbuffer_size(rb));
    for(i=0; i<10;i++){
		ringbuffer_put(rb, TEST_STR, 60);
	}
    ringbuffer_dump(rb);
    hlogd("2 ringbuffer_test ringbuffer_size:%d\n\n", ringbuffer_size(rb));
	for(i=0; i<10;i++){
		uint32_t len;
		char outbuf[100]={0};
		ringbuffer_pop(rb, outbuf, &len);
		hlogd("i:02%d len:%d, outbuf:%s\n", i, len, outbuf);
	}
	ringbuffer_deinit(rb);

    return EXIT_SUCCESS;
}	

#if 0
https://blog.csdn.net/u011569253/article/details/71637100
https://www.linuxidc.com/Linux/2016-12/137936.htm
#endif
