// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void RemoteIO_SendMouseMove(int X,int Y)
{
	char data[12];
	memcpy(data+0,(char*)&X,4);
	memcpy(data+4,(char*)&Y,4);
	memset(data+8,0,4);
	RemoteIO_SendCommand(RIO_MOUSE_MOVE,(char*)&data,12);
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void RemoteIO_SendMouseMove(int X,int Y)
{
	char data[12];
	memcpy(data+0,(char*)&X,4);
	memcpy(data+4,(char*)&Y,4);
	memset(data+8,0,4);
	RemoteIO_SendCommand(RIO_MOUSE_MOVE,(char*)&data,12,ILibAsyncSocket_MemoryOwnership_USER);
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void RemoteIO_SendMouseDown(int X,int Y,int Button)
{
	char data[12];
	memcpy(data+0,(char*)&X,4);
	memcpy(data+4,(char*)&Y,4);
	memcpy(data+8,(char*)&Button,4);
	RemoteIO_SendCommand(RIO_MOUSE_DOWN,(char*)&data,12,ILibAsyncSocket_MemoryOwnership_USER);
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void RemoteIO_SendMouseDown(int X,int Y,int Button)
{
	char data[12];
	memcpy(data+0,(char*)&X,4);
	memcpy(data+4,(char*)&Y,4);
	memcpy(data+8,(char*)&Button,4);
	RemoteIO_SendCommand(RIO_MOUSE_DOWN,(char*)&data,12);
}
// PRIVATE - User internaly to build and send XRT commands. If XRT is not connected,
// the command is ignored.
void RemoteIO_SendCommand(unsigned short command, char* data, int datalength, int userfree)
{
	char header[4];
	header[0] = (datalength+4) & 0xFF;
	header[1] = (datalength+4) >> 8;
	header[2] = command & 0xFF;
	header[3] = command >> 8;

	if (RIO != NULL)
	{
		if (datalength > 0xFFFB) RemoteIO_SendCommand(RIO_JUMBO,(char*)&datalength,4,ILibAsyncSocket_MemoryOwnership_USER);
		ILibAsyncSocket_Send(RIO->Session,header,4,ILibAsyncSocket_MemoryOwnership_USER);
		if (data != NULL) ILibAsyncSocket_Send(RIO->Session,data,datalength,userfree);
	}
}
// PRIVATE - User internaly to build and send XRT commands. If XRT is not connected,
// the command is ignored.
void RemoteIO_SendCommand(unsigned short command, char* data, int datalength)
{
	char header[4];
	header[0] = (datalength+4) & 0xFF;
	header[1] = (datalength+4) >> 8;
	header[2] = command & 0xFF;
	header[3] = command >> 8;

	if (RIO != NULL)
	{
		if (datalength > 0xFFFB) RemoteIO_SendCommand(RIO_JUMBO,(char*)&datalength,4);
		ILibAsyncSend(RIO->Session,header,4);
		if (data != NULL) ILibAsyncSend(RIO->Session,data,datalength);
	}
}
// PRIVATE - Called by the AsyncSocket module when the XRT connection is established. Once connected,
// the stack immidiatly sends the XRT REQUEST command to get things started.
void OnRemoteIOConnectSink(void* socketModule, int Connected, void *user)
{
	if (Connected != 0)
	{
		// Send REQUEST command
		RemoteIO_SendCommand(RIO_REQUEST,RIO->PeerConnection,(int)strlen(RIO->PeerConnection),ILibAsyncSocket_MemoryOwnership_USER);
	}
	else
	{
		sem_wait(&RemoteIOLock);

		// Set new connection state
		if (RIO->PeerConnection != NULL) free(RIO->PeerConnection);
		RIO->PeerConnection = NULL;

		// Event the new connection
		UpnpSetState_RemoteIOClient_RemoteIO_PeerConnection(RIO->RIOmicroStack,"");

		sem_post(&RemoteIOLock);

		// Event the user
		if (RemoteIOConnectionChanged != NULL) RemoteIOConnectionChanged(RIO->PeerConnection);
	}
}
// PRIVATE - Called by the AsyncSocket module when the XRT connection is established. Once connected,
// the stack immidiatly sends the XRT REQUEST command to get things started.
void OnRemoteIOConnectSink(void* socketModule, int Connected)
{
	if (Connected != 0)
	{
		// Send REQUEST command
		RemoteIO_SendCommand(RIO_REQUEST,RIO->PeerConnection,(int)strlen(RIO->PeerConnection));
	}
	else
	{
		sem_wait(&RemoteIOLock);

		// Set new connection state
		if (RIO->PeerConnection != NULL) RIO_FREE(RIO->PeerConnection);
		RIO->PeerConnection = NULL;

		// Event the new connection
		UpnpSetState_RemoteIO_PeerConnection(RIO->RIOmicroStack,"");

		sem_post(&RemoteIOLock);

		// Event the user
		if (RemoteIOConnectionChanged != NULL) RemoteIOConnectionChanged(RIO->PeerConnection);
	}
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void UpnpRemoteInput_InputKeyPress(void* upnptoken,int key)
{
	RemoteIO_SendCommand(RIO_KEY_PRESS,(char*)&key,4);
	UpnpResponse_RemoteInput_InputKeyPress(upnptoken);
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void RemoteIO_SendKeyDown(int key)
{
	RemoteIO_SendCommand(RIO_KEY_DOWN,(char*)&key,4,ILibAsyncSocket_MemoryOwnership_USER);
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void RemoteIO_SendKeyPress(int key)
{
	RemoteIO_SendCommand(RIO_KEY_PRESS,(char*)&key,4,ILibAsyncSocket_MemoryOwnership_USER);
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void UpnpRemoteIOClient_RemoteInput_InputKeyDown(void* upnptoken,int key)
{
	RemoteIO_SendCommand(RIO_KEY_DOWN,(char*)&key,4,ILibAsyncSocket_MemoryOwnership_USER);
	UpnpResponse_RemoteIOClient_RemoteInput_InputKeyDown(upnptoken);
}
void XrtVideo_CommandSink(unsigned short command, char* data, int datalength)
{
	struct RIO_COMMAND_DRAWFILLBOX* cmd_fillbox;
	struct RIO_COMMAND_DRAWIMAGE* cmd_image;
	struct RIO_COMMAND_ALLOCATE* cmd_allocate;
	struct RIO_COMMAND_OBJECT* cmd_setobject;
	struct RIO_COMMAND_COPYOBJECT* cmd_copyobject;
	struct RIO_XWPC_BIGIMAGE* cmd_bigimage;

	switch (command)
	{
		case RIO_RESET:
			printf("RemoteIO RESET Command\r\n");
			// TODO: Reset, debuggin do nothing
			break;
		case RIO_EXIT:
			printf("RemoteIO EXIT Command\r\n");
			// TODO: Exit, debugging do nothing.
			break;
		case RIO_DRAWFILLBOX:
			if (datalength != 11) break;
			cmd_fillbox = (struct RIO_COMMAND_DRAWFILLBOX*)data;
			printf("RemoteIO DRAWFILLBOX Command: (X=%d,Y=%d,W=%d,H=%d,CR=%d,CG=%d,CB=%d)\r\n",cmd_fillbox->x,cmd_fillbox->y,cmd_fillbox->w,cmd_fillbox->h,cmd_fillbox->r,cmd_fillbox->g,cmd_fillbox->b);
			// TODO: Draw Fill Box on screen. Box location & color provided
			break;
		case RIO_DRAWIMAGE:
			if (datalength < 4) break;
			cmd_image = (struct RIO_COMMAND_DRAWIMAGE*)data;
			printf("RemoteIO DRAWIMAGE Command: (X=%d,Y=%d,SIZE=%d)\r\n",cmd_image->x,cmd_image->y,datalength-4);
			// TODO: Draw Image at (data+4).
			// Image width and height must be obtained by decoding the image.
			// Image format is set in the main() method.
			break;
		case RIO_ALLOCATE:
			if (datalength < 10) break;
			cmd_allocate = (struct RIO_COMMAND_ALLOCATE*)data;
			printf("RemoteIO ALLOCATE Command: (X=%d,Y=%d,W=%d,H=%d,ID=%d)\r\n",cmd_allocate->x,cmd_allocate->y,cmd_allocate->w,cmd_allocate->h,cmd_allocate->id);
			// TODO: OPTIONAL - Place video window
			// Generaly, ID = 1 for the main video window, Window is allocated but should not show
			// unless video is actualy playing.
			break;
		case RIO_SETOBJECT:
			if (datalength < 4) break;
			cmd_setobject = (struct RIO_COMMAND_OBJECT*)data;
			printf("RemoteIO SETOBJECT Command: (ID=%d,DataLength=%d)\r\n",cmd_setobject->id,datalength-2);
			// TODO: OPTIONAL - Place this object into object cache
			break;
		case RIO_CLEAROBJECT:
			if (datalength < 2) break;
			cmd_setobject = (struct RIO_COMMAND_OBJECT*)data;
			printf("RemoteIO CLEAROBJECT Command: (ID=%d)\r\n",cmd_setobject->id);
			// TODO: OPTIONAL - Clear object ID from cache
			break;
		case RIO_COPYOBJECT:
			if (datalength < 6) break;
			cmd_copyobject = (struct RIO_COMMAND_COPYOBJECT*)data;
			printf("RemoteIO COPYOBJECT Command: (ID=%d,X=%d,Y=%d)\r\n",cmd_copyobject->id,cmd_copyobject->x,cmd_copyobject->y);
			// TODO: OPTIONAL - Copy object ID to location X,Y on screen
			break;
		case RIO_XWPC_BIGIMAGE:
			cmd_bigimage = (struct RIO_XWPC_BIGIMAGE*)data;
			printf("RemoteIO XWPC_BIGIMAGE Command: (X=%d,Y=%d,DataLength=%d)\r\n",cmd_bigimage->x,cmd_bigimage->y,datalength-12);
			// TODO: PROPRIATARY OPTIONAL - Draw image (data+12) to position X,Y on screen
			break;
		case RIO_XWPC_PING:
			// Respon with PONG command
			printf("RemoteIO got PING, sending PONG\r\n");
			RemoteIO_SendCommand(RIO_XWPC_PONG,"X",1,1);
			break;
		default:
			printf("XrtVideo_CommandSink: Command = %d, DataLength = %d\r\n",command,datalength);
			// TODO: Do nothing. Used for custom commands such as an LCD panel, LED's and more.
			break;
	}
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void UpnpRemoteInput_InputKeyDown(void* upnptoken,int key)
{
	RemoteIO_SendCommand(RIO_KEY_DOWN,(char*)&key,4);
	UpnpResponse_RemoteInput_InputKeyDown(upnptoken);
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void RemoteIO_SendKeyDown(int key)
{
	RemoteIO_SendCommand(RIO_KEY_DOWN,(char*)&key,4);
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void RemoteIO_SendKeyUp(int key)
{
	RemoteIO_SendCommand(RIO_KEY_UP,(char*)&key,4);
}
// Called by the UPnP Remote I/O Microstack
// Implements the InputKeyPress call, lets a CP inject user input into this RIO client.
void RemoteIO_SendKeyPress(int key)
{
	RemoteIO_SendCommand(RIO_KEY_PRESS,(char*)&key,4);
}