#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <resolv.h>
#include <stdlib.h>
#include <sys/time.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>

#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/types.h>
#include <asm/types.h>
#include <netinet/if_ether.h>

#include "dy_socket_server_session.h"
#include "lnxall_list.h"
#include "dy_utils/dy_common.h"

#define BUF_SZ   4096

extern void *socket_server_loop(void *param);

static int add_pkt_data(socket_server_session_t *session, int client_fd, uint8_t *buf, int len)
{
    socket_data_node_t *socket_data = NULL;

    socket_data = malloc(sizeof(socket_data_node_t) + len);
    if (socket_data == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed");
        return -1;
    }
	socket_data->client_fd = client_fd;
    socket_data->buf_len = len;
    memcpy(socket_data->buf, buf, len);

    // 将数据加入LIST
    pthread_mutex_lock(&session->sock_lock);
    list_add_tail(&socket_data->list, &session->rcv_list);
    session->op_code |= SOCKET_DO_RCV_MASK;
    pthread_mutex_unlock(&session->sock_lock);

    pthread_cond_signal(&session->sock_cond);
    return 0;
}


/*
handle_message - 处理每个 socket 上的消息收发
*/
static int handle_message(socket_server_session_t *session, fd_set *master, int socket)
{
    uint8_t buf[BUF_SZ + 1];
    int len;

    /* 开始处理每个新连接上的数据收发 */
    bzero(buf, BUF_SZ + 1);

    /* 接收客户端的消息 */
    while (1)
    {
        if (session->type == SOCKET_TCP)
            len = recv(socket, buf, BUF_SZ, 0);
        else
            len = recvfrom(socket, buf, BUF_SZ, 0, (struct sockaddr *)&session->server_addr, &session->socklen);
        if (len > 0)
        {
            add_pkt_data(session, socket, buf, len);
            if (len < MAXBUF)
            {
                break;
            }
        }
        else
        {
            if (len < 0)
            {
                dy_syslog(LOG_ERR, "receive failed, socketfd:%d %d:'%s'", socket, errno, strerror(errno));
                if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR)) //SOCKET 错误
                {
                    dy_syslog(LOG_ERR, "SOCKET connection error");
                    return -2;
                }
            }
            else
            {
            	session->handle_client_up_down(session, 0, NULL, socket);
		        close (socket);
		        FD_CLR (socket, master);    // remove from master set		        
                dy_syslog(LOG_ERR, "%d disconnect!", socket);
                return -2;
            }
            return -1;
        }
    }
    /* 处理每个新连接上的数据收发结束 */
    return len;
}

static int exec_cmd(char *buff, char *ip, char *result)
{
    int ret = -1;
    char *string = NULL;
    ret = system_reply(buff, &string);
    if (ret == 0)
    {
        if (!string)return 0;
		dy_syslog(LOG_DEBUG, "---string %s", string);
		if(strlen(string))
		{
			char *tmp = strstr(string, ip);
			char *buf = tmp;
			char *str;

		    int i=0;

			dy_syslog(LOG_DEBUG, "-----tmp:%s", tmp);

		    while((str = strtok(buf, " ")) != NULL)
		    {
		        dy_syslog(LOG_DEBUG, "i=%d,%s\n",++i,str);
		        buf=NULL;
				if(i >= 4)
				{
					strncpy(result, str, 32);
					break;
				}
		    }
			
			dy_syslog(LOG_DEBUG, "result:%s", result);
		}
        free(string);
		return 1;
    }
    return -1;
}

int getmac( int sockfd) 
{
    char mac[32] = {0};
	exec_cmd("cat /proc/net/arp", "10.3.1.67", mac);
	dy_syslog(LOG_DEBUG, "-----mac:%s", mac);
    return 0;  
}


static void new_connection(socket_server_session_t *session, fd_set *master_fds, int *fdmax)
{
    int newfd;
    struct sockaddr_in client_addr;
    socklen_t sin_size;

    sin_size = sizeof (client_addr);
    newfd = accept (session->server_sockfd, (struct sockaddr *) &client_addr,&sin_size);
    if (newfd == -1)
	{
		dy_syslog(LOG_ERR, "accept newfd=%d\n",newfd);
        return;
    }
	else
	{
        dy_syslog(LOG_DEBUG, "accept the new connection from newfd=%d\n",newfd);
        FD_SET (newfd, master_fds);
        if (newfd > *fdmax)
		{
            *fdmax = newfd;
        }

		socket_connect_node_t *connect_node = calloc(1, sizeof(socket_connect_node_t));
		strncpy(connect_node->hostname, inet_ntoa(client_addr.sin_addr), sizeof(connect_node->hostname));
		connect_node->socket_fd = newfd;

		dy_syslog(LOG_DEBUG, "======hostname:%s socket_fd %d", connect_node->hostname, connect_node->socket_fd);		
		getmac(newfd);
		session->handle_client_up_down(session, 1, connect_node->hostname, newfd);		
		list_add_tail(&connect_node->list, &session->connect_list);
    }
}

static int init_server_socket(socket_server_session_t *session, int port)
{
    struct sockaddr_in server;

    int server_port_num = port;
    session->server_sockfd = socket (AF_INET, SOCK_STREAM, 0);
    if (session->server_sockfd < 0)
    {
        dy_syslog(LOG_ERR, "server: socket");
        return -1;
    }

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons (server_port_num);
    memset (&(server.sin_zero), 0, sizeof (server.sin_zero));

    if (-1 == bind (session->server_sockfd, (struct sockaddr *) &server,
                sizeof (struct sockaddr)))
    {
        close (session->server_sockfd);
        dy_syslog(LOG_ERR, "server: bind");
        return -1;
    }

    /* listen */
    if (-1 == listen (session->server_sockfd, 10))
    {
        dy_syslog(LOG_ERR, "listen");
        return -1;    
    }

    dy_syslog(LOG_INFO, "server port %d: waiting for connections... \n",server_port_num);
    return 0;
}


static int start_socket_server_loop(socket_server_session_t *session)
{
    int i = 0;
    fd_set read_fds;
    int fdmax;
    int ret;
	struct timeval timeout;

    ret = init_server_socket(session, session->socket_server_port);
    if (ret != 0)
	{
        dy_syslog(LOG_ERR, "unable to create socket!\n");
        goto out;
    }

    FD_ZERO (&session->master);
    FD_SET (session->server_sockfd, &session->master);
    fdmax = session->server_sockfd;
	session->connected = 1;

    while (1)
	{
		timeout.tv_usec = 0;
        timeout.tv_sec = 3;
		
        read_fds = session->master;
		ret = select (fdmax + 1, &read_fds, NULL, NULL, &timeout);
        if (ret == -1)
		{
			dy_syslog(LOG_ERR, "select %s:%d error:%s\n", session->socket_server_addr, session->socket_server_port, strerror(errno));
			break;
        }

		if(ret > 0)
		{		
	        for (i = 0; i <= fdmax; i++)
			{
	            if (FD_ISSET (i, &read_fds)) {
	                if (i == session->server_sockfd)
					{
	                    new_connection(session, &session->master, &fdmax);
	                }
					else
					{
						handle_message(session, &session->master, i);
	                }
	            }
	        }
		}
    }
	
out:
	if (session->server_sockfd != -1)
	{
		close(session->server_sockfd);
	}
	session->server_sockfd = -1;
	session->connected = 0;
	return -1;

}

int dy_socket_server_session_snd(socket_server_session_t *session, int client_fd, char *buf, int len, char *user_param)
{
    socket_data_node_t *socket_data = NULL;

    if (session->connected == 0)
    {
        return -1;
    }

    socket_data = malloc(sizeof(socket_data_node_t) + len);
    if (socket_data == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed");
        return -1;
    }
	socket_data->client_fd = client_fd;
    socket_data->buf_len = len;
    memcpy(socket_data->buf, buf, len);
	if(user_param)
		strncpy(socket_data->user_param, user_param, sizeof(socket_data->user_param));

    // 将数据加入LIST
    pthread_mutex_lock(&session->sock_lock);
    list_add_tail(&socket_data->list, &session->snd_list);
    session->op_code |= SOCKET_DO_SND_MASK;
    pthread_mutex_unlock(&session->sock_lock);

    pthread_cond_signal(&session->sock_cond);

    return 0;
}

static void *start_socket_server(void *param)
{
    socket_server_session_t *session = (socket_server_session_t *)param;
    while (1)
    {
        start_socket_server_loop(session);
        sleep(10);
    }
}

int dy_session_close_client(socket_server_session_t *session, int socket)
{
	socket_connect_node_t *connect_node = NULL;
    socket_connect_node_t *tmp = NULL;

	session->handle_client_up_down(session, 0, NULL, socket);
    close (socket);
    FD_CLR (socket, &session->master);    // remove from master set	

	dy_syslog(LOG_INFO, "%d disconnect!", socket);	

    if (list_empty(&session->connect_list))
    {
        return 0;
    }
    list_for_each_entry_safe(connect_node, tmp, &session->connect_list, list)
    {
    	if(connect_node->socket_fd == socket)
		{
			list_del(&connect_node->list);
			free(connect_node);
		}
    }
}


int dy_socket_server_session_init(socket_server_session_t *session)
{
    pthread_t thread_sock;
    pthread_t thread_loop;

    INIT_LIST_HEAD(&session->rcv_list);
    INIT_LIST_HEAD(&session->snd_list);
	INIT_LIST_HEAD(&session->connect_list);
    pthread_mutex_init(&session->sock_lock, NULL);
    pthread_cond_init(&session->sock_cond, NULL);

    pthread_create(&thread_loop, 0, socket_server_loop, (void *)session);
    pthread_create(&thread_sock, 0, start_socket_server, (void *)session);
}
