
#include <stdlib.h>
#include <string.h>

#include "Desk.Event.h"
#include "Desk.File.h"
#include "Desk.Debug.h"
#include "Desk.WimpSWIs.h"
#include "Desk.Error.h"

#include "Desk.Import2.h"


#define	Desk_Import2_DebugPrefix	Desk_error_PLACE "Desk_Import: "


typedef struct	{
	Desk_bool				in_use;
	Desk_Import2_fileimporter	fileimporter;	/* Function which loads the file.	*/
	Desk_Import2_ramallocator	ramallocator;	/* Function which ram-transfers (NIY)	*/
	Desk_Import2_ramhandler		ramhandler;
	
	Desk_Import2_clientinfo		clientinfo;
	
	unsigned int			ourref;			/* wimp message 'ourref' for the last message that we have sent.		*/
	size_t				last_rambuffersize;	/* So we can recognise last transfer - when num	bytes transfered<last_rambuffersize	*/
	}
	import_block;


/*static void	Desk_Import2_ClaimOrReleaseMessageHandler( event_claimorreleasefn fn, import_block* import);*/


static void	Desk_Import2__CleanUp( import_block* import);



static void	Desk_Import2_SendDataSaveAck( Desk_message_block* datasave, import_block* import)
/*
Replies to message 'datasave', initiating file-transfer
 */
	{
	Desk_message_block	reply = *datasave;
	
	Desk_Debug_Printf( Desk_Import2_DebugPrefix "Sending <Wimp$Scrap> DataSaveAck reply to DATASAVE message from %i\n", datasave->header.sender);
	
	Desk_Debug_Assert( datasave->header.action == Desk_message_DATASAVE);
	/* 
	We need to tell the sender of this message_DATASAVE to do the transfer
	via  <Wimp$Scrap>. We also tell them that we aren't the filer, so their
	file  won't be 'safe' after we've loaded it.
	*/
	reply.header.action		= Desk_message_DATASAVEACK;
	reply.header.yourref		= datasave->header.myref;
	reply.data.datasaveack.estsize	= -1;	/* = Unsafe	*/
	
	strcpy( reply.data.datasaveack.filename, "<Wimp$Scrap>");	/* Should really check that <Wimp$Scrap>.^ exists etc.	*/
	
	reply.header.size = sizeof( Desk_message_block);
		/* 
		We've changed the message size by strcpy-ing into ...filename.
		Should really calculate the correct length here, not just use the
		maximum possible.
		*/
	
	Desk_Wimp_SendMessage( Desk_event_SENDWANTACK, (Desk_message_block*) &reply, reply.header.sender, 0);
	import->ourref	= reply.header.myref;
	}




static void	Desk_Import2_SendRAMFetch( Desk_message_block* datasave_or_ramtransmit, import_block* import)
/*
Sends a message_RAMFETCH as a reply to the message with header 'header'.
This is in a separate function because it can be called in two situations:
1) As a response to the initial message_DATASAVE that we receive.
2) As a response to each of the message_RAMTRANSMIT's during the RAM transfer.
 */
	{
	Desk_Debug_Assert( datasave_or_ramtransmit->header.action == Desk_message_DATASAVE || datasave_or_ramtransmit->header.action == Desk_message_RAMTRANSMIT);
	Desk_Debug_Assert( import->ramallocator);
	
	Desk_Debug_Printf( Desk_Import2_DebugPrefix "Preparing to send RAMFetch. Bytes transfered so far = %i, estdatasize=%i\n", 
			import->clientinfo.ram_progress, 
			import->clientinfo.initialdatasave.data.datasave.estsize
			);
	
		{
		size_t	buffsize	= 0;
		void*	buffer		= import->ramallocator( &buffsize, &import->clientinfo);
		
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "Client has allocated buffer size %i\n", buffsize);
		
		if (!buffer)	{
			/* Terminate the transfer. Rely on ramallocator to display an error etc.	*/
			Desk_Import2__CleanUp( import);
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "Client ram-allocator failed to allocate memory - aborting import\n");
			return;
			}
		
		import->last_rambuffersize = buffsize;
		
			{
			Desk_message_block	msg;
			msg.data.ramfetch.buffer	= buffer;
			msg.data.ramfetch.buffsize	= buffsize;
			
			msg.header		= datasave_or_ramtransmit->header;
			msg.header.action	= Desk_message_RAMFETCH;
			msg.header.yourref	= datasave_or_ramtransmit->header.myref;
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "Sending RAMFETCH\n");
			Desk_Wimp_SendMessage( Desk_event_SENDWANTACK, (Desk_message_block*) &msg, msg.header.sender, 0);
			import->ourref = msg.header.myref;
			}
		}
	}





Desk_SDLS_PtrFn(
	static,
	Desk_bool,
	Desk_Import2_MessageHandler( Desk_event_pollblock* event, void* reference)
	)
	{
	import_block		*import	= (import_block *)		reference;
	
	
	if ( event->type == Desk_event_ACK && event->data.message.header.myref == import->ourref)	{
		
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "Received bounced message...\n");
		
		if ( event->data.message.header.action == Desk_message_RAMFETCH)	{
		
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "Message is our message_RAMFETCH being bounced\n");
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "Telling ramhandler to free initial ram buffer\n");
			import->ramhandler( event->data.message.data.ramfetch.buffer, -1, &import->clientinfo);
			
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "Sending a DATASAVEACK - trying scrap-file instead of RAM transfer\n");
			Desk_Import2_SendDataSaveAck( &import->clientinfo.initialdatasave, import);
			}
		
		else	{
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "Bounced message is unknown type %i - aborting import.\n", 
				event->data.message.header.action
				);
			Desk_Import2__CleanUp( import);
			}
		
		return Desk_bool_TRUE;
		}
	
	if ( event->data.message.header.yourref != import->ourref)	return Desk_bool_FALSE;
	
	Desk_Debug_Printf( Desk_Import2_DebugPrefix "Dealing with message reply...\n");
	
	
	
	
	if ( event->data.message.header.action == Desk_message_RAMTRANSMIT)	{
	
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "Handling message_RAMTRANSMIT - %i bytes written into out buffer\n", 
			event->data.message.data.ramtransmit.byteswritten
			);
		
		import->clientinfo.ram_progress += event->data.message.data.ramtransmit.byteswritten;
		
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "Calling client ramhandler, to deal with ram-transfered data\n");
		import->ramhandler( event->data.message.data.ramtransmit.buffer, event->data.message.data.ramtransmit.byteswritten, &import->clientinfo);
			
		if ( event->data.message.data.ramtransmit.byteswritten < import->last_rambuffersize)	{
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "byteswritten < last_rambuffersize - succesfull end of RAM import\n");
			import->ramhandler( NULL, 0, &import->clientinfo);	/* Tell client the good news...	*/
			Desk_Import2__CleanUp( import);
			}
		else	{
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "Sending RAMFetch - asking for more data...\n");
			Desk_Import2_SendRAMFetch( &event->data.message, import);
			}
		return Desk_bool_TRUE;
		}
	
	if ( event->data.message.header.action == Desk_message_DATALOAD)	{
		int	loaderror;
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "Received DATALOAD for filename '%s' - calling client fileimporter\n", 
			event->data.message.data.dataload.filename
			);
		loaderror = import->fileimporter( &event->data.message.data.dataload, &import->clientinfo);
		
		/* Now finish the file-transfer protocol...	*/
		/*
		Because message_DATALOAD is used in two situations, we have to be careful not to delete the file after loading
		when it is straight from the filer... Actually, this code doesn't yet support loads from the Filer, so
		we're probably ok anyway...
		 */
		
		Desk_Debug_Assert( import->ourref);
		
		if ( import->ourref)	{
			/* 
			This DATALOAD is a response to our previous 
			message_DATASAVEACK, so the file will be <Wimp$Scrap>. Hence
			we should delete <Wimp$Scrap> 'cos we've just loaded it.
			*/
			
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "Preparing to delete temp file\n");
			
			import->ourref = 0;
				/* Clear things ready for the next message_DATALOAD	*/
			
			if ( strcmp( event->data.message.data.dataload.filename, "<Wimp$Scrap>"))	{
				
				Desk_Debug_Printf( Desk_Import2_DebugPrefix "Error: filename isn't <Wimp$Scrap>. Aborting import\n");
				/*
				Error_Report( 
					0, 
					"Scrapfile transfer, but filename is '%s' instead of '<Wimp$Scrap>' - not deleting file", 
					event->data.message.data.dataload.filename
					);
				*/
				}
			
			else	{
				Desk_Debug_Printf( Desk_Import2_DebugPrefix "Deleting <Wimp$Scrap>\n");
				Desk_File_Delete( "<Wimp$Scrap>");
				}
			}
		
		/*
		Tell whoever is giving us the file that we have loaded it. If we couldn't load the file,
		don't reply - the sender will hopefully treat this as an error...
		 */
		if ( !loaderror)	{
			Desk_message_block	reply	= event->data.message;
			
			reply.header.action	= Desk_message_DATALOADACK;
			reply.header.yourref	= event->data.message.header.myref;
			Desk_Debug_Printf( Desk_Import2_DebugPrefix "Sending DATALOADACK to finish transfer\n");
			Desk_Wimp_SendMessage( Desk_event_SEND, (Desk_message_block*) &reply, event->data.message.header.sender, 0);
			}
		
		Desk_Import2__CleanUp( import);
		return Desk_bool_TRUE;
		}
	
		{
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "Unrecognised message reply message number %i\n", event->data.message.header.action);
		Desk_Import2__CleanUp( import);
		return Desk_bool_TRUE;
		}
	}










void	Desk_Import2_ImportData( 
		Desk_event_pollblock*		event,	/* The message_DATASAVE	*/
		Desk_Import2_fileimporter	fileimporter, 
		Desk_Import2_ramallocator	ramallocator,
		Desk_Import2_ramhandler		ramhandler,
		void*				reference
		)
{
	static import_block	globalimport = { Desk_bool_FALSE	};
	
	import_block*	import = &globalimport;
	
	Desk_Debug_Printf( Desk_Import2_DebugPrefix "Desk_Import2_ImportData called\n");
	
	if ( !fileimporter)	{
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "Desk_Import2_ImportData called with NULL file importer function - aborting transfer\n");
		return;
		}
	
	if ( event->data.message.header.action != Desk_message_DATASAVE)	{
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "message != message_DATASAVE - aborting transfer.\n");
		return;
		}
	
	if ( import->in_use)	{
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "Desk_Import2_ImportData called while in use. Abandoning previous usage\n");
		Desk_Import2__CleanUp( import);
		}
	
	import->in_use = Desk_bool_TRUE;
	
	
	if ( (ramallocator && !ramhandler) || (!ramallocator && ramhandler))	{
		Desk_Debug_Printf( Desk_Import2_DebugPrefix "Warning: Desk_Import2_ImportData called with inconsistant ram-functions\n");
		ramallocator	= NULL;
		ramhandler	= NULL;
		}
	
	Desk_Debug_Printf( Desk_Import2_DebugPrefix "Desk_Import2_ImportData, filetype is %i, estsize=%i\n", 
			event->data.message.data.datasave.filetype, 
			event->data.message.data.datasave.estsize
			);
	
	import->fileimporter	= fileimporter;
	import->ramallocator	= ramallocator;
	import->ramhandler	= ramhandler;
	
	import->clientinfo.ram_progress		= 0;
	import->clientinfo.reference		= reference;
	
	import->ourref		= 0;
	import->clientinfo.initialdatasave	= event->data.message;
	
	import->last_rambuffersize	= 0;
	
	/* Try a message_RAMFETCH if client is capable...	*/
	if ( ramallocator)	Desk_Import2_SendRAMFetch( &import->clientinfo.initialdatasave, import);
	else			Desk_Import2_SendDataSaveAck( &import->clientinfo.initialdatasave, import);
	
	/*Desk_Import2_ClaimOrReleaseMessageHandler( Event_Claim, import);*/
	Desk_Event_MessagesClaim( Desk_Import2_MessageHandler, import);
	}



/*
static void	Desk_Import2_ClaimOrReleaseMessageHandler( event_claimorreleasefn fn, import_block* import)
	{
	fn( event_USERMESSAGE,		event_ANY, event_ANY,	_DeskLib_SDLS_dllEntry( Desk_Import2_MessageHandler), import);
	fn( event_USERMESSAGERECORDED,	event_ANY, event_ANY,	_DeskLib_SDLS_dllEntry( Desk_Import2_MessageHandler), import);
	fn( event_ACK,			event_ANY, event_ANY,	_DeskLib_SDLS_dllEntry( Desk_Import2_MessageHandler), import);
	}
*/


static void	Desk_Import2__CleanUp( import_block* import)
	{
	/*Desk_Import2_ClaimOrReleaseMessageHandler( Event_Release, import);*/
	Desk_Event_MessagesRelease( Desk_Import2_MessageHandler, import);
	import->in_use = Desk_bool_FALSE;
	}

