/* output.c --- saving and transferring data.  */

#include "d2rd.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include "kernel.h"
#include "output.h"
#include "visdelay.h"
#include "font.h"
#include "flex.h"

#define VERT_SIZE (int)(A4_HEIGHT*180*256)   /* height of page in Draw internal units */

/* Forward declarations.  */

void output_sprite (outputter, FILE *);
void output_draw (outputter, FILE *);
size_t ram_write (const void *, size_t, size_t, FILE *);

static char *my_buf;
static char *your_buf;
static int buf_size;
static int buf_pos;
static wimp_t dest_task;
static int your_ref;


void
send_buf (int size)
{
  wimp_msgstr msg;
  wimp_eventstr b;

  msg.hdr.action = wimp_MRAMTRANSMIT;
  msg.hdr.your_ref = your_ref;
  msg.hdr.size = sizeof (wimp_msgstr);
  msg.data.ramtransmit.addr = your_buf;
  msg.data.ramtransmit.nbyteswritten = size;
  wimpt_noerr (wimp_transferblock (wimpt_task (), my_buf, dest_task, your_buf, size));
  if (size != buf_size)
    wimpt_noerr (wimp_sendmessage (wimp_ESEND, &msg, dest_task));
  else
    {
      wimpt_noerr (wimp_sendmessage (wimp_ESENDWANTACK, &msg, dest_task));
      for (;;)
        {
          wimpt_noerr (wimp_poll (0x1973, &b));
          switch (b.e)
            {
            case wimp_ESEND:
            case wimp_ESENDWANTACK:
              if (b.data.msg.hdr.your_ref == msg.hdr.my_ref)
                {
                  dest_task = b.data.msg.hdr.task;                 /* paranoid */
                  your_ref = b.data.msg.hdr.my_ref;
                  your_buf = b.data.msg.data.ramfetch.addr;
                  buf_size = b.data.msg.data.ramfetch.nbytes;
                  my_buf = realloc (my_buf, buf_size);
                  return;
                }
              else
                tfatal ("EXPORT FAILED!");
#if 0
                tfatal ("References fail (%d, %d, %d, %d)", msg.hdr.my_ref, msg.hdr.your_ref, b.data.msg.hdr.my_ref, b.data.msg.hdr.your_ref);
#endif            
            case wimp_EACK:
              fatal ("RAM transfer failed");
              break;
        
            default:
#if 0
              fatal ("unwanted event: %d", b.e);
#endif
              continue;
            }
          break;
        }    
    }
} /* send_buf */



void
output_send (wimp_msgstr *m)
{
  FILE *f = NULL;

  your_buf = m->data.ramfetch.addr;
  buf_size = m->data.ramfetch.nbytes;
  dest_task = m->hdr.task;
  your_ref = m->hdr.my_ref;

  /* If the buffer on the other size is large enough, we can get rid of the
     sprite at once.  */

  if (saveas_filetype == 0xff9 && buf_size > flex_size ((flex_ptr) &which_menu->area))
    {
      buf_pos = 0;
      buf_size = 0;
      my_buf = (char *) &which_menu->area->number;
      send_buf (flex_size ((flex_ptr) &which_menu->area) - 4);
      return;
    }

  my_buf = xmalloc (buf_size);
  buf_pos = 0;

  visdelay_begin ();

  switch (saveas_filetype)
    {
    case 0xff9:
      ram_write (&which_menu->area->number, 1, which_menu->area->size - 4, f);
      break;
    case 0xaff:
      output_draw (&ram_write, f);
      break;
    default:
      tfatal ("But I have never heard of filetype 0x%x!", saveas_filetype);
      break;
    }

  wimp_create_menu ((wimp_menustr *) -1, 0, 0);
  send_buf (buf_pos);
  visdelay_end ();
  free (my_buf);
} /* saveas_send */



size_t
ram_write (const void *b, size_t size, size_t nmemb, FILE *f)
{
  int n = size * nmemb, ret = n;
  char *from = (char *)b;

  f = f;

  for (;;)
    {
      if (buf_pos + n < buf_size)
        {
          for (; n; buf_pos++, from++, n--)
            my_buf[buf_pos] = *from;
          break;
        }
      for (; buf_pos < buf_size; buf_pos++, from++, n--)
        my_buf[buf_pos] = *from;
      send_buf (buf_size);
      buf_pos = 0;
    }
  return (ret);
} /* ram_write */


void
output_saveas (char *name)
{
  char cmd[300];
  FILE *f;

  wimp_create_menu ((wimp_menustr *)-1, 0, 0);
  visdelay_begin ();

  f = fopen (name, "wb");
  if (f == NULL)
    {
      fatal ("Can't open file `%s'.", name);
      return;
    }

  switch (saveas_filetype)
    {
    case 0xff9:
      fwrite (&which_menu->area->number, 1, which_menu->area->size - 4, f);
      sprintf (cmd, "settype %s ff9", name);
      break;

    case 0xaff:
      output_draw (&fwrite, f);
      sprintf (cmd, "settype %s aff", name);
      break;

    default:
      tfatal ("But I have never heard of filetype 0x%x!", saveas_filetype);
      break;
    }
  fclose (f);
  _kernel_oscli (cmd);
  visdelay_end ();
} /* output_saveas */

/* The routines below are concerned with outputting a draw format file.  */

static int
write_n (FILE *f, outputter put_out, int n, ...)
{
  va_list ap;
  int *a, i;

  va_start (ap, n);
  a = (int *) xmalloc (n * sizeof (int));
  for (i = 0; i < n; i++)
    a[i] = va_arg (ap, int);
  va_end (ap);
  i = put_out (a, sizeof (int), n, f);
  free (a);
  return (i);
} /* write_n */



void
output_draw (outputter put_out, FILE *f)
{
  struct display *w = which_menu;
  struct page *p = w->page;
  struct dvi_file *df = w->file;
  struct text *txt;
  struct rule *rl;
  int *dvi_to_font = xcalloc (sizeof (int), 256);
  int *font_to_dvi = xcalloc (sizeof (int), 256);
  int max_font = 1, size = 0, rsize, i, j, k, l, t, x, y;
  char *s;

  /*
  ** hor_offset holds the horizontal offset for all objects, in
  ** millipoints.  Usually 1in, or 72000 millipoints.
  */
  int hor_offset = w->hor_offset;

  /*
  ** ver_offset holds the distance from the bottom of the page to
  ** the offset point, normally 1in from the top edge.  This is
  ** held in millipoints.
  */
  int ver_offset = w->ver_size - w->ver_offset;

  /* Output DrawFile header.  */

  put_out ("Draw", 1, 4, f);
  write_n (f, put_out, 2, 201, 0);
  put_out ("Preview     ", 1, 12, f);

#define VS (11.69*180.0*256.0*(10000.0 / 14142.0))
  write_n (f, put_out, 4, 0, 0, VS, VERT_SIZE); /* avoid constant-overflow! */

  /* Build and output the font information object.  */

  txt = p->texts;
  for (t = 0; t < p->text_max; t++, txt++)
    if (dvi_to_font[txt->font] == 0)
      {
        font_to_dvi[max_font] = txt->font;
        dvi_to_font[txt->font] = max_font;
        size += 2 + strlen (df->fonts[txt->font]->name);
        max_font++;
      }
  if (size)
    {
      rsize = (size + 3) & ~3;
      write_n (f, put_out, 2, 0, rsize + 8);
      for (i = 1; i < max_font; i++)
        {
          s = df->fonts[font_to_dvi[i]]->name;
          put_out (&i, 1, 1, f);
          put_out (s, 1, 1 + strlen (s), f);
        }
      l = 0;
      for (i = size; i < rsize; i++)
        put_out (&l, 1, 1, f);
    }


  /* Output the text blobs.  */

  for (t = 0, txt = p->texts; t < p->text_max; t++, txt++)
    {
      l = strlen (&p->chars[txt->text]);
      i = (l + 4) & ~3;
      size = 40 * df->fonts[txt->font]->size;
      font_converttoos (hor_offset + txt->x, ver_offset - txt->y, &x, &y);
      font_converttoos (hor_offset + txt->rx, ver_offset - txt->ty, &j, &k);

      x *= 256, y *= 256, j *= 256, k *= 256;

      write_n (f, put_out, 4, 1, 52 + i, x, y);
      write_n (f, put_out, 2, j, k);
      write_n (f, put_out, 7, 0, 0xffffff00, dvi_to_font[txt->font], size, size, x, y);
      put_out (&p->chars[txt->text], 1, l, f);
      k = 0;
      for (j = l; j < i; j++)
        put_out (&k, 1, 1, f);
    }

  /* Output rules.  */

  for (t = 0, rl = p->rules; t < p->rule_max; t++, rl++)
    {
      font_converttoos ((hor_offset + rl->x) * 256,
                        (ver_offset - rl->y) * 256, &x, &y);
      font_converttoos (rl->w * 256, rl->h * 256, &j, &k);

      write_n (f, put_out, 6, 2, 24 + 11 * 4, x, y, x + j, y + k);
      write_n (f, put_out, 11, -1, 0, k, 0x20100042, 2, x, y+k/2, 8, x + j, y+k/2, 0);
    }
} /* output_draw */

/* EOF */
