/*************************************************  
  File name     : pal_tcpclient.h
  Module        : 
  Author        : amir 
  Version       : 0.1   
  Created on    : 2022-02-07  
  Description   :
      Data structure and function definitions required by the socket interface

  Modify History:   
  1. Date:        Author:         Modification:
*************************************************/

#include "hthread.h"
#include "mw_tcpclient.h"
#include "hlog.h"
#include "hbase.h"
#include "hsocket.h"
#include "hloop.h"
#include "hevent.h"

#define dump_addr(io){\
    char localaddrstr[SOCKADDR_STRLEN] = {0};\
    char peeraddrstr[SOCKADDR_STRLEN]  = {0};\
    SOCKADDR_STR(io->localaddr, localaddrstr);\
    SOCKADDR_STR(io->peeraddr,  peeraddrstr);\
    hlogw("%s  %s,%s\n", __func__, localaddrstr, peeraddrstr);}


static void on_close(hio_t* io) {
    tcpclient *tclient = io->loop->userdata;
    if (tclient->hio == io){
        tclient->hio = NULL;
    }
    dump_addr(io);
}

static void on_recv(hio_t* io, void* buf, int readbytes) {
    tcpclient *tclient = io->loop->userdata;
    iobuffer_appendData(&tclient->read_iobuf, buf, readbytes);
    tclient->handler.on_unpacket(tclient, &tclient->read_iobuf);
}

static void on_connect(hio_t* io) {
    tcpclient *tclient = io->loop->userdata;
    if (tclient->hio) { /*最快的已连接上了, 关闭后面的*/
        hio_close(io);
        return;
    }
    dump_addr(io);
    tclient->hio = io;
    hio_setcb_read(io, on_recv);
    hio_read_start(io);
    TLV tlv = {.T = CONN_CONNECTED};
    tclient->handler.on_packet(tclient, &tlv);
}

void tcpclient_conn(tcpclient *tclient, const char *domain_or_ip, const int port) {
    hio_t* io = hio_create_socket(tclient->loop, domain_or_ip, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
    if (io == NULL) {
        perror("socket");
        exit(1);
    }
    dump_addr(io);
    tcp_nodelay(hio_fd(io), 1);
    hio_setcb_connect(io, on_connect);
    hio_setcb_close(io, on_close);
    hio_connect(io);
    TLV tlv = {.T = CONN_STARTED};
    tclient->handler.on_packet(tclient, &tlv);
	
}

int32_t tcpclient_send(tcpclient *tclient, const char *data, const uint32_t sz){
    char out_data[MAX_PATH];
    uint32_t cnt = tclient->handler.on_pack(out_data, sizeof(out_data), data, sz);
	if ( cnt > 0 ){
		if (tclient->status != 'E'){
			hlogd(">>> %s size:%u", __func__, cnt);
            hio_write(tclient->hio, out_data, cnt);
			return OK;
		}
	}
	return NG;
}

static void *tcpclient_tast(void *param)
{
    tcpclient *tclient = (tcpclient *)param;
	hloge("%s tcpclient:%p loop:%p\n",__func__, param, tclient->loop);
    tclient->status = 'R';
    while(tclient->status != 'E'){
        hloop_run(tclient->loop);
    }
	hlogw("%s exit:%p\n",__func__, param);
    //tclient->handler.on_funcpacket(tclient, CONN_DESTROY);
	tclient->th = 0;
    return(NULL);
}

tcpclient *tcpclient_init(int stack_size, packet_handler handler){
     tcpclient *tclient = (tcpclient*)calloc(1, sizeof(tcpclient));
     tclient->loop = hloop_new(0);
     tclient->loop->userdata = tclient;
     tclient->status     = 'I';//idle
     tclient->stack_size = stack_size;
     tclient->handler    = handler;
     iobuffer_init(&tclient->read_iobuf);
     tclient->th = hthread_create(tcpclient_tast, (void*)tclient);
     return tclient;
}
 
int32_t tcpclient_deinit(tcpclient *tclient){
     if (tclient)
     {
         tclient->status = 'E';
         hloop_wakeup(tclient->loop);
         hlogw("%s hloop_free\n",__func__);
         hloop_free(&tclient->loop);
         free(tclient);
         return OK;
     }
     return NG;
 }



/**********************应用层demo***********************/

#define DEMO_UART_JSON (1) /* 1. 为了测试string在串口工具显示, 实际要发收TLV*/

/* 应用层四步
**
** 一, 声明枚举指令 和 回调函数
** 二, 填写 packet_cmd_s 的 s_cmd_list
** 三, 发数on_pack, 收数on_unpacket后回调
**/

/* 第一步 */
typedef enum {
	EV_LOGIN					= 1000,//=>Server
}EV_CMD;

typedef struct{
	EV_CMD cmd;
	uint32_t (*on_cmd)(tcpclient *client, TLV *tlv);
}packet_cmd_s;

static uint32_t on_login(tcpclient *client, TLV *tlv){
	hloge("%s tlv->T:%u L:%u V:%s", __func__, tlv->T, tlv->L, tlv->V);
	return OK;
}

/* 第二步 */
static packet_cmd_s s_cmd_list[]={
	{EV_LOGIN, 	 on_login},
};

static int32_t  on_packet(tcpclient *client, void *data){
    json_pkt packet  = {0};
    char buf[MAX_PATH] = {0};     
    TLV *recv_tlv = (TLV *)data;   
    switch(recv_tlv->T){
		case CONN_STARTED:
			break;
		case CONN_CONNECTING:
			break;
		case CONN_CONNECTED:
            json_pkt_add_int(&packet, "cmd", EV_LOGIN);
			json_pkt_add_str(&packet, "udid", "my_uuid_is_123456789");
			json_pkt_add_str(&packet, "wakeup_type", "pir");
			break;
		case CONN_SECOND_TICK:
			break;
		case CONN_CLOSED:
			break;
		case CONN_DESTROY:
			break;
        default:
            for ( uint16_t i=0; i< sizeof(s_cmd_list)/sizeof(s_cmd_list[0]); i++ ){
        		if ( s_cmd_list[i].cmd == recv_tlv->T ){
                    s_cmd_list[i].on_cmd(client, recv_tlv);
                    return OK;
        		}
        	}
            hloge("unknown cmd:%u", recv_tlv->T);
            break; 
	}

    if (packet.json){
        char *str = json_pkt_toJson_string(&packet);
        uint32_t len = 0;
        if (str && ( len = strlen(str)) > 0 && len + sizeof(TLV) < sizeof(buf)){
            TLV *send_tlv =(TLV*)buf;
            send_tlv->T = packet.cmd;
            send_tlv->L = len;
            memcpy(send_tlv->V, str, len);
            tcpclient_send(client, (const char *)send_tlv, sizeof(TLV) + send_tlv->L);
            free(str);
        }else {
            hloge("error json:%p, len:%u", str, len);
        }
        json_pkt_reset(&packet);
    }
	return OK;
}

/* 第三步 */
int32_t on_pack(char *out_data, const uint32_t max_sz, const char *in_data, const uint32_t sz){
      /* 1. 比如可以在这里对data进行加密 */
      /* 2. in_data 是TLV */
    uint32_t cnt = 0;
    if ( sz < max_sz ){
#if DEMO_UART_JSON
        TLV *tlv =(TLV*)in_data;
        memcpy(out_data, tlv->V, tlv->L);
        cnt = tlv->L;
#else
        memcpy(out_data, in_data, sz);
        cnt = sz;
#endif
    }
	return cnt;
}

int32_t on_unpacket(tcpclient *client, iobuffer *iobuf){ /*TLV*/
    json_pkt packet = {0};
#if DEMO_UART_JSON
    if (json_pkt_parse(&packet, iobuf->iodata, "cmd") ){
        char buf[MAX_PATH] = {0};
        TLV *recv_tlv = (TLV *)buf; 
        recv_tlv->T = packet.cmd;
        recv_tlv->L = iobuf->size;
        memcpy(recv_tlv->V, iobuf->iodata, iobuf->size);
        on_packet(client, recv_tlv);
        iobuffer_eraseall(iobuf);
    }
#else
    TLV *tlv =(TLV*)iobuf->iodata;
    if (json_pkt_parse(&packet, tlv->V, "cmd") ){
        iobuffer_erase(iobuf, 0, sizeof(TLV)+tlv->L);
        on_packet(client, &packet);
    }
#endif

    if (packet.json){
        json_pkt_deinit(&packet);
    }else{
		hloge("<< unpack error \\n cannot be found: %s", iobuf->iodata);
	}
	return OK;
}

void tcpclient_test(){
	packet_handler handler = {on_packet, on_unpacket, on_pack};
	tcpclient *client      = tcpclient_init(1024, handler);
    tcpclient_conn(client, "192.168.1.98", 60000);
    //tcpclient_conn(client, "192.168.1.238", 80);
    //tcpclient_conn(client, "www.baidu.com", 80);
    hv_sleep(3);
}

