/* retinaflick.c
   Robert Poole
*/

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

#include <exec/libraries.h>

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <devices/timer.h>
#include <clib/alib_protos.h>
#include <time.h>

#include <libraries/retina.h>
#include <clib/retina_protos.h>
#include <pragmas/retina_pragmas.h>

#include "xflick.h"
#include "time_routines.h"

#define USE_WRITERECT 1

extern struct IntuitionBase *IntuitionBase;
struct _xy_RetinaBase *RetinaBase;
struct DefaultScreenInfo screen_info;

int __oslibversion = 37;

struct RetinaScreen *rScreen;
verbose = 0;
int memwidth, scrheight;
/* for timing */
static int usecs, secs;
static int fdelay = -1;     /* frame delay */

/* prototypes */
static void convert_fdelay(void);
static void func_disp(int, int, unsigned char *, int, int, int, int, int, int);


/* where the real functions are */
static void convert_fdelay(void)
{
  usecs = (fdelay % 70) * 14285;
  secs = fdelay / 70;
}

int main(int argc, char *argv[])
{
  register int i;
  int fd;
  struct fli_header flihead;
  unsigned char *data = NULL;
  int repeatcount = 2;
  unsigned char notfirst = 0;

  if (!setup_timer()) {
    fprintf(stderr, "Can't allocate timer resources.\n");
    exit(-1);
  }

  if (argc > 1) {
    for (i = 1; i < argc; i++) {
      if (argv[i][0] != '-') {
	fd = open(argv[i], 0);
	if (fd < 0) {
	  fprintf(stderr, "Can't open file %s.\n", argv[i]);
	  exit(-1);
	}
	read_flihead(fd, &flihead);
      }
      else {
	switch(argv[i][1]) {
	case 'd':
	  fdelay = atoi(argv[i] + 2);
	  printf("Frame delay of %d/70 sec.\n", fdelay);
	  break;

	case 'r':
	  repeatcount = atoi(argv[i] + 2);
	  printf("Will loop animation %d times.\n", repeatcount);
	  break;

	case '?':
	case 'h':
	  printf("Usage:\n");
	  printf("%s [flags] filename\n", argv[0]);
	  printf("Flags:\n");
	  printf("-d<number>\tFrame delay in 1/70th of a sec.\n");
	  printf("-r<number>\tRepeat count.\n");
	  printf("-?\tThis information display.\n");
	  printf("-h\n");
	  exit(0);
	  break;

	default:
	  fprintf(stderr, "Warning: illegal option %s\n", argv[i]);
	  break;
	}
      }
    }
  }
  else {
    fprintf(stderr, "Must start from shell, and must supply filename.\n");
    exit(-1);
  }

  if ((flihead.fhd_magic & 0x0000ffff) != FLI_MAGIC &&
      (flihead.fhd_magic & 0x0000ffff) != FLC_MAGIC) {
    fprintf(stderr, "%s is not a FLI/FLC file.\n", argv[1]);
    exit(0);
  }

  if (fdelay < 0) {
    fdelay = flihead.fhd_speed;
  }
  convert_fdelay();

  data = (unsigned char *) malloc(flihead.fhd_width * flihead.fhd_height);
  if (data == NULL) {
    fprintf(stderr, "Failed to allocate %d bytes.\n",
	    flihead.fhd_width * flihead.fhd_height);
    exit(-1);
  }

  if ((RetinaBase = (struct _xy_RetinaBase *)
       OpenLibrary("retina.library", RETINA_LIB_VERSION)) != NULL) {
    if ((rScreen = Retina_OpenScreen(memwidth = flihead.fhd_width,
				     scrheight = flihead.fhd_height,
				     MID_DEFAULT_08,
				     RSFF_DOUBLEBUFFER | RSFF_DBUFPALETTE,
				     NULL))
	!= NULL) {
      /* screen has opened successfully, we can do our thing now */

      while (repeatcount--) {
	interpret_fli(fd, &flihead, data, notfirst, func_disp);
	notfirst = 1;
      }

      /* clean up now */
      close(fd);
      free(data);
      dispose_timer_resources();
      Retina_CloseScreen(rScreen);
      CloseLibrary((struct Library *) RetinaBase);
      exit(0);
    }
    else {
      fprintf(stderr, "Couldn't open double buffered Retina screen.\n");
      if (data)
	free(data);
      dispose_timer_resources();
      exit(-1);
    }
  }
  else {
    fprintf(stderr, "Couldn't open retina.library version %ld.\n",
	    RETINA_LIB_VERSION);
    if (data)
      free(data);
    dispose_timer_resources();
    exit(-1);
  }
}

static void func_disp(int ftype, int frame, unsigned char *buf,
		      int srcx, int srcy, int destx, int desty,
		      int swidth, int sheight)
{
  static int colchange = 0;  /* colormap has changed */
#ifndef USE_WRITERECT
  register int i, j;
#endif

  switch (ftype) {
  case FLI_COPY:
  case FLI_LC:
  case FLI_DELTA:
  case FLI_BRUN:

#ifdef USE_WRITERECT
    Retina_WriteRect(buf, srcx, srcy, memwidth,
		     RECTMODE_256 | RECTMODEF_BYTEMEMWIDTH,
		     rScreen,
		     destx, desty, swidth, sheight, NULL);
#else
    for (j = 0; j < sheight; j++) {
      for (i = 0; i < swidth; i++) {
	Retina_SetAPen(rScreen, *(buf + srcx + i + (srcy + j)*memwidth));
	Retina_WritePixel(rScreen, i + destx, j + desty);
      }
    }
#endif

    /* we can now swap the double-buffered bitmaps */
    Retina_SwapBitMap(rScreen);
    Retina_CopyRect(rScreen, destx, desty, swidth, sheight, rScreen,
		    destx, desty, /* damage region */
		    CRF_SRCDBUF);
    break;

  case FLI_BLACK:
#ifdef debug
    printf("I am setting a region black.\n");
#endif
    Retina_SetAPen(rScreen, 0x00000000L); /* we want BLACK! */
    Retina_RectFill(rScreen, destx, desty,
		    destx + swidth, desty + sheight);
    break;

  case FLI_COLOR:
    while (srcy--) {
      UBYTE color, red, grn, blu;

      Retina_SetPalette(rScreen, color = srcx++,
			red = ((*(buf++) & 0x3f) << 2),
			grn = ((*(buf++) & 0x3f) << 2),
			blu = ((*(buf++) & 0x3f) << 2));
#ifdef DEBUG
      printf("Setting color %d to R%2x G%2x B%2x\n",
	     color, red, grn, blu);
#endif
    }
    colchange = 1;
    break;

  case FLI_256_COLOR:
    while (srcy--) {
      UBYTE color, red, grn, blu;

      Retina_SetPalette(rScreen, color = srcx++,
			red = (*(buf++) & 0xff),
			grn = (*(buf++) & 0x3f),
			blu = (*(buf++) & 0x3f));
#ifdef DEBUG
      printf("Setting color %d to R%2x G%2x B%2x\n",
	     color, red, grn, blu);
#endif
    }
    colchange = 1;
    break;

  case FLI_SYNC:
    if (colchange) {
      colchange = 0;
    }
    /* is there a frame delay? */
    if (usecs || secs) {
      /* wait for frame delay */
      synchronous_wait(secs, usecs);
    }
    break;
  }
}
