#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include "com_api.h"
 
#define CCU_TCP_PORT            16565
#define BUFFER_SIZE     1024
#define MAX_LISTEN_NUM      10
#define MAX_IP_LEN      16
#define DEVICE_CODE_LEN      33



typedef struct{
	char deviceCode[DEVICE_CODE_LEN];
	char ip[MAX_IP_LEN];
	int sock;
	int isConnect;
}kk_tcp_ctrl_t;

static kk_tcp_ctrl_t g_tcp_ctrl[MAX_LISTEN_NUM];
static struct ev_loop *g_loop = NULL;
static pthread_t g_pTh = NULL;
static int g_init = 0;
static ipc_cb* g_cb = NULL;
static struct ev_io w_accept;


static int get_idx_by_ip(char ip[MAX_IP_LEN]){
    int i = 0;

    if (ip == NULL || strlen(ip) == 0){
        return -1;
    }
    for(;i < MAX_LISTEN_NUM; i++){
       if(strcmp(ip, g_tcp_ctrl[i].ip) == 0){
            return i;
       } 
    }
    return -1;
    
} 

static int get_sock_by_deviceCode(char deviceCode[DEVICE_CODE_LEN]){
    int i = 0;

    if (deviceCode == NULL || strlen(deviceCode) == 0){
        return -1;
    }
    for(;i < MAX_LISTEN_NUM; i++){
       if(strcmp(deviceCode, g_tcp_ctrl[i].deviceCode) == 0){
            return  g_tcp_ctrl[i].sock;
       } 
    }
    return -1;
    
} 

static kk_tcp_ctrl_t* get_channel_by_ip(char ip[MAX_IP_LEN]){
    int i = 0;

    if (ip == NULL || strlen(ip) == 0){
        return NULL;
    }

    printf("[%s] ip=%s \n", __FUNCTION__,ip);
    for(;i < MAX_LISTEN_NUM; i++){
       if(strcmp(ip, g_tcp_ctrl[i].ip) == 0){
            printf("[%s] idx=%d ip=%s sock=%d\n", __FUNCTION__,i, ip, g_tcp_ctrl[i].sock);
            return  &g_tcp_ctrl[i];
       } 
    }
    return NULL;
    
} 


static int set_status_by_ip(char ip[MAX_IP_LEN], int status){
    int i = 0;

    if (ip == NULL || strlen(ip) == 0){
        return -1;
    }
    for(;i < MAX_LISTEN_NUM; i++){
       if(strcmp(ip, g_tcp_ctrl[i].ip) == 0){
            g_tcp_ctrl[i].isConnect = status;
            return 0;
       } 
    }
    return -1;
    
}
static int reset_by_sock(int sock){
    int i = 0;

    if (sock < 0){
        return -1;
    }
    for(;i < MAX_LISTEN_NUM; i++){
       if(sock == g_tcp_ctrl[i].sock){
            g_tcp_ctrl[i].isConnect = 0;
			g_tcp_ctrl[i].isConnect = -1;
            return 0;
       } 
    }
    return -1;
    
}
static int set_sock_by_ip(char ip[MAX_IP_LEN], int sock){
    int i = 0;

    if (ip == NULL || strlen(ip) == 0){
        return -1;
    }
    printf("[%s] ip=%s \n", __FUNCTION__,ip);
    for(;i < MAX_LISTEN_NUM; i++){
       if(strcmp(ip, g_tcp_ctrl[i].ip) == 0){
            
            g_tcp_ctrl[i].sock = sock;
            printf("[%s] idx=%d ip=%s sock=%d\n", __FUNCTION__,i, ip, g_tcp_ctrl[i].sock);
            return 0;
       } 
    }
    return -1;
    
}

int kk_is_tcp_channel(char devCode[DEVICE_CODE_LEN]){
    
    int i = 0;

    if (devCode == NULL || strlen(devCode) == 0){
        return -1;
    }
    //printf("[%s] devCode=%s \n", __FUNCTION__,devCode);
    for(;i < MAX_LISTEN_NUM; i++){
       if(strcmp(devCode, g_tcp_ctrl[i].deviceCode) == 0){
            
            printf("[%s] idx=%d ip=%s sock=%d\n", __FUNCTION__,i, devCode, g_tcp_ctrl[i].sock);
            return i;
       } 
    }
    return -1;
}

int kk_set_tcp_channel(char devCode[DEVICE_CODE_LEN], char ip[MAX_IP_LEN]){
    int i = 0;
	int isEmptyIdx = -1;

    if (devCode == NULL || strlen(devCode) == 0 || ip == NULL || strlen(ip) == 0){
        printf("paramenter error \n");
        return -1;
    }
	
    for(;i < MAX_LISTEN_NUM; i++){

       if(strcmp(devCode, g_tcp_ctrl[i].deviceCode) == 0){
            strncpy(g_tcp_ctrl[i].ip, ip, strlen(ip));
            printf("find and replace it [%d][%s][%s] \n",i, g_tcp_ctrl[i].ip, devCode);
            break;
       } 

	   if (strlen(g_tcp_ctrl[i].deviceCode) == 0 && isEmptyIdx == -1){
			isEmptyIdx = i;
		}
    }

	if (i < MAX_LISTEN_NUM){
		return 0;
	}

	if (isEmptyIdx != -1){
        
		strncpy(g_tcp_ctrl[isEmptyIdx].ip, ip, strlen(ip));
		strncpy(g_tcp_ctrl[isEmptyIdx].deviceCode, devCode, strlen(devCode));
		printf("idx deviceCode ip[%d][%s][%s]",isEmptyIdx, g_tcp_ctrl[isEmptyIdx].deviceCode, g_tcp_ctrl[isEmptyIdx].ip);
		return 0;
	}
	
    return -1;
    
}




/*初始化服务端*/
static int server_socket_init(int *sd, char *ipaddr, uint16_t port)
{
    //创建socket
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sock){
        printf("error socket \n");
        goto err1;
    }
    //设置立即释放端口并可以再次使用
    int reuse = 1;
    if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))){
        printf("error setsockopt \n");
        goto err2;
    }
    //设置为非阻塞
    if (-1 == fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK)){
        printf("================== fcntl \n");
        goto err2;
    }
    struct sockaddr_in addr;
    memset(&addr, 0 , sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    if (NULL == ipaddr) {
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
    } else {
        addr.sin_addr.s_addr = inet_addr(ipaddr);
    }
    //绑定监听
    if (-1 == bind(sock, (struct sockaddr *)&addr, sizeof(addr))){
        printf("error bind \n");
        goto err2;
    }
    if (-1 == listen(sock, MAX_LISTEN_NUM)){
        printf("error listen \n");
        goto err2;
    }
    *sd = sock;
    return 0;
err2:
    close(sock);
err1:
    return -1;
}
 
/*读回调*/
static void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
    printf("================== read_cb \n");
    char buffer[BUFFER_SIZE] = {0};
    if (EV_ERROR & revents) {
      printf("read got invalid event...\r\n");
      return;
    }
    int res = 0;
	kk_tcp_ctrl_t* tcp_ctrl = (kk_tcp_ctrl_t*)watcher->data;
    int32_t bytes = read(watcher->fd, buffer, sizeof(buffer));
    if (-1 == bytes) {
        //tcp Error
        if (EINTR != errno && EAGAIN != errno) {
            res = 1;
        }
    } else if (0 == bytes) {
        //tcp Close
        res = 2;
    }
    if (0 != res) {
        //关闭事件循环并释放watcher
        printf("TCP CLOSE\r\n");
        reset_by_sock(watcher->fd);
        ev_io_stop(loop,watcher);
        free(watcher);
    } else {
        //intf("READ:\r\n    %s\r\n", buffer);
        printf("read_cb deviceCode ip sock [%s][%s][%d]",tcp_ctrl->deviceCode,tcp_ctrl->ip, tcp_ctrl->sock);
		if (g_cb != NULL){
			g_cb(buffer,bytes,tcp_ctrl->deviceCode);
		}
    }
}
 
 
/*accept回调函数*/
static void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
    printf("================== accept_cb \n");
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    if (EV_ERROR & revents) {
      printf("accept got invalid event...\r\n");
      return;
    }
    //accept连接
    int sock = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len);
    if (-1 == sock) {
        return;
    }
    //设置非阻塞
    if(-1 == fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK)) {
        close(sock);
        return;
    }

    if(set_sock_by_ip(inet_ntoa(client_addr.sin_addr),sock) < 0){
        //ip无效，reject it
        printf("This ip[%s][%u] is invaild, reject it! \r\n", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
        close(sock);
        return;
    }
    printf("Successfully connected with client: %s:%u\r\n", \
    inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
    
    //加入事件循环
    struct ev_io *w_client = (struct ev_io*) malloc (sizeof(struct ev_io));
    if (w_client == NULL){
        printf("malloc w_client failed \r\n");
        return;
    }
    
	w_client->data = get_channel_by_ip(inet_ntoa(client_addr.sin_addr));
    ev_io_init(w_client, read_cb, sock, EV_READ);
    ev_io_start(loop, w_client);
}




/*int run()
{
    int sd;
    struct ev_io w_accept;
    g_loop = ev_loop_new(EVBACKEND_EPOLL);
    if (NULL == g_loop) {
        printf("loop create failed\r\n");
        return -1;
    }
    if (server_socket_init(&sd, NULL, CCU_TCP_PORT) < 0) {
        printf("server init failed\r\n");
        return -1;
    }
    ev_io_init(&w_accept, accept_cb, sd, EV_READ);
    ev_io_start(g_loop, &w_accept);
    ev_run(g_loop, 0);
    return 0;
}*/

static void loop_tcp_thread(void *arg){
    printf("loop_tcp_thread start!\r\n");    
    int sd;
    
    g_loop = ev_loop_new(EVBACKEND_EPOLL);
    if (NULL == g_loop) {
        printf("loop create failed\r\n");
        return -1;
    }
    if (server_socket_init(&sd, NULL, CCU_TCP_PORT) < 0) {
        printf("server init failed\r\n");
        return -1;
    }
    ev_io_init(&w_accept, accept_cb, sd, EV_READ);
    ev_io_start(g_loop, &w_accept);
    ev_run (g_loop, 0);
    close(sd);
    printf("loop_tcp_thread================== end \n");
	
}


int kk_tcp_channel_ser_send(char* data, int len, char chalMark[DEVICE_CODE_LEN]){
    int ret = 0;
    if (data != NULL){
        int sd = get_sock_by_deviceCode(chalMark);
        if (sd > -1){
            ret = write(sd, data, len);
            if (ret < 0){
                printf("[%s] write failed!!!! \n",__FUNCTION__);
            }
        }
        
    }
}


int kk_TCP_channel_init(ipc_cb cb)
{
    int i = 0;
	int sd;    

	if (g_init == 1){
		printf("kk_TCP_channel_init has been inited \n");
		return -1;
	}
	g_init = 1;
	memset(g_tcp_ctrl, 0, sizeof(kk_tcp_ctrl_t)*MAX_LISTEN_NUM);
	for(i = 0; i < MAX_LISTEN_NUM; i++){
		g_tcp_ctrl[i].sock = -1;
	}    

	if (g_pTh ==NULL && 0 != pthread_create(&g_pTh, NULL, loop_tcp_thread, NULL)) {
        printf("create pthread failed\r\n");
        return -1;
    };
	g_cb = cb;    

    return 0;


}


int kk_TCP_channel_deinit(ipc_type type)
{
    int i = 0;
	
	for(i = 0; i < MAX_LISTEN_NUM; i++){
		if(g_tcp_ctrl[i].sock > -1){
			//close(g_tcp_ctrl[i].sock);
		}
	}

	ev_io_stop(g_loop, &w_accept);
	ev_break(g_loop,EVBREAK_ALL);
    pthread_exit(g_pTh);
    ev_loop_destroy(g_loop);
    g_loop = NULL;
    g_pTh = NULL;
    g_init = 0;

    
	return 0;

}



void *_MutexCreate(void)
{
    int err_num;
    pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
    if (NULL == mutex) {
        return NULL;
    }

    if (0 != (err_num = pthread_mutex_init(mutex, NULL))) {
        printf("create mutex failed");
        free(mutex);
        return NULL;
    }

    return mutex;
}

void _MutexDestroy(void *mutex)
{
    int err_num;

    if (!mutex) {
        printf("mutex want to destroy is NULL!");
        return;
    }
    if (0 != (err_num = pthread_mutex_destroy((pthread_mutex_t *)mutex))) {
        printf("destroy mutex failed");
    }

    free(mutex);
}

void _MutexLock(void *mutex)
{
    int err_num;
    if (0 != (err_num = pthread_mutex_lock((pthread_mutex_t *)mutex))) {
        printf("lock mutex failed: - '%s' (%d)", strerror(err_num), err_num);
    }
}

void _MutexUnlock(void *mutex)
{
    int err_num;
    if (0 != (err_num = pthread_mutex_unlock((pthread_mutex_t *)mutex))) {
        printf("unlock mutex failed - '%s' (%d)", strerror(err_num), err_num);
    }
}


//client
/*初始化服务端*/
typedef struct {
    void *mutex;
    int   sd;
    ipc_cb* cb;
} kk_tcp_client_t;

static kk_tcp_client_t g_client_ctrl;

static int _init_client(){
    memset(&g_client_ctrl, 0 ,sizeof(kk_tcp_client_t));
    /* Create Mutex */
    
    g_client_ctrl.mutex = _MutexCreate();
    if (g_client_ctrl.mutex == NULL) {
        return -1;
    }
    g_client_ctrl.sd = -1;
    
}
static void loop_tcp_client_thread(void *arg){
    printf("loop_tcp_client_thread start!\r\n"); 
    char buf[1024]= {0};
    int ret = 0;
    while(1){
        _MutexLock(g_client_ctrl.mutex);
        ret = read(g_client_ctrl.sd, buf, sizeof(buf));
        _MutexUnlock(g_client_ctrl.mutex);
        if(-1== ret){
            //printf("=================read error \n");
            //break ;
        }else if(ret > 0){
            printf("buf = %s\n",buf);
            if (g_client_ctrl.cb != NULL){
                g_client_ctrl.cb(buf,ret,"");
            }
        }
        usleep(100000);

    }
    printf("loop_tcp_client_thread================== end \n");
	
}

static int client_socket_init(int *sd, char *ipaddr, uint16_t port)
{
    //创建socket
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sock){
        printf("error socket \n");
        goto err1;
    }
    //设置立即释放端口并可以再次使用
    int reuse = 1;
    if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))){
        printf("error setsockopt \n");
        goto err2;
    }
    //设置为非阻塞
    if (-1 == fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK)){
        printf("================== fcntl \n");
        goto err2;
    }
    struct sockaddr_in addr;
    memset(&addr, 0 , sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    if (NULL == ipaddr) {
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
    } else {
        addr.sin_addr.s_addr = inet_addr(ipaddr);
    }
    //连接
    int retry = 0;
    for(;retry < 10; retry++){
        if(-1 != connect(sock, (struct sockaddr *)&addr, sizeof(addr))){
            break;
        }
        printf("==================connect retry=%d \n", retry);
        sleep(1);
    }
    if (retry >= 10){
        printf("==================connect failed \n");
        goto err2;
    }
    
    printf("==================[%s] ok sock=%d \n", __FUNCTION__, sock);
    *sd = sock;
    return 0;
err2:
    close(sock);
err1:
    return -1;
}

int kk_tcp_client_send(char* data, int len){
    int ret = 0;
    if (g_client_ctrl.sd > -1 && data != NULL){
        _MutexLock(g_client_ctrl.mutex);
        ret = write(g_client_ctrl.sd, data, len);
         _MutexUnlock(g_client_ctrl.mutex);
        if (ret < 0){
            printf("[%s] write failed!!!! \n",__FUNCTION__);
        }
    }
}
int kk_tcp_client_init(char ip[MAX_IP_LEN], int port, ipc_cb cb)
{
    kk_tcp_client_deinit();
    _init_client();
    if(-1 == client_socket_init(&g_client_ctrl.sd,ip, port)){
        printf("connect failed \n");
        return -1;
    }

    if (g_pTh ==NULL && 0 != pthread_create(&g_pTh, NULL, loop_tcp_client_thread, NULL)) {
        printf("create pthread failed\r\n");
        return -1;
    };
	g_client_ctrl.cb = cb; 
}

kk_tcp_client_deinit(){
    if (g_client_ctrl.sd > -1){
        close(g_client_ctrl.sd);
    }
    _MutexDestroy(g_client_ctrl.mutex);
}




