#include <stdlib.h>
#include <stdint.h>
#include "hthread.h"
#include "hlog.h"
#include "mw_mmc.h"

typedef struct {
  hthread_t  th[HEVENT_PRIORITY_SIZE]; /* in case of block by one only thread */
  hloop_t   *loop[HEVENT_PRIORITY_SIZE];
  hmutex_t  mutex;
  uint8_t   inited;
}mmc;
static mmc s_mmc_inst = {0};

static void mmc_atomic_inc(void *p){
    kpacket *packet =(kpacket *)p;
    kpacket_inc(packet);
}

static void mmc_atomic_dec(void *p){
    kpacket *packet =(kpacket *)p;
    kpacket_dec(packet);

}

static uint32_t mmc_get_type(void *p){
    kpacket *packet =(kpacket *)p;
    return packet->tlv.T;
}

HTHREAD_ROUTINE(mmc_thread){
	hloop_t *loop = (hloop_t *)userdata;
    hloop_run(loop);
    return(NULL);
}

uint32_t mmc_pub(kpacket *packet){
    const uint32_t idx = packet->tlv.T % HEVENT_PRIORITY_SIZE;
    hloop_t* loop = s_mmc_inst.loop[idx];
    if ( loop ){
    	hloop_mmc_pub_event(loop, packet);
        return OK;
    } else {
        return NG;
    }
	
}


/*
 * priority not the same with loop[HEVENT_PRIORITY_SIZE]
**/

uint32_t mmc_sub(uint16_t event, hevent_cb cb, hevent_priority_e priority){
    const uint32_t idx = event % HEVENT_PRIORITY_SIZE;
    if (!s_mmc_inst.inited){
        hmutex_init(&s_mmc_inst.mutex);
        s_mmc_inst.inited = 1;
    }

    hmutex_lock(&s_mmc_inst.mutex);
    hloop_t* loop = s_mmc_inst.loop[idx];
    hthread_t th  = s_mmc_inst.th[idx];
    if (!th){
        s_mmc_inst.th[idx]= hthread_create(mmc_thread, loop);
    }
    hloop_mmc_sub_event(loop, event, priority, cb);
    hmutex_unlock(&s_mmc_inst.mutex);
	return 0;
}

uint32_t mmc_init(uint32_t mmc_queue_size){
    for (uint32_t i = 0; i<sizeof(s_mmc_inst.loop)/sizeof(s_mmc_inst.loop[0]); i++){
        hloop_t* loop = s_mmc_inst.loop[i] = hloop_new(HLOOP_FLAG_AUTO_FREE);
        if (!loop){
            return NG;
        }
        hloop_mmc_init(loop, mmc_atomic_inc, mmc_atomic_dec, mmc_get_type, mmc_queue_size);
    }
    return OK;
}


uint32_t mmc_deinit(){
    uint32_t err = 0;
    for (hevent_priority_e idx = HEVENT_LOWEST_PRIORITY; idx < HEVENT_PRIORITY_SIZE; idx++ )
    {
        hloop_t* loop = s_mmc_inst.loop[idx];
        if ( loop ) {
            s_mmc_inst.loop[idx] = NULL;
            hloop_stop(loop);
            uint64_t start_ms   = gettimeofday_ms();
            if (hthread_join(s_mmc_inst.th[idx]) != 0) {
                err = ESRCH;
                hloge("%s hthread_join priority:%d %p", __func__, idx, s_mmc_inst.th[idx]);
            }else {
                hlogw("%s %u/%u hthread_join priority:%d %p elapsed:%ul ms", __func__, idx+1, HEVENT_PRIORITY_SIZE, idx, s_mmc_inst.th[idx], gettimeofday_ms() - start_ms); 
            }
        }else{
            hloge("%s not init hthread_join priority:%d %p", __func__, idx+1, s_mmc_inst.th[idx]);
        }
    }
	return err;
}

static void on_mmc_events_low(hevent_t* ev) {
    kpacket *p = (kpacket*)ev->userdata;
    hlogd("%s event_type=%d userdata=%s\n", __func__, (int)ev->event_type, p->tlv.V);
}

static void on_mmc_events_normal(hevent_t* ev) {
    kpacket *p = (kpacket*)ev->userdata;
    hlogd("%s event_type=%d userdata=%s\n", __func__, (int)ev->event_type, p->tlv.V);
}

static void on_mmc_events_high(hevent_t* ev) {
    kpacket *p = (kpacket*)ev->userdata;
    hlogd("%s event_type=%d userdata=%s\n", __func__, (int)ev->event_type, p->tlv.V);
}


void mmc_test()
{
    
#define MMC_EVENT_QUEUE_INIT_SIZE       128
    enum{
        EVENT_1,
        EVENT_2,
    };
    mmc_init(MMC_EVENT_QUEUE_INIT_SIZE);
    mmc_sub(EVENT_1, on_mmc_events_low, HEVENT_LOWEST_PRIORITY);
    mmc_sub(EVENT_1, on_mmc_events_normal, HEVENT_NORMAL_PRIORITY);
    mmc_sub(EVENT_1, on_mmc_events_high, HEVENT_HIGHEST_PRIORITY);
    kpacket *packet = kpacket(EVENT_1, 10);
    kpacket_put_string(packet, "123456");
    mmc_pub(packet);
    kpacket_dec(packet);

    hlogd("%s packet=%p\n", __func__, packet);

    mmc_sub(EVENT_2, on_mmc_events_low, HEVENT_LOWEST_PRIORITY);
    mmc_sub(EVENT_2, on_mmc_events_normal, HEVENT_NORMAL_PRIORITY);
    mmc_sub(EVENT_2, on_mmc_events_high, HEVENT_HIGHEST_PRIORITY);
    packet = kpacket(EVENT_2, 10);
    kpacket_put_string(packet, "abcdefg");
    mmc_pub(packet);
    kpacket_dec(packet);
    hlogd("%s packet=%p\n", __func__, packet);
    
    hv_sleep(1);
    mmc_deinit();
}
