/* 
* Amiga Graphics for Window Metafile Reader.
*
* $Log: amiga.c,v $
*  Revision 1.9  1995/12/04  19:27:25  JFusco
*  Added interlace flag to OpenScreen().
*  Added debug statements.
*
*  Revision 1.8  1994/11/09  08:16:51  JFusco
*  Does not print title if file is being saved.
*
*  Revision 1.7  1994/11/08  22:10:52  JFusco
*  Added iff save.
*
*  Revision 1.6  1994/06/27  22:40:00  JFusco
*  Reverted to 400x200 window for aspect ratio, which still needs work.
*
*  Revision 1.5  1994/06/26  18:03:53  JFusco
*  Fixes color matching problems.
*
*  Revision 1.4  1994/06/25  02:28:03  JFusco
*  Better fills (?).  Tries to match luminance of color values.
*
*  Revision 1.3  1994/06/20  23:22:21  JFusco
*  Now does polygon filling (barely).
*
*  Revision 1.2  1994/06/19  22:59:55  JFusco
*  Added still more machine dependent stuff.
*
*  Revision 1.1  1994/06/18  03:09:47  JFusco
*  Initial revision
*
*/

#include <stdio.h>
#include <string.h>
#undef memcmp
#include <assert.h>

#define INTUI_V36_NAMES_ONLY

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <graphics/copper.h>
#include <graphics/display.h>
#include <graphics/view.h>
#include <time.h>

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>

#include "wmf.h"
extern wmf_hdr_t FileHdr;
extern char *filename, *savename;

#include "amiga.h"

#define ONE_TIME_WARNING(msg) { static warn=1;\
  if (warn) {warn=0; fprintf(stderr,msg);}}

#ifdef DEBUG
extern int RecCtr;
#define DBGREC printf("RecCtr=%d\n",RecCtr)
#else
#define DBGREC
#endif

static unsigned	        numColors  = NUM_COLORS;
static unsigned	        ColorsUsed = 1;
static RGB_color_t      ColorTable[NUM_COLORS];

#ifdef LATTICE
int CXBRK(void)      {CleanExit(0);return 0;}
/* void chkabort(void)  {return;} */
#endif

struct Screen  *AmigaScreen = NULL;
struct Window  *AmigaWindow = NULL;
struct Library *IntuitionBase = NULL, *GfxBase = NULL;

static unsigned char *tmpRasBuf = NULL;

/* Virtual Window (for debug only) */
extern short Top, Left, Bottom, Right;

/* Local Prototypes. */
void OpenAmigaLibs();
void GetCCIR( RGB_color_t *, unsigned long *, long *, long * );
void OpenAmigaWindow();
void SetRGBColor( RGB_color_t * );

void OpenAmigaLibs()
{
   if(!(IntuitionBase = OpenLibrary("intuition.library", 37L))) {
      printf("Could not open intuition.library!\n");
      exit(0);
   }

   if(!(GfxBase = OpenLibrary("graphics.library",0))) {
      printf("Could not open graphics.library!\n");
      exit(0);
   };
} 

void CleanAmiga()
{
   int i;
#ifdef DEBUG
   printf("ColorsUsed=%d\n",ColorsUsed);
   for(i=0;i<ColorsUsed;i++) {
      printf("%2d r=%02x g=%02x b=%02x\n", i, 
              ColorTable[i].Red, ColorTable[i].Green, ColorTable[i].Blue );
   }
#endif
   if (tmpRasBuf) FreeMem( tmpRasBuf, BITPLANE_SIZE );
   if (AmigaWindow)   CloseWindow(AmigaWindow);
   if (AmigaScreen)   CloseScreen(AmigaScreen);
   if (IntuitionBase) CloseLibrary(IntuitionBase);
   if (GfxBase)       CloseLibrary(GfxBase);
}

void OpenAmigaWindow()
{
   unsigned short *areabuffer = NULL;
   static struct AreaInfo area_info;
   static struct TmpRas tmp_ras;
   static UWORD pens[] = { ~0 };
   unsigned i;

   OpenAmigaLibs();

   AmigaScreen = OpenScreenTags( NULL,
                                 SA_Width,      SCREENX,
                                 SA_Height,     SCREENY,
                                 SA_Depth,      SCREEN_DEPTH,
                                 SA_Type,       CUSTOMSCREEN,
                                 SA_Pens,       (ULONG) pens,
                                 SA_DisplayID,  HIRES_KEY | LACE,
                                 SA_Title,      filename,
                                 SA_ShowTitle,  (ULONG)(savename==NULL),
                                 TAG_DONE );
   if (!(AmigaScreen)) {
      perror("could not open screen.");
      CleanAmiga();
      exit(1);
   };

   AmigaWindow = OpenWindowTags( NULL,
                         WA_Left, 10,
                         WA_Top, 10,
                         WA_Width, WINDOWX,
                         WA_Height, WINDOWY,
                         WA_Borderless, TRUE,
                         WA_Backdrop, TRUE,
                         WA_CloseGadget, FALSE,
                         WA_SizeGadget, TRUE,
                         WA_DragBar, TRUE,
                         WA_SmartRefresh, TRUE,
                         WA_CustomScreen, AmigaScreen,
                         WA_IDCMP, IDCMP_VANILLAKEY | IDCMP_MOUSEBUTTONS,
                         TAG_DONE );

   if (!(AmigaWindow)) {
      perror("could not open window.");
      CleanAmiga();
      exit(1);
   };

   /* Default the color palette. */
   for (i=0; i<numColors; i++) {
      SetRGB4( &AmigaScreen->ViewPort, i,
               (15-(i*(16/numColors))),
               (15-(i*(16/numColors))),
               (15-(i*(16/numColors))) );
   }

   /* Set default value for pens and drawing mode. */
   SetAPen(AmigaWindow->RPort,(numColors-1));
   SetBPen(AmigaWindow->RPort,0);
   SetDrMd(AmigaWindow->RPort,JAM2);

   /* Reserve pen 0 for background store 8-bit color regs. */
   ColorTable[0].Type  = 0;
   ColorTable[0].Red   = 0xff;
   ColorTable[0].Green = 0xff;
   ColorTable[0].Blue  = 0xff;
   ColorsUsed = 1;

   /* Initialize AreaInfo - this should be way too much space! */
   areabuffer = (unsigned short *) calloc(FileHdr.Largest, sizeof(unsigned short));
   tmpRasBuf = AllocMem( BITPLANE_SIZE, MEMF_CLEAR | MEMF_CHIP );
   assert( tmpRasBuf );
   assert( areabuffer );
   InitArea(&area_info, areabuffer,(FileHdr.Largest*sizeof(short))/5 ); 
   InitTmpRas(&tmp_ras, tmpRasBuf, BITPLANE_SIZE );

   AmigaWindow->RPort->TmpRas = &tmp_ras;
   AmigaWindow->RPort->AreaInfo = &area_info;
}

MoveAmiga( short x, short y )
{
   Move( AmigaWindow->RPort, x, y );
}

DrawAmiga( short x, short y )
{
   Draw( AmigaWindow->RPort, x, y );
}

AmigaDelay( unsigned t )
{
   time_t time1;

   time1 = time(NULL);
   while (time(NULL) - time1 < t);
}

void AmigaWait()
{
   ULONG signalmask, signals = 0;

   signalmask = 1L << AmigaWindow->UserPort->mp_SigBit;

   while( !(signals & signalmask) ) {
      signals = Wait(signalmask);
   }
}

void AmigaSave( char *fname )
{
   ScreenDump(AmigaScreen, fname);
}

GetPixelAspect( int *x, int *y )
{
   /* Needs more, okay for now. */
   if (INTERLACED) {
      *x=1;
      *y=1;
   }
   else {
      *x=1;
      *y=2;
   }
}

SetDrawingMode( unsigned short mode )
{
   switch( mode ) {
   case 7:
      DBGREC;
      SetDrMd(AmigaWindow->RPort, JAM2 );
      fprintf(stderr,"XOR drawing\n");
      break;
   case 13:
      SetDrMd(AmigaWindow->RPort, JAM2 | INVERSVID);
      break;
   default:
      DBGREC;
      fprintf(stderr,"Unimplemented Drawing Mode %04hx\n", mode);
      SetDrMd(AmigaWindow->RPort, JAM2 | INVERSVID);
      break;
   };
}

PolygonMoveAmiga( short x, short y )
{
   AreaMove( AmigaWindow->RPort, x, y );
}

PolygonDrawAmiga( short x, short y )
{
   AreaDraw( AmigaWindow->RPort, x, y );
}

PolygonEndAmiga()
{
   if (AreaEnd( AmigaWindow->RPort ))
   {
      DBGREC;
      fprintf(stderr,"Invalid area\n");
   }
}

SetBrushStyleAmiga( brush_t *Brush )
{
   SetRGBColor( &Brush->Color.RGB );
   switch (Brush->Style) {
   case 0: /* Solid */
      SetDrMd(AmigaWindow->RPort,JAM2);
      break;
   case 1: /* Hollow */
      SetDrMd(AmigaWindow->RPort,JAM2|COMPLEMENT);
      break;
   case 2:
      ONE_TIME_WARNING("Cross hatched brush not supported\n");
      break;
   default:
      ONE_TIME_WARNING("Unknown brush type.\n");
      break;
   }
}

SetPenStyleAmiga( pen_t *Pen )
{
   static int warnings=0;
   unsigned short pattern;

   if (Pen->Width>1) { 
      ONE_TIME_WARNING("Only pen width of 1 supported.\n"); 
   }

   switch( Pen->Style ) {
   case 0:
      pattern = 0xffff; /* Solid */
      break;
   case 1:
      pattern = 0xcccc; /* Dashed */
      break;
   case 2:
      pattern = 0xaaaa; /* Dotted */
      break;
   case 3:
      pattern = 0xd6b5; /* Alternating Dot and Dash */
      break;
   case 4: 
      pattern = 0xb5ad; /* Alternating Dash and two Dots */
      break;
   case 5: 
      pattern = 0xffff; /* None ? */
      break;
   case 6:
      /* Inside Frame. */
      DBGREC;
      fprintf(stderr,"Inside frame pen type is unimplemented.\n");
      break;
   default:
      DBGREC;
      fprintf(stderr,"Unrecognized pen style %hd assuming solid.\n",Pen->Style);
      pattern = 0xffff;
      break;
   }
   SetRGBColor( &Pen->Color.RGB );
}

void SetRGBColor( RGB_color_t *Color )
{
   unsigned long lum, mlum;
   long Cb, Cr, best, mCb, mCr, match, bestdiff;
   unsigned idx, idx_match;

   if (Color->Type != 1) {

      /* Set to zero for memcmp. */
      Color->Type = 0;

      /* Check to see if it's already in the color table. */
      for( idx=0; idx<ColorsUsed; idx++) {

         if (memcmp( &ColorTable[idx], 
                     Color, sizeof(RGB_color_t)) == 0 ) break;
      }

      if (ColorsUsed && idx < ColorsUsed) {

         /* It's already in the color table. */
         SetAPen(AmigaWindow->RPort,idx);
      }

      else if ( ColorsUsed != numColors ) {

         /* Not in the color table, but there's room for a new entry. */
         assert( ColorsUsed < numColors );
         memcpy( &ColorTable[ColorsUsed], 
                 Color, sizeof(RGB_color_t));
         ColorTable[ColorsUsed].Type = 0;
         SetRGB4( &AmigaScreen->ViewPort, 
                  ColorsUsed,
                  (ColorTable[ColorsUsed].Red   >> 4),
                  (ColorTable[ColorsUsed].Green >> 4),
                  (ColorTable[ColorsUsed].Blue  >> 4) );
         SetAPen(AmigaWindow->RPort,ColorsUsed);
         ColorsUsed++;
      }

      else {

         /* Not in the table and there's no more room. */
         /* Get the closest match.                     */
         ONE_TIME_WARNING("Too many colors, will find closest match.\n");
         GetCCIR( Color, &lum, &Cb, &Cr );

         for( idx=0; idx<ColorsUsed; idx++) {

            GetCCIR( &ColorTable[idx], &mlum, &mCb, &mCr );

	    /* Compare the dot product of the two color 'vectors' */
            match = lum * mlum + Cb * mCb + Cr * mCr;

            if (idx==0) {
               idx_match = 0;
               bestdiff = abs(match);
               best = match;
            }

            /* Take our best match and compare the difference. */
	    else if (bestdiff > abs(match-best)) {
               idx_match = idx;
	       bestdiff = abs(match-best);
               best = match;
            }
         }
         SetAPen(AmigaWindow->RPort,idx_match);
      }
      SetBPen(AmigaWindow->RPort,0);
   }
   else {
      Indexed_color_t *lu = (Indexed_color_t *)Color;
      if (lu->Index < ColorsUsed) {
         fprintf(stderr,"Using color index\n");
         SetAPen(AmigaWindow->RPort,lu->Index);
      }
      else {
         DBGREC;
         fprintf(stderr,"Color index out of range (not supported)\n");
      }
   }
}

/* Make a color vector. */
void GetCCIR( RGB_color_t *Color, unsigned long *lum, 
                             long *Cb, long *Cr )
{
   *lum =  Color->Red   * 30
         + Color->Green * 59
         + Color->Blue  * 11;

   *Cb  =  Color->Red   * 17
         - Color->Green * 33
         + Color->Blue  * 50;

   *Cr  =  Color->Red   * 50
         - Color->Green * 42
         - Color->Blue  *  8;

   *lum /= 100;
   *Cb  /= 100;
   *Cr  /= 100;
}
