/*
	####	#	#	# #
	#	#	#	#	#	The FreeWare C library for
	#	#	##	###	#	# #	# ###	RISC OS machines
	#	# #	# #	# #	#	# #	#	___________________________________
	#	# ####	###	##	#	# #	#
	#	# #	# # #	#	# #	#	Please refer to the accompanying
	####	### ####	#	# ##### # ###	documentation for conditions of use
	________________________________________________________________________

	File:	Save.Save.c
	Author:	Copyright  1994 Julian Smith and Jason Howat
	Version: 1.03 (14 Nov 1995)
	Purpose: Automated handling of save-as window
	History: 1.01 (13 Jun 1994) - JS - Added support for filetypes
	1.02 (18 Jun 1994) - JH - Changed sense of value returned by
	Desk_Export_filesaver to be in line with rest of DeskLib.
	Desk_Export_filsaver uses filename instead of stream.	Tidied code
	to use core routines and proper DeskLib conventions.	Added
	Desk_Export__KeyHandler to check for RETURN key in text icon.	For
	a RETURN key or OK click, added check that filename isn't
	just a leafname.	Changed Desk_Export__DragIconClickHandler to use
	Icon_StartSolidDrag.	Changed several routines to make
	messages in their own blocks -- they used to corrupt
	event_lastevent.	In Desk_Export__MessageHandler the initial
	reference check excludes unacknowledged messages that were
	returned.
	1.03 (14 Nov 1995) JS Made SDLS compatible.
*/

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

#include "Desk.Event.h"

#include "Desk.WimpSWIs.h"
#include "Desk.Icon.h"
#include "Desk.Keycodes.h"
#include "Desk.Error.h"
#include "Desk.Save.h"
#include "Desk.Str.h"
#include "Desk.File.h"
#include "Desk.Debug.h"
#include "Desk.Wimp.h"

#include "Desk.Export.h"



#define	Desk_Export_DebugPrefix	Desk_error_PLACE "Desk_Export: "




typedef struct	{
	Desk_export_filesaver		filesaver;
	Desk_export_ramsaver		ramsaver;
	Desk_export_resulthandler	resulthandler;
	size_t				estimatedsize;
	int				filetype;
	void				*reference;
	int				ram_progress;		/* Num of bytes ram-transfered. */
	unsigned int			last_message_ref;	/* So we know which incoming messages are replies to us	*/
	Desk_message_action		initial_messageaction;	/* DATASAVE or EDITDATASAVE	*/
	}
	Desk_Export_exportblock;




/*static void	Desk_Export__EventClaimOrRelease( Desk_Export_exportblock *exportblock, event_claimorreleasefn fn);
*/


static void	Desk_Export__ResetSaveBlock( Desk_Export_exportblock *exportblock);
static void	Desk_Export__CleanUp( Desk_Export_exportblock *exportblock);



static int	Desk_Export__SaveDataToFilename( char *filename, Desk_Export_exportblock *exportblock)
	{
	int	err = exportblock->filesaver( filename, exportblock->reference);
	
	if ( !err)	Desk_File_SetType( filename, exportblock->filetype);
	
	return err;
	}






Desk_SDLS_PtrFn(
	static,
	Desk_bool,
	Desk_Export__MessageHandler( Desk_event_pollblock *event, void *reference)
	)
/*static BOOL Desk_Export__MessageHandler(event_pollblock *event, void *ref)*/
/* cc warns about extern ... not declared in header in SDLS compiles	*/
	{
	Desk_Export_exportblock	*exportblock = (Desk_Export_exportblock*) reference;
	Desk_message_block	*msg = &event->data.message;
	
	if ( (event->type == Desk_event_ACK) && (msg->header.myref == exportblock->last_message_ref))	{
		exportblock->resulthandler( Desk_export_result_RECEIVERFAILED, exportblock->reference);
		Desk_Debug_Printf( Desk_Export_DebugPrefix "message type %i bounced, aborting export\n", msg->header.action);
		if ( msg->header.action == Desk_message_DATALOAD && 0==strcmp( msg->data.dataload.filename, "<Wimp$Scrap>"))	{
			Desk_File_Delete( "<Wimp$Scrap>");
			Desk_Error_Report( 0, "Data transfer failed: Receiver died");
			}
		Desk_Export__CleanUp( exportblock);
		return Desk_bool_TRUE;
		}
	
	if ( msg->header.yourref != exportblock->last_message_ref)	return Desk_bool_FALSE;	/* Not a reply to our previous message	*/
	
	if ( msg->header.action == Desk_message_DATASAVEACK)		{
		
		int	err;
		
		Desk_Debug_Printf( Desk_Export_DebugPrefix "Received Desk_message_DATASAVEACK - calling file-saver...\n");
		err = Desk_Export__SaveDataToFilename( msg->data.datasaveack.filename, exportblock);
		
		if ( !err)
			{
			Desk_message_block reply;
			
			memcpy( &reply, msg, sizeof( Desk_message_block));
			
			reply.header.action		= Desk_message_DATALOAD;
			reply.data.dataload.size	= 49;
			reply.header.yourref		= reply.header.myref;
			
			Desk_Wimp_SendMessage( Desk_event_SENDWANTACK, (Desk_message_block*) &reply, reply.header.sender, 0);
			exportblock->last_message_ref = reply.header.myref;
			}
		
		else	{
			Desk_Debug_Printf( Desk_Export_DebugPrefix "File-saver failed, aborting export...\n");
			exportblock->resulthandler( Desk_export_result_FILESAVERFAILED, exportblock);
			Desk_Export__CleanUp( exportblock);
			}
		
		return Desk_bool_TRUE;
		}
	
	if ( msg->header.action == Desk_message_RAMFETCH)	{
		int			byteswritten;
		Desk_bool		last_ramtransfer;
		Desk_message_block	reply;
		
		Desk_Debug_Printf( Desk_Export_DebugPrefix "Received Desk_message_RAMFETCH. Receiver's buffer size is %i\n", msg->data.ramfetch.buffsize);
		
		if ( exportblock->ramsaver == NULL)	{
			Desk_Debug_Printf( Desk_Export_DebugPrefix "Ignoring Desk_message_RAMFETCH - no client ram exporter\n");
			return Desk_bool_TRUE;
			}
		/* If we ignore message_RAMFETCH, the receiving task should
		* try again using a transfer through <Wimp$Scrap>.
		*/
		
		Desk_Debug_Printf( Desk_Export_DebugPrefix "Calling client ramsaver...\n");
		byteswritten = exportblock->ramsaver( msg, exportblock->ram_progress, exportblock->reference);
		
		last_ramtransfer = (byteswritten < msg->data.ramfetch.buffsize) ? Desk_bool_TRUE : Desk_bool_FALSE;
		exportblock->ram_progress += byteswritten;
		
		if ( byteswritten < 0)	{
			Desk_Debug_Printf( Desk_Export_DebugPrefix "Client ramsaver returned %i - error occurred, aborting export\n", byteswritten);
			(exportblock->resulthandler)( Desk_export_result_RAMSAVERFAILED, exportblock);
			Desk_Export__CleanUp( exportblock);
			return Desk_bool_TRUE;
			}
		
		memcpy( &reply, msg, sizeof( Desk_message_block));
		reply.header.yourref			= reply.header.myref;
		reply.header.action			= Desk_message_RAMTRANSMIT;
		reply.data.ramtransmit.byteswritten	= byteswritten;
		
		Desk_Wimp_SendMessage( (last_ramtransfer) ? Desk_event_SEND : Desk_event_SENDWANTACK, (Desk_message_block*) &reply, reply.header.sender, 0);
		
		exportblock->last_message_ref = reply.header.myref;
		
		Desk_Debug_Printf( Desk_Export_DebugPrefix "Total of %i bytes exported, using RAM transfer\n", exportblock->ram_progress);
		
		if ( last_ramtransfer)	{
			Desk_Debug_Printf( Desk_Export_DebugPrefix "Ram export finished\n");
			Desk_Export__CleanUp( exportblock);
			(exportblock->resulthandler)( Desk_export_result_OK, exportblock->reference);
			}
		
		return Desk_bool_TRUE;
		}
	
	if ( msg->header.action == Desk_message_DATALOADACK)
		{
		/* everything OK	*/
		Desk_Debug_Printf( Desk_Export_DebugPrefix "Received Desk_message_DATALOADACK - end of scrap-file transfer.\n");
		Desk_Export__CleanUp( exportblock);
		
		(exportblock->resulthandler)( Desk_export_result_OK, exportblock->reference);
		return Desk_bool_TRUE;
		}
	
	
		{
		Desk_Debug_Printf( Desk_Export_DebugPrefix "Unrecognised reply, message %i, aborting export\n", msg->header.action);
		Desk_Export__CleanUp( exportblock);
		return Desk_bool_TRUE;
		}
	
	return Desk_bool_FALSE;
	}


static void	Desk_Export__DefaultResultHandler( Desk_export_result result, void *reference)
{
	Desk_UNUSED_ARG( reference);
	
	switch( result)	{
		case Desk_export_result_RECEIVERFAILED:
			Desk_Debug_Printf( Desk_Export_DebugPrefix "Data export failed: Receiver failed.");
			break;
		
		case Desk_export_result_FILESAVERFAILED:
			Desk_Debug_Printf( Desk_Export_DebugPrefix "Data export failed: Filer-saver failed");
			break;
		
		case Desk_export_result_RAMSAVERFAILED:
			Desk_Debug_Printf( Desk_Export_DebugPrefix "Data export failed: RAM-saver failed");
			break;
		}
}



/*
static void	Desk_Export__EventClaimOrRelease( Desk_Export_exportblock *exportblock, event_claimorreleasefn fn)
{
	fn( event_SEND,		event_ANY, event_ANY, _DeskLib_SDLS_dllEntry( Desk_Export__MessageHandler), exportblock);
	fn( event_SENDWANTACK,	event_ANY, event_ANY, _DeskLib_SDLS_dllEntry( Desk_Export__MessageHandler), exportblock);
	fn( event_ACK,		event_ANY, event_ANY, _DeskLib_SDLS_dllEntry( Desk_Export__MessageHandler), exportblock);
}
*/



void	Desk_Export_ExportData(
			Desk_event_pollblock*		event,		/* NULL or Desk_message_DATAREQUEST if relevent...		*/
			Desk_mouse_block*		mouse,		/* NULL if not saving from a drag.				*/
			const char*			leafname,
			Desk_export_filesaver		filesaver,
			Desk_export_ramsaver		ramsaver,
			Desk_export_resulthandler	resulthandler,
			size_t				estimatedsize,
			int				filetype,
			void*				reference
			)
	{
	Desk_Export_ExportData2( 
		event, mouse, leafname, filesaver, ramsaver, resulthandler, 
		estimatedsize, filetype, reference, Desk_message_DATASAVE,
		(event) ? event->data.message.header.sender : (Desk_task_handle) mouse->window
		);
	}



void	Desk_Export_ExportData2(
			Desk_event_pollblock*		event,		/* NULL or Desk_message_DATAREQUEST if relevent...		*/
			Desk_mouse_block*		mouse,		/* NULL if not saving from a drag.				*/
			const char*			leafname,
			Desk_export_filesaver		filesaver,
			Desk_export_ramsaver		ramsaver,
			Desk_export_resulthandler	resulthandler,
			size_t				estimatedsize,
			int				filetype,
			void*				reference,
			Desk_message_action		initial_messageaction,
			Desk_task_handle		destination
			)
	{
	static Desk_Export_exportblock	global_exportblock;
	Desk_Export_exportblock*	exportblock = &global_exportblock;
	
	Desk_Debug_Printf( Desk_Export_DebugPrefix "Desk_Export_ExportData exporting to task/window %i\n", 
		(event) ? event->data.message.header.sender : mouse->window
		);
	
	if ( !filesaver)	{
		/* Must have a file-saver	*/
		Desk_Debug_Printf( "Desk_Export_ExportData called with NULL filesaver - aborting export\n");
		return;
		}
	
	exportblock->filesaver			= filesaver;
	exportblock->ramsaver			= ramsaver;
	exportblock->resulthandler		= resulthandler;
	exportblock->estimatedsize		= estimatedsize;
	exportblock->reference			= reference;
	exportblock->filetype			= filetype;
	exportblock->initial_messageaction	= initial_messageaction;
	
	Desk_Export__ResetSaveBlock( exportblock);
	
	if ( exportblock->resulthandler == NULL)	exportblock->resulthandler = Desk_Export__DefaultResultHandler;
	
	Desk_Debug_Assert( (event && !mouse) || (!event && mouse));
	
	/*Desk_Export__EventClaimOrRelease( exportblock, Event_Claim);*/
	Desk_Event_MessagesClaim( Desk_Export__MessageHandler, exportblock);
	
		{
 		Desk_message_block 	msg;
 		
		msg.header.size		= 80;
		msg.header.yourref	= (event) ? event->data.message.header.myref : 0;
		msg.header.action	= initial_messageaction;
		if (mouse)	{
			msg.data.datasave.window	= mouse->window;
			msg.data.datasave.icon		= mouse->icon;
			msg.data.datasave.pos		= mouse->pos;
			}
		if (event)	{
			Desk_message_datarequest*	dr = (Desk_message_datarequest*) &event->data.message.data;
			
			msg.data.datasave.window	= dr->window;
			msg.data.datasave.icon		= (Desk_icon_handle) dr->reference;
			msg.data.datasave.pos		= dr->pos;
			}
		
		msg.data.datasave.estsize	= exportblock->estimatedsize;
		msg.data.datasave.filetype	= exportblock->filetype;
		
		strcpy( msg.data.datasave.leafname, leafname);
		
		Desk_Debug_Printf( Desk_Export_DebugPrefix "Sending Desk_message_DATASAVE. estsize=%i, filetype=%i, leafname='%s'\n",
			msg.data.datasave.estsize,
			msg.data.datasave.filetype,
			msg.data.datasave.leafname
			);
		
		Desk_Wimp_SendMessage( 
			Desk_event_SENDWANTACK, 
			(Desk_message_block*) &msg, 
			//(event) ? event->data.message.header.sender : mouse->window, 
			destination,
			(mouse) ? mouse->icon : 0
			);
		exportblock->last_message_ref = msg.header.myref;
		}
	}




static void	Desk_Export__ResetSaveBlock( Desk_Export_exportblock *exportblock)
	{
	exportblock->ram_progress	= 0;
	exportblock->last_message_ref	= -1;
	/*Desk_Export__EventClaimOrRelease( exportblock, Event_Release);*/
	}

static void	Desk_Export__CleanUp( Desk_Export_exportblock *exportblock)
	{
	Desk_Export__ResetSaveBlock( exportblock);
	Desk_Event_MessagesRelease( Desk_Export__MessageHandler, exportblock);
	}
