/*
             LUFA Library
     Copyright (C) Dean Camera, 2010.
              
  dean [at] fourwalledcubicle [dot] com
      www.fourwalledcubicle.com
*/

/*
  Copyright 2010  Dean Camera (dean [at] fourwalledcubicle [dot] com)

  Permission to use, copy, modify, distribute, and sell this 
  software and its documentation for any purpose is hereby granted
  without fee, provided that the above copyright notice appear in 
  all copies and that both that the copyright notice and this
  permission notice and warranty disclaimer appear in supporting 
  documentation, and that the name of the author not be used in 
  advertising or publicity pertaining to distribution of the 
  software without specific, written prior permission.

  The author disclaim all warranties with regard to this
  software, including all implied warranties of merchantability
  and fitness.  In no event shall the author be liable for any
  special, indirect or consequential damages or any damages
  whatsoever resulting from loss of use, data or profits, whether
  in an action of contract, negligence or other tortious action,
  arising out of or in connection with the use or performance of
  this software.
*/

#define  INCLUDE_FROM_SIDESHOWCOMMANDS_H
#include "SideshowCommands.h"

UNICODE_STRING_t(80) UserSID       = {LengthInBytes: sizeof(SECURITY_INTERACTIVE_RID_SID),
                                      UnicodeString: SECURITY_INTERACTIVE_RID_SID};

Unicode_String_t DeviceName        = {LengthInBytes: sizeof(L"LUFA Sideshow Device"),
                                      UnicodeString: L"LUFA Sideshow Device"};

Unicode_String_t Manufacturer      = {LengthInBytes: sizeof(L"Dean Camera"),
                                      UnicodeString: L"Dean Camera"};

Unicode_String_t SupportedLanguage = {LengthInBytes: sizeof(L"en-US:1"),
                                      UnicodeString: L"en-US:1"};
									  
void Sideshow_ProcessCommandPacket(void)
{
	SideShow_PacketHeader_t PacketHeader;
	
	Endpoint_SelectEndpoint(SIDESHOW_OUT_EPNUM);	
	Endpoint_Read_Stream_LE(&PacketHeader, sizeof(SideShow_PacketHeader_t));
	
	PacketHeader.Type.Response = true;

	printf("\r\nCmd: %lX", (PacketHeader.Type.TypeLong & 0x00FFFFFF));

	switch (PacketHeader.Type.TypeLong & 0x00FFFFFF)
	{
		case SIDESHOW_CMD_PING:
			SideShow_Ping(&PacketHeader);
			break;
		case SIDESHOW_CMD_SYNC:
			SideShow_Sync(&PacketHeader);
			break;
		case SIDESHOW_CMD_GET_CURRENT_USER:
			SideShow_GetCurrentUser(&PacketHeader);
			break;
		case SIDESHOW_CMD_SET_CURRENT_USER:
			SideShow_SetCurrentUser(&PacketHeader);
			break;
		case SIDESHOW_CMD_GET_CAPABILITIES:
			SideShow_GetCapabilities(&PacketHeader);
			break;			
		case SIDESHOW_CMD_GET_DEVICE_NAME:
			SideShow_GetString(&PacketHeader, &DeviceName);
			break;
		case SIDESHOW_CMD_GET_MANUFACTURER:
			SideShow_GetString(&PacketHeader, &Manufacturer);
			break;
		case SIDESHOW_CMD_GET_APPLICATION_ORDER:
			SideShow_GetApplicationOrder(&PacketHeader);
			break;
		case SIDESHOW_CMD_GET_SUPPORTED_ENDPOINTS:
			SideShow_GetSupportedEndpoints(&PacketHeader);
			break;
		case SIDESHOW_CMD_ADD_APPLICATION:
			SideShow_AddApplication(&PacketHeader);
			break;
		case SIDESHOW_CMD_ADD_CONTENT:
			SideShow_AddContent(&PacketHeader);
			break;
		case SIDESHOW_CMD_DELETE_CONTENT:
			SideShow_DeleteContent(&PacketHeader);
			break;
		case SIDESHOW_CMD_DELETE_ALL_CONTENT:
			SideShow_DeleteAllContent(&PacketHeader);
			break;		
		case SIDESHOW_CMD_DELETE_APPLICATION:
			SideShow_DeleteApplication(&PacketHeader);
			break;
		case SIDESHOW_CMD_DELETE_ALL_APPLICATIONS:
			SideShow_DeleteAllApplications(&PacketHeader);
			break;
		default:
			PacketHeader.Length -= sizeof(SideShow_PacketHeader_t);

			Endpoint_Discard_Stream(PacketHeader.Length);
			Endpoint_ClearOUT();

			PacketHeader.Length   = sizeof(SideShow_PacketHeader_t);
			PacketHeader.Type.NAK = true;
			
			Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);	
			Endpoint_Write_Stream_LE(&PacketHeader, sizeof(SideShow_PacketHeader_t));		
			Endpoint_ClearIN();
			
			printf(" UNK");
	}
}

static void SideShow_Ping(SideShow_PacketHeader_t* const PacketHeader)
{
	Endpoint_ClearOUT();

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);	
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));		
	Endpoint_ClearIN();
}

static void SideShow_Sync(SideShow_PacketHeader_t* const PacketHeader)
{
	GUID_t ProtocolGUID;

	Endpoint_Read_Stream_LE(&ProtocolGUID, sizeof(GUID_t));
	Endpoint_ClearOUT();
	
	if (!(GUID_COMPARE(&ProtocolGUID, (uint32_t[])STANDARD_PROTOCOL_GUID)))
	  PacketHeader->Type.NAK = true;
	
	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));		
	Endpoint_Write_Stream_LE(&ProtocolGUID, sizeof(GUID_t));
	Endpoint_ClearIN();
}

static void SideShow_GetCurrentUser(SideShow_PacketHeader_t* const PacketHeader)
{
	Endpoint_ClearOUT();

	PacketHeader->Length = sizeof(SideShow_PacketHeader_t) + sizeof(uint32_t) + UserSID.LengthInBytes;

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	SideShow_Write_Unicode_String(&UserSID);
	Endpoint_ClearIN();
}

static void SideShow_SetCurrentUser(SideShow_PacketHeader_t* const PacketHeader)
{
	SideShow_Read_Unicode_String(&UserSID, sizeof(UserSID.UnicodeString));
	Endpoint_ClearOUT();
	
	PacketHeader->Length = sizeof(SideShow_PacketHeader_t);

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	Endpoint_ClearIN();
}

static void SideShow_GetCapabilities(SideShow_PacketHeader_t* const PacketHeader)
{
	SideShow_PropertyKey_t  Property;
	SideShow_PropertyData_t PropertyData;

	Endpoint_Read_Stream_LE(&Property, sizeof(SideShow_PropertyKey_t));
	Endpoint_ClearOUT();
	
	printf(" ID: %lu", Property.PropertyID);

	PacketHeader->Length = sizeof(SideShow_PacketHeader_t);

	if (GUID_COMPARE(&Property.PropertyGUID, (uint32_t[])SIDESHOW_PROPERTY_GUID))
	{
		switch (Property.PropertyID)
		{
			case PROPERTY_SIDESHOW_SCREENTYPE:
				PropertyData.DataType    = VT_I4;
				PropertyData.Data.Data32 = ScreenText;
				PacketHeader->Length += sizeof(uint32_t);
				
				break;
			case PROPERTY_SIDESHOW_SCREENWIDTH:
			case PROPERTY_SIDESHOW_CLIENTWIDTH:
				PropertyData.DataType    = VT_UI2;
				PropertyData.Data.Data16 = 16;
				PacketHeader->Length += sizeof(uint16_t);
			
				break;
			case PROPERTY_SIDESHOW_SCREENHEIGHT:
			case PROPERTY_SIDESHOW_CLIENTHEIGHT:
				PropertyData.DataType    = VT_UI2;
				PropertyData.Data.Data16 = 2;
				PacketHeader->Length += sizeof(uint16_t);
			
				break;
			case PROPERTY_SIDESHOW_COLORDEPTH:
				PropertyData.DataType    = VT_UI2;
				PropertyData.Data.Data16 = 1;
				PacketHeader->Length += sizeof(uint16_t);
			
				break;
			case PROPERTY_SIDESHOW_COLORTYPE:
				PropertyData.DataType    = VT_UI2;
				PropertyData.Data.Data16 = BlackAndWhiteDisplay;
				PacketHeader->Length += sizeof(uint16_t);
			
				break;
			case PROPERTY_SIDESHOW_DATACACHE:
				PropertyData.DataType    = VT_BOOL;
				PropertyData.Data.Data16 = false;
				PacketHeader->Length += sizeof(uint16_t);
			
				break;
			case PROPERTY_SIDESHOW_SUPPORTEDLANGS:
			case PROPERTY_SIDESHOW_CURRENTLANG:
				PropertyData.DataType    = VT_LPWSTR;
				PropertyData.Data.DataPointer = &SupportedLanguage;
				PacketHeader->Length += SupportedLanguage.LengthInBytes;
			
				break;
			default:
				PropertyData.DataType    = VT_EMPTY;
				break;
		}
	}
	else if (GUID_COMPARE(&Property.PropertyGUID, (uint32_t[])DEVICE_PROPERTY_GUID))
	{
		switch (Property.PropertyID)
		{
			case PROPERTY_DEVICE_DEVICETYPE:
				PropertyData.DataType    = VT_UI4;
				PropertyData.Data.Data32 = GenericDevice;
				PacketHeader->Length += sizeof(uint32_t);
				
				break;
		}
	}	
	else
	{
		PacketHeader->Type.NAK = true;		
		
		printf(" WRONG GUID");
		printf(" %lX %lX %lX %lX", Property.PropertyGUID.Chunks[0], Property.PropertyGUID.Chunks[1],
		                           Property.PropertyGUID.Chunks[2],  Property.PropertyGUID.Chunks[3]);		
	}

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	
	if (!(PacketHeader->Type.NAK))
	{
		switch (PropertyData.DataType)
		{
			case VT_UI4:
			case VT_I4:
				Endpoint_Write_Stream_LE(&PropertyData.Data.Data32, sizeof(uint32_t));
				break;
			case VT_UI2:
			case VT_I2:
			case VT_BOOL:
				Endpoint_Write_Stream_LE(&PropertyData.Data.Data16, sizeof(uint16_t));
				break;
			case VT_LPWSTR:
				SideShow_Write_Unicode_String((Unicode_String_t*)PropertyData.Data.Data16);
				break;
		}
	}
	
	Endpoint_ClearIN();
	return;
}

static void SideShow_GetString(SideShow_PacketHeader_t* const PacketHeader, void* const UnicodeStruct)
{
	Endpoint_ClearOUT();

	PacketHeader->Length = sizeof(SideShow_PacketHeader_t) +
	                       sizeof(uint32_t) + ((Unicode_String_t*)UnicodeStruct)->LengthInBytes;
	
	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	SideShow_Write_Unicode_String(UnicodeStruct);
	Endpoint_ClearIN();
}

static void SideShow_GetApplicationOrder(SideShow_PacketHeader_t* const PacketHeader)
{
	uint8_t  TotalApplications = 0;
		   
	Endpoint_ClearOUT();

	for (uint8_t App = 0; App < MAX_APPLICATIONS; App++)
	{
		if (InstalledApplications[App].InUse)
		  TotalApplications++;
	}

	PacketHeader->Length = sizeof(SideShow_PacketHeader_t) +
	                       sizeof(uint32_t) + (TotalApplications * sizeof(GUID_t));
	
	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	Endpoint_Write_DWord_LE(TotalApplications);
	
	for (uint8_t App = 0; App < MAX_APPLICATIONS; App++)
	{
		if (InstalledApplications[App].InUse)
		  Endpoint_Write_Stream_LE(&InstalledApplications[App].ApplicationID, sizeof(GUID_t));
	}

	Endpoint_ClearIN();
}

static void SideShow_GetSupportedEndpoints(SideShow_PacketHeader_t* const PacketHeader)
{
	GUID_t SupportedEndpointGUID = (GUID_t){Chunks: SIMPLE_CONTENT_FORMAT_GUID};

	Endpoint_ClearOUT();

	PacketHeader->Length = sizeof(SideShow_PacketHeader_t) + sizeof(uint32_t) + sizeof(GUID_t);
	
	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	Endpoint_Write_DWord_LE(1);
	Endpoint_Write_Stream_LE(&SupportedEndpointGUID, sizeof(GUID_t));
	Endpoint_ClearIN();
}

static void SideShow_AddApplication(SideShow_PacketHeader_t* const PacketHeader)
{
	SideShow_Application_t* CurrApp;
	GUID_t                  ApplicationID;

	Endpoint_Read_Stream_LE(&ApplicationID, sizeof(GUID_t));

	CurrApp = SideShow_GetApplicationFromGUID(&ApplicationID);

	if (CurrApp == NULL)
	  CurrApp = SideShow_GetFreeApplication();

	if (CurrApp == NULL)
	{
		PacketHeader->Length -= sizeof(SideShow_PacketHeader_t) + sizeof(GUID_t);

		Endpoint_Discard_Stream(PacketHeader->Length);
		Endpoint_ClearOUT();

		PacketHeader->Type.NAK = true;
	}
	else
	{
		CurrApp->ApplicationID = ApplicationID;
		Endpoint_Read_Stream_LE(&CurrApp->EndpointID, sizeof(GUID_t));
		SideShow_Read_Unicode_String(&CurrApp->ApplicationName, sizeof(CurrApp->ApplicationName.UnicodeString));
		Endpoint_Read_Stream_LE(&CurrApp->CachePolicy, sizeof(uint32_t));
		Endpoint_Read_Stream_LE(&CurrApp->OnlineOnly, sizeof(uint32_t));
		SideShow_Discard_Byte_Stream();
		SideShow_Discard_Byte_Stream();
		SideShow_Discard_Byte_Stream();
		Endpoint_ClearOUT();
		
		CurrApp->InUse = true;
		CurrApp->HaveContent = false;
		CurrApp->CurrentContentID = 1;
	}

	PacketHeader->Length = sizeof(SideShow_PacketHeader_t);

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	Endpoint_ClearIN();
}

static void SideShow_DeleteApplication(SideShow_PacketHeader_t* const PacketHeader)
{
	GUID_t ApplicationGUID;
	
	Endpoint_Read_Stream_LE(&ApplicationGUID, sizeof(GUID_t));	
	Endpoint_ClearOUT();

	SideShow_Application_t* AppToDelete = SideShow_GetApplicationFromGUID(&ApplicationGUID);

	if (AppToDelete != NULL)
	  AppToDelete->InUse = false;
	else
	  PacketHeader->Type.NAK = true;

	PacketHeader->Length = sizeof(SideShow_PacketHeader_t);

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	Endpoint_ClearIN();
}

static void SideShow_DeleteAllApplications(SideShow_PacketHeader_t* const PacketHeader)
{
	Endpoint_ClearOUT();
	
	for (uint8_t App = 0; App < MAX_APPLICATIONS; App++)
	  InstalledApplications[App].InUse = false;

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	Endpoint_ClearIN();
}

static void SideShow_AddContent(SideShow_PacketHeader_t* const PacketHeader)
{
	GUID_t ApplicationID;
	GUID_t EndpointID;
	SideShow_Application_t* Application;
	
	Endpoint_Read_Stream_LE(&ApplicationID, sizeof(GUID_t));
	Endpoint_Read_Stream_LE(&EndpointID, sizeof(GUID_t));
	
	Application = SideShow_GetApplicationFromGUID(&ApplicationID);
	
	if (Application == NULL)
	{
		SideShow_Discard_Byte_Stream();
		PacketHeader->Type.NAK = true;
	}
	else if (!(SideShow_AddSimpleContent(PacketHeader, Application)))
	{
		PacketHeader->Type.NAK = true;
	}
	
	Endpoint_ClearOUT();

	PacketHeader->Length = sizeof(SideShow_PacketHeader_t);

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	Endpoint_ClearIN();
}

static void SideShow_DeleteContent(SideShow_PacketHeader_t* const PacketHeader)
{
	GUID_t   ApplicationID;
	GUID_t   EndpointID;
	uint32_t ContentID;

	Endpoint_Read_Stream_LE(&ApplicationID, sizeof(GUID_t));
	Endpoint_Read_Stream_LE(&EndpointID, sizeof(GUID_t));
	Endpoint_Read_Stream_LE(&ContentID, sizeof(uint32_t));
	Endpoint_ClearOUT();
	
	SideShow_Application_t* Application = SideShow_GetApplicationFromGUID(&ApplicationID);
	
	if ((Application != NULL) && (Application->CurrentContentID == ContentID))
	  Application->HaveContent = false;
	else
	  PacketHeader->Type.NAK = true;
	  
	PacketHeader->Length = sizeof(SideShow_PacketHeader_t);

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	Endpoint_ClearIN();
}

static void SideShow_DeleteAllContent(SideShow_PacketHeader_t* const PacketHeader)
{
	GUID_t ApplicationID;
	GUID_t EndpointID;

	Endpoint_Read_Stream_LE(&ApplicationID, sizeof(GUID_t));
	Endpoint_Read_Stream_LE(&EndpointID, sizeof(GUID_t));
	Endpoint_ClearOUT();

	SideShow_Application_t* Application = SideShow_GetApplicationFromGUID(&ApplicationID);
	
	if (Application != NULL)
	  Application->HaveContent = false;
	else
	  PacketHeader->Type.NAK = true;	  

	PacketHeader->Length = sizeof(SideShow_PacketHeader_t);

	Endpoint_SelectEndpoint(SIDESHOW_IN_EPNUM);
	Endpoint_Write_Stream_LE(PacketHeader, sizeof(SideShow_PacketHeader_t));
	Endpoint_ClearIN();  
}
