#include	<sys/types.h>
#include	<sys/stat.h>
#include	<sys/param.h>
#include	<limits.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<sys/select.h>
#include 	<arpa/inet.h>
#include	<time.h>
#include	<fcntl.h>
#include	<errno.h>
#include	<unistd.h>
#include	<ctype.h>
#include	<stdarg.h>
#include	<string.h>

#include	<sched.h>
#include	<pthread.h>
#include	<semaphore.h>
#include    <net/if.h> 
#include    <net/if_arp.h>
#include    <sys/ioctl.h>
#include	<sys/types.h>
#include	<sys/socket.h>
#include	<netinet/in.h>
#include	<netdb.h>
#include	<poll.h>

#define CAM_VISCA_UDP		// use udp
//#define CAM_VISCA_TCP		// use tcp

#define CAM_IPADDRESS	"192.168.5.163"
#define CAM_VISCA_PORT	1259

static int       s_thread_run    = 0;
static pthread_t s_thread_handle = 0;
static unsigned long s_cam_ipaddr = 0;
static int s_socket_fd = -1;

static int socket_nonblock(int socket, int enable)
{
    if (enable)
        return fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) | O_NONBLOCK);
    else
        return fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) & ~O_NONBLOCK);
}

static long url_getipaddress(const char *insrc)
{
	unsigned long bRet = INADDR_NONE;
	struct hostent *hostname = NULL;
	
	bRet = inet_addr(insrc);
	if(bRet == INADDR_NONE)
	{
		hostname = gethostbyname(insrc);
		if(hostname)
		{
			bRet = *((unsigned long*)hostname->h_addr);
		}
	}
	return (long)bRet;
}

static int create_socket(void)
{
	int fd = -1;
	struct sockaddr_in sockAddr;
#if defined(CAM_VISCA_TCP)
	int iRet;
	fd_set write_set;
	struct timeval tv;
	fd = socket(AF_INET, SOCK_STREAM, 0);
	socket_nonblock(fd, 1);
	memset(&sockAddr, 0, sizeof(sockAddr));
	sockAddr.sin_family	  = AF_INET;
	sockAddr.sin_addr.s_addr = s_cam_ipaddr;
	sockAddr.sin_port = htons(CAM_VISCA_PORT);
	iRet = connect(fd, (struct sockaddr*)&sockAddr, sizeof(sockAddr));
	FD_ZERO(&write_set);
	FD_SET(fd, &write_set);
	tv.tv_sec  = 2;
	tv.tv_usec = 0;
	iRet = select(fd + 1, NULL, &write_set, NULL, &tv);
	if(iRet != 1 || !FD_ISSET(fd, &write_set))
	{
		printf("connect %s failed\n", CAM_IPADDRESS);
		close(fd);
		return -1;
	}
#elif defined(CAM_VISCA_UDP)
	fd = socket(AF_INET, SOCK_DGRAM, 0);
	memset(&sockAddr, 0, sizeof(sockAddr));
	sockAddr.sin_family	  = AF_INET;
	sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	sockAddr.sin_port = htons(8889);
	if(bind(fd, (struct sockaddr*)&sockAddr, sizeof(sockAddr)) < 0)
	{
		printf("bind port failed\n");
		close(fd);
		return -1;
	}
#else
	#error "error for not define type"
#endif//CAM_VISCA_TCP

	return fd;
}

static void thread_recv(void)
{
	int i, iRet;
	struct timeval tv;
	fd_set read_set;
	unsigned char byBuffer[256];
	while(s_thread_run)
	{
		FD_ZERO(&read_set);
		FD_SET(s_socket_fd, &read_set);
		tv.tv_sec  = 1;
		tv.tv_usec = 0;
		iRet = select(s_socket_fd + 1, &read_set, NULL, NULL, &tv);
		if(iRet <= 0)
			continue;
		iRet = recv(s_socket_fd, (char*)byBuffer, sizeof(byBuffer), 0);
		if(iRet <= 0)
			continue;
		for(i=0; i<iRet; i++)
		{
			printf("%02X ", byBuffer[i]);
		}
		printf("\n");
	}
}
static int socked_send_cmd(unsigned char *pBuffer, int nLen)
{
	int iRet = 0;
#if defined(CAM_VISCA_UDP)
	struct sockaddr_in dest_addr;
	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.sin_family = AF_INET;
	dest_addr.sin_addr.s_addr = s_cam_ipaddr;
	dest_addr.sin_port = htons(CAM_VISCA_PORT);
	iRet = sendto(s_socket_fd, pBuffer, nLen, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
#elif defined(CAM_VISCA_TCP)
	iRet = send(s_socket_fd, pBuffer, nLen, 0);
#endif//CAM_VISCA_UDP
	return iRet == nLen ? 0 : -1;
}

static unsigned char s_PTDriveCmd[]   = {0x81, 0x01, 0x06, 0x01, 0x18, 0x14, 0x03, 0x01, 0xFF};
static unsigned char s_ZoomDriveCmd[] = {0x81, 0x01, 0x04, 0x07, 0x00, 0xFF};
int main(int argc, char *argv[])
{
	int nPTZMoveType = 0; // 1 - PT, 2 - Zoom
	s_cam_ipaddr = url_getipaddress(CAM_IPADDRESS);
	s_socket_fd  = create_socket();
	if(s_socket_fd == -1)
	{
		exit(-1);
		return 0;
	}
	s_thread_run = 1;
	pthread_create(&s_thread_handle, NULL, (void*)thread_recv, NULL);
	printf("net_visca run...\n");
	while(1)
	{
		char cc = getchar();
		if(cc == 'q')
			break;
		else if(cc == 'l')
		{
			// left
			nPTZMoveType = 1;
			s_PTDriveCmd[6] = 0x01;
			s_PTDriveCmd[7] = 0x03;
			socked_send_cmd(s_PTDriveCmd, sizeof(s_PTDriveCmd));
		}
		else if(cc == 'r')
		{
			// right
			nPTZMoveType = 1;
			s_PTDriveCmd[6] = 0x02;
			s_PTDriveCmd[7] = 0x01;
			socked_send_cmd(s_PTDriveCmd, sizeof(s_PTDriveCmd));
		}
		else if(cc == 'u')
		{
			// up
			nPTZMoveType = 1;
			s_PTDriveCmd[6] = 0x03;
			s_PTDriveCmd[7] = 0x01;
			socked_send_cmd(s_PTDriveCmd, sizeof(s_PTDriveCmd));
		}
		else if(cc == 'd')
		{
			// down
			nPTZMoveType = 1;
			s_PTDriveCmd[6] = 0x03;
			s_PTDriveCmd[7] = 0x02;
			socked_send_cmd(s_PTDriveCmd, sizeof(s_PTDriveCmd));
		}
		else if(cc == 'w')
		{
			// zoom wide
			nPTZMoveType = 2;
			s_ZoomDriveCmd[4] = 0x03;
			socked_send_cmd(s_ZoomDriveCmd, sizeof(s_ZoomDriveCmd));
		}
		else if(cc == 't')
		{
			// zoom tele
			nPTZMoveType = 2;
			s_ZoomDriveCmd[4] = 0x02;
			socked_send_cmd(s_ZoomDriveCmd, sizeof(s_ZoomDriveCmd));
		}
		else if(cc == 's')
		{
			// stop
			if(nPTZMoveType == 1)
			{
				s_PTDriveCmd[6] = 0x03;
				s_PTDriveCmd[7] = 0x03;
				socked_send_cmd(s_PTDriveCmd, sizeof(s_PTDriveCmd));
			}
			else if(nPTZMoveType == 2)
			{
				s_ZoomDriveCmd[4] = 0x00;
				socked_send_cmd(s_ZoomDriveCmd, sizeof(s_ZoomDriveCmd));
			}
			nPTZMoveType = 0;
		}
	}
	s_thread_run = 0;
	pthread_join(s_thread_handle, NULL);
	close(s_socket_fd);
	return 0;
}
