/* !FormText.import.c */

#include <string.h>

#include "event.h"
#include "wimplib.h"

#include "err.h"
#include "import.h"

const char WimpScrap[] = "<Wimp$Scrap>";

static struct {
  ImportLoader loader;
  ImportBufferHandler bufferer;
  ImportComplete complete;
  void *buffer;
  int total_size;	/* Total bytes transferred so far */
  int buffer_size;	/* Size of buffer to be filled */
  WimpMessage message;	/* Original message in case RAMFetch not ack'ed */
} import_ref;

/* Local handlers */
static int ramtrans_handler(WimpMessage *, void *);
static int noack_handler(int, WimpPollBlock *, IdBlock *, void *);

void import_start(const WimpMessage *message, ImportLoader loader,
	ImportBufferHandler bufferer, ImportComplete complete, void *handle)
{
  int result;	/* bytes loaded */
  int scrap;	/* loading from Scrap file */
  import_ref.message = *message;
  import_ref.message.hdr.your_ref = message->hdr.my_ref;
  switch (message->hdr.action_code)
  {
    case Wimp_MDataLoad:
    case Wimp_MDataOpen:
      import_ref.message.hdr.action_code = Wimp_MDataLoadAck;
      /* If Message_DataOpen reply straight away regardless of whether
         load is successful */
      if (message->hdr.action_code == Wimp_MDataOpen)
        E(wimp_send_message(Wimp_EUserMessage, &import_ref.message,
        	message->hdr.sender, 0, 0));
      /* Get client to load */
      result = (*loader)(message->data.data_load.leaf_name, handle);
      /* Reply now if successful after Message_DataLoad */
      if (result>0 && message->hdr.action_code==Wimp_MDataLoad)
        E(wimp_send_message(Wimp_EUserMessage, &import_ref.message,
        	message->hdr.sender, 0, 0));
      scrap = !strcmp(message->data.data_load.leaf_name, WimpScrap);
      /* Delete Scrap if necessary */
      if (scrap)
      {
        _kernel_osfile_block kob;
        _kernel_osfile(6, WimpScrap, &kob);
      }
      /* Notify client load is complete */
      if (scrap)
        (*complete)(0, result, handle);
      else
        (*complete)(message->data.data_load.leaf_name, result, handle);
      break;
    case Wimp_MDataSave:
      /* First try RAM transfer if we can */
      if (bufferer)
      {
        import_ref.loader = loader;
        import_ref.bufferer = bufferer;
        import_ref.complete = complete;
        import_ref.total_size = 0;
        import_ref.buffer = 0;
        /* Some applications don't fill this in properly so check */
        import_ref.buffer_size = message->data.data_save.estimated_size;
        if (import_ref.buffer_size <= 0) import_ref.buffer_size = ArbitrarySize;
        /* Get client to allocate buffer */
        import_ref.buffer_size = (*bufferer)(&import_ref.buffer,
        	import_ref.buffer_size, handle);
        if (!import_ref.buffer_size) return;	/* Oh dear */
        /* Send first Message_RAMFetch */
        import_ref.message.hdr.size = 20 + sizeof(WimpRAMFetchMessage);
        import_ref.message.hdr.action_code = Wimp_MRAMFetch;
        import_ref.message.data.ram_fetch.buffer = import_ref.buffer;
        import_ref.message.data.ram_fetch.buffer_size = import_ref.buffer_size;
        if (E(wimp_send_message(Wimp_EUserMessageRecorded, &import_ref.message,
        	message->hdr.sender, 0, 0)))
          return;
        /* Register handlers for replies to it */
        E(event_register_message_handler(Wimp_MRAMTransmit,
        	ramtrans_handler, handle));
        E(event_register_wimp_handler(-1, Wimp_EUserMessageAcknowledge,
        	noack_handler, handle));
        /* Store original message */
        import_ref.message = *message;
      }
      else	/* Use Scrap */
      {
        import_ref.message.hdr.action_code = Wimp_MDataSaveAck;
        import_ref.message.data.data_save_ack.estimated_size = -1;
        	/* 'unsafe' destination */
        strcpy(import_ref.message.data.data_save_ack.leaf_name, WimpScrap);
        E(wimp_send_message(Wimp_EUserMessage, &import_ref.message,
        	message->hdr.sender, 0, 0));
      }
  }
}

/* Frees local handlers when we've finished */
static void free_handlers(void *handle)
{
  E(event_deregister_message_handler(Wimp_MRAMTransmit,
  	ramtrans_handler, handle));
  E(event_deregister_wimp_handler(-1, Wimp_EUserMessageAcknowledge,
  	noack_handler, handle));
}

static int ramtrans_handler(WimpMessage *message, void *handle)
{
  int oldsize = import_ref.buffer_size;
  import_ref.total_size += message->data.ram_transmit.nbytes;
  import_ref.buffer_size = (*import_ref.bufferer)(&import_ref.buffer,
  	message->data.ram_transmit.nbytes, handle);
  if (message->data.ram_transmit.nbytes < oldsize)
  {
    free_handlers(handle);
    (*import_ref.complete)(0, import_ref.total_size, handle);
  }
  else if (import_ref.buffer_size > 0)
  {
    import_ref.message.hdr.size = 20 + sizeof(WimpRAMFetchMessage);
    import_ref.message.hdr.your_ref = message->hdr.my_ref;
    import_ref.message.hdr.action_code = Wimp_MRAMFetch;
    import_ref.message.data.ram_fetch.buffer = import_ref.buffer;
    import_ref.message.data.ram_fetch.buffer_size = import_ref.buffer_size;
    if (E(wimp_send_message(Wimp_EUserMessageRecorded, &import_ref.message,
    	message->hdr.sender, 0, 0)))
      free_handlers(handle);
  }
  else
    free_handlers(handle);
  return 1;
}

static int noack_handler(int c, WimpPollBlock *event, IdBlock *id, void *handle)
{
  free_handlers(handle);
  /* If any bytes have already been transferred abandon transfer quietly,
     otherwise resort to Scrap */
  if (!import_ref.total_size)
    import_start(&import_ref.message, import_ref.loader, 0,
    	import_ref.complete, handle);
  return 1;
}
