#include <alloc.h>
#include <stdlib.h>
#include "tge.h"
#include "mouse.h"


/* More colours may, of course, be added. */

#define INVISIBLE	0
#define	BLACK		1
#define	WHITE		2


/* This function returns the number of the colour which is closest to the
   colour whose RGB values are given.  The colour in colourExclude is
   guaranteed not to be chosen; it is used to avoid the selection of colour
   zero -- the "invisible" colour. */

unsigned colourClosestTo(unsigned char red, unsigned char green, unsigned char
	blue, unsigned colourExclude);


/*****
****** Pointer definitions.  0=invisible, 1=black, 2=white
*****/

unsigned char arrowPointer[] = {		/* ARROW_POINTER */
  6,0,8,0,			/* dimesions */
  1,1,0,0,0,0,			/* pointer data */
  1,2,1,0,0,0,
  1,2,2,1,0,0,
  1,2,2,2,1,0,
  1,2,2,2,2,1,
  1,2,1,2,1,1,
  1,1,1,1,2,1,
  0,0,0,1,1,1
};

unsigned char targetPointer[] = {		/* TARGET_POINTER */
  9,0,9,0,			/* dimesions */
  0,0,0,1,1,1,0,0,0,
  0,0,1,2,2,2,1,0,0,		/* pointer data */
  0,1,2,1,1,1,2,1,0,
  1,2,1,0,0,0,1,2,1,
  1,2,1,0,2,0,1,2,1,
  1,2,1,0,0,0,1,2,1,
  0,1,2,1,1,1,2,1,0,
  0,0,1,2,2,2,1,0,0,
  0,0,0,1,1,1,0,0,0
};

static unsigned char mousePointer[1024];  /* default buffer size: 1 Kb */
static int curPointerNum = -1;		  /* the current pointer */


/******
******* Code
******/

void setupMousePointer(int pointerNum)
{
  unsigned char *src, *dest=mousePointer;
  register unsigned count, max;
  unsigned black, white;
  unsigned wide, deep;
  int xHotSpot, yHotSpot;

  curPointerNum = pointerNum;	/* update current pointer number */
  switch (pointerNum)		/* set up proper source for pointer */
  {
    case ARROW_POINTER:		/* is it the arrow pointer? */
      src = arrowPointer;
      xHotSpot = 0;
      yHotSpot = 0;
      break;
    case TARGET_POINTER:	/* is it the target pointer? */
      src = targetPointer;
      xHotSpot = 4;
      yHotSpot = 4;
      break;
    default:			/* undefined */
      return;
  }


  /* Find the colours closest to the ones in the pointer */

  black = colourClosestTo(0, 0, 0, 0);
  white = colourClosestTo(255, 255, 255, 0);


  wide = *(unsigned *)src;		/* pointer dimensions */
  src += 2;
  deep = *(unsigned *)src;
  src += 2;
  *(unsigned *)dest = wide;
  dest += 2;
  *(unsigned *)dest = deep;
  dest += 2;


  /* Create the pointer bitmap using newly-found colour numbers */

  max = wide * deep;
  for (count=0; count<max; count++)
  {
    switch (*src)			/* decide what to put in dest */
    {
      case BLACK:
        *dest = black;
        break;
      case WHITE:
        *dest = white;
        break;
      default:
        *dest = *src;
    }
    src++;
    dest++;
  }

  /* Pass the bitmap to the driver */

  setPointerMouse(xHotSpot, yHotSpot, mousePointer);
}

/* Set up the same pointer again, thus possibly rechosing the colours
   composing the pointer.  This function is useful after the palette has
   been changed, but you want the pointer to remain the same. */

void setPointerColours(void)
{
  setupMousePointer(curPointerNum);
}


/* Find the colour closest to the one specified, excluding colourExclude */

unsigned colourClosestTo(unsigned char red, unsigned char green,
	unsigned char blue, unsigned colourExclude)
{
  unsigned char *palette;
  register int count;
  int redDif, greenDif, blueDif, curDif, bestDif=1000, curNet, bestNet=1000;
  unsigned char bestColour;

  if (((void *)palette=farmalloc(768)) == NULL)	/* allocate some RAM */
    return (-1);

  getBlockPalette(0, 256, palette);		/* get the palette */


  /* Main colour-finding loop */

  for (count=255; count>=0; count--)
  {
    if (count != colourExclude)
    {
      redDif = red - palette[3*count];	/* calculate greatest difference */
      greenDif = green - palette[3*count+1];
      blueDif = blue - palette[3*count+2];
      curNet = redDif + greenDif + blueDif;
      if (redDif < 0)
        redDif = -(redDif);
      if (greenDif < 0)
        greenDif = -(greenDif);
      if (blueDif < 0)
        blueDif = -(blueDif);
      curDif = max(max(redDif,greenDif), blueDif);

      if (!curDif)			/* if same colour, return */
      {
        bestColour = count;
        break;
      }
      if (curDif < bestDif)		/* if better colour, set bestColour */
      {
        if (curNet <= bestNet)
        {
          bestDif = curDif;
          bestColour = count;
        }
      }
    }
  }

  farfree(palette);			/* clean up and go home */
  return (bestColour);
}