#include <stdio.h>
#include <string.h>

#include "Desk.Event.h"
#include "Desk.WimpSWIs.h"
#include "Desk.DeskMem.h"
#include "Desk.LinkList.h"
#include "Desk.Module.h"
#include "Desk.Export.h"
#include "Desk.TaskWindow.h"
#include "Desk.File.h"

#include "MemCheck.MemCheck.h"



typedef struct	{
	Desk_linklist_header		header;
	Desk_taskwindow_displayfn	displayfn;
	Desk_task_handle		taskhandle;
	void*				reference;
	int				txt;
	Desk_deskmem_buffer		ramtransfer_buffer;
	Desk_taskwindow_egofn		egofn;
	}
	child_info;



Desk_task_handle	Desk_TaskWindow_GetTaskHandle( Desk_taskwindow_id childid)
	{
	child_info*	child = (child_info*) childid;
	return child->taskhandle;
	}


static Desk_bool	MorioHandler( Desk_event_pollblock* event, void* reference)
	{
	child_info*	child = (child_info*) reference;
	
	if ( event->data.message.header.sender!=child->taskhandle)	return Desk_bool_FALSE;
	
	if ( event->data.message.header.action == Desk_message_TASKWINDOW_MORIO)	{
		Desk_Event_Release( Desk_event_USERMESSAGE, Desk_event_ANY, Desk_event_ANY, MorioHandler, child);
		child->displayfn( NULL, 0, (Desk_taskwindow_id) child, child->reference);
		return Desk_bool_TRUE;
		}
	if ( event->data.message.header.action == Desk_message_TASKWINDOW_OUTPUT)	{
		child->displayfn( 
			(void*) &event->data.message.data.words[1], 
			event->data.message.data.words[0], 
			(Desk_taskwindow_id) child, 
			child->reference
			);
		return Desk_bool_TRUE;
		}
	return Desk_bool_FALSE;
	}


static Desk_bool	EgoHandler( Desk_event_pollblock* event, void* reference)
	{
	child_info*	child = (child_info*) reference;
	if	(
		event->data.message.header.action != Desk_message_TASKWINDOW_EGO
		|| event->data.message.data.words[0]!=child->txt
		)
		return Desk_bool_FALSE;
	
	child->taskhandle = event->data.message.header.sender;
	Desk_Debug_Printf( Desk_error_PLACE "EgoHandler - child taskhadle=0x%p\n", (void*) child->taskhandle);
	
	MemCheck_RegisterMiscBlock( &child->taskhandle, 4);
	MemCheck_SetBlockWritable( &child->taskhandle, 0);
	
	Desk_Event_Release( Desk_event_USERMESSAGE, Desk_event_ANY, Desk_event_ANY, EgoHandler, child);
	Desk_Event_Claim( Desk_event_USERMESSAGE, Desk_event_ANY, Desk_event_ANY, MorioHandler, child);
	
	if ( child->egofn)	child->egofn( child->taskhandle, (Desk_taskwindow_id) child, child->reference);
	
	return Desk_bool_TRUE;
	}


static Desk_linklist_header	children = { NULL, NULL};



Desk_taskwindow_id
	Desk_TaskWindow_CreateChild( Desk_taskwindow_displayfn displayfn, Desk_taskwindow_egofn egofn, const char* cmd, void* reference)
	{
	char		cmd2[ 256];
	static int	txt = 0;
	child_info*	child = Desk_DeskMem_MallocType( child_info);
	
	Desk_LinkList_AddToTail( &children, &child->header);
	
	child->displayfn		= displayfn;
	child->reference		= reference;
	child->txt			= txt;
	child->taskhandle		= 0;
	child->ramtransfer_buffer.data	= NULL;
	child->egofn		 	= egofn;
	
	sprintf( cmd2, "%s -task &%x -txt &%x", cmd, Desk_Event_taskhandle, child->txt);
	Desk_Wimp_StartTask( cmd2);
	Desk_Event_Claim( Desk_event_USERMESSAGE, Desk_event_ANY, Desk_event_ANY, EgoHandler, child);
	txt++;
	return (Desk_taskwindow_id) child;
	}



static int	RAMTransferer( Desk_message_block* ramfetch, int progress, void* reference)
	{
	child_info*	child = (child_info*) reference;
	int		num = Desk_MIN( Desk_DeskMem_BufferGetDataSize( &child->ramtransfer_buffer)-progress, ramfetch->data.ramfetch.buffsize);
	Desk_Debug_Assert( num>=0);
	
	Desk_Debug_Printf( Desk_error_PLACE "RAMTransferer called - progress=%i, num=%i\n", progress, num);
	
	Desk_Wimp_TransferBlock( 
		Desk_Event_taskhandle, 
		Desk_DeskMem_BufferGetString( &child->ramtransfer_buffer), 
		child->taskhandle, 
		ramfetch->data.ramfetch.buffer,
		num
		);
	
	if ( num < ramfetch->data.ramfetch.buffsize)	{
		Desk_DeskMem_BufferFree( &child->ramtransfer_buffer);
		child->ramtransfer_buffer.data = NULL;
		}
	
	return num;
	}


static int	FileSaver( const char* filename, void* reference)
	{
	child_info*	child = (child_info*) reference;
	Desk_File_SaveMemory( 
		filename, 
		Desk_DeskMem_BufferGetString( &child->ramtransfer_buffer), 
		Desk_DeskMem_BufferGetDataSize( &child->ramtransfer_buffer)
		);
	Desk_DeskMem_BufferFree( &child->ramtransfer_buffer);
	child->ramtransfer_buffer.data = NULL;
	return 0;
	}


static void	ResultHander( Desk_export_result result, void* reference)
	{
	child_info*	child = (child_info*) reference;
	if ( result != Desk_export_result_OK)	Desk_Debug_Printf( Desk_error_PLACE "Data transfer failed - restult %i\n", result);
	Desk_UNUSED( child);
	}

void	Desk_TaskWindow_SendData( Desk_taskwindow_id childid, void* data, size_t datasize)
	{
	child_info*		child = (child_info*) childid;
	
	if ( child->taskhandle==0)	Desk_Error2_HandleTextf( "Desk_TaskWindow_SendData called before child (id 0x%p) has started up", child);
	
	Desk_Debug_Printf( Desk_error_PLACE "Desk_TaskWindow_SendData called, datasize=%i\n", datasize);
	
	if ( Desk_DeskMem_BufferGetString( &child->ramtransfer_buffer))	{
		// We are in the middle of a RAM transfer, so simply cat new data onto end of existing data...
		// Should eventually omtimse this - require the caller to keep data persistent, 
		// and create a linked-list of pending data chunks.
		// This will require the ramtramsfere to use multiple Wimp_TransferBlock to transfer
		// the disjoint data.
		size_t 	oldsize = Desk_DeskMem_BufferGetDataSize( &child->ramtransfer_buffer);
		Desk_Debug_Printf( Desk_error_PLACE "Desk_TaskWindow_SendData adding to existing buffer\n");
		Desk_DeskMem_BufferEnsureExtra( &child->ramtransfer_buffer, datasize);
		memcpy( Desk_DeskMem_BufferGetString( &child->ramtransfer_buffer)+oldsize, data, datasize);
		}
	
	else if ( datasize>236)	{
		Desk_mouse_block	mouse = { {0,0}, 0, 0, 0};
		mouse.window = (Desk_window_handle) child->taskhandle;
		
		Desk_Debug_Printf( Desk_error_PLACE "Desk_TaskWindow_SendData starting RAM transfer\n");
		
		Desk_DeskMem_BufferInit( &child->ramtransfer_buffer, 256);
		Desk_DeskMem_BufferEnsure( &child->ramtransfer_buffer, datasize);
		memcpy( Desk_DeskMem_BufferGetString( &child->ramtransfer_buffer), data, datasize);
		
		Desk_Export_ExportData(
			NULL,
			&mouse,
			"",		// Leafname
			FileSaver,
			RAMTransferer,
			ResultHander,		// Result handler
			datasize,
			0xfff,
			child
			);
		}
	
	else	Desk_TaskWindow_SendDataSmall( childid, data, datasize);
	}



void	Desk_TaskWindow_SendDataSmall( 
		Desk_taskwindow_id	childid, 
		void*			data, 
		size_t			datasize
		)
	{
	child_info*		child = (child_info*) childid;
	Desk_message_block	message;
	
	Desk_Debug_Assert( datasize<=236);
	
	Desk_Debug_Printf( Desk_error_PLACE "Desk_TaskWindow_SendDataSmall called\n");
	
	// Simple case - PRM 3-265 is /wrong/ - data in message TaskWindow_Input is contained in the message block
	// like message TaskWindow_Output.
	
	message.header.size	= 256;
	message.header.yourref	= 0;
	message.header.action	= Desk_message_TASKWINDOW_INPUT;
	
	message.data.words[0] = datasize;
	memcpy( &message.data.words[1], data, datasize);
	Desk_Wimp_SendMessage( Desk_event_USERMESSAGE, &message, child->taskhandle, 0);
	
	Desk_Debug_Printf( Desk_error_PLACE "Desk_TaskWindow_SendDataSmall - child taskhadle=0x%p\n", (void*) child->taskhandle);
	}



void	Desk_TaskWindow_KillChild( Desk_taskwindow_id childid);

void	Desk_TaskWindow_SuspendChild( Desk_taskwindow_id childid);

void	Desk_TaskWindow_ResumeChild( Desk_taskwindow_id childid);
