/*
 * 1995; Gary Duncan (gduncan@werple.net.au)
 *
 *      - based on boxes code from DlineArt (released with DMouse)
 *
 *      - this is freeware.
 *
 *
 */
/*-------------------------------------------------------------*/

/*-------------------------------------------------------------
Functions:-


PenCountMyBlanker (TAGITEM * TagList)
QueryMyBlanker (void)
InitMyBlanker (TAGITEM * TagList)
EndMyBlanker (BOXES * BX)
AnimMyBlanker (BOXES * BX)
MyBlankerLibFree (void)
MyBlankerLibInit (void)
do_box (RASTPORT * rp, XY * start, XY * end, XY * limit)
checkbounce_start (XY * start, XY * limmax, XY * limmin)
checkbounce_end (XY * end, XY * limmax, XY * limmin)
get_rand_point (int min, int max)
get_rand_dir ()
get_rand_scale ()
my_ran (int lim)
time_diff (ULONG e_freq, ECLOCKVAL * e_val1, ECLOCKVAL * e_val2)
open_timer (void)
close_timer (void)
DrawBounds (RASTPORT * rp, XY * tlhc, XY * brhc)
init_array (BOXES * BX)

-------------------------------------------------------------*/

#include <stdlib.h>
#include <stdio.h>
#include <exec/types.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>

#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/macros.h>
#include <clib/timer_protos.h>
#include <devices/timer.h>
#include <dos/dos.h>
#include <exec/memory.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <exec/execbase.h>
#include <graphics/text.h>
#include <graphics/gfxbase.h>
#include <intuition/preferences.h>
#include <intuition/screens.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/iffparse.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <time.h>
#include <utility/tagitem.h>

#include "/BTD.h"

#include "typedefs.h"		/* GMD */
/*-------------------------------------------------------------*/

#if 0
#define DPUTSTR(x) DPutStr(x)
void DPutStr (char *p);
#else
#define DPUTSTR(x)
#endif

#define DTAG(o) (BTD_Client+(o))

#define BX_Boxes DTAG(0)
#define BX_Speed DTAG(1)

#define DEF_BOXES 10L
#define MAX_BOXES 20L
#define MINBOUND 10

#define MIN_SPEED 1L
#define MAX_SPEED 10L

typedef struct BTDInfo BTDINFO;
typedef struct BTDDrawInfo BTDDRAWINFO;

typedef struct XY
  {
    LONG x;
    LONG y;
  }
XY;

typedef struct DIRECTION
  {
    XY start;
    XY end;
  }
DIRECTION;



#define FindTagData(l,t,d) GetTagData((t),(d),(l))

INTUITIONBASE *IntuitionBase;
GFXBASE *GfxBase;
TIMERBASE *TimerBase;
TIMEREQUEST box_timer;
TIMEREQUEST *t_ptr = &box_timer;
ECLOCKVAL e_then, e_now, e_lcolch;
ULONG e_freq;

typedef struct
  {
    UBYTE r;
    UBYTE g;
    UBYTE b;
  }
MY_RGB;

#define MY_SIX 6

MY_RGB col_ray[MY_SIX] =
{
  0, 0, 255,
  0, 255, 0,
  255, 0, 0,
  0, 255, 255,
  255, 255, 0,
  255, 0, 255
};

int delay[MAX_SPEED] =
{
  0,
  5,
  10,
  20,
  30,
  40,
  50,
  80,
  90,
  100
};


struct BTDInteger BoxesIntParams[] =
{
  BX_Speed, "Delay", BTDPT_INTEGER, 2L, MIN_SPEED, MAX_SPEED, TRUE,
};

struct BTDNode *BoxesParams[] =
{
  &BoxesIntParams[0].BI_Node,
  NULL
};

BTDINFO BoxesInfo =
{
  BTDI_Revision,
  MAKE_ID ('B', 'O', 'X', ' '),
  "Boxes Blanker",
  "little boxes, full of ticky-tack...",
  "Gary Duncan 1997(1.2)",
  BoxesParams
};

typedef struct
  {
    BTDDRAWINFO *BTDDrawInfo;
    LONG entry_count;
    LONG col_count;
    LONG xoffs;
    LONG g_Count;
    LONG g_scale1;
    LONG g_scale2;
    LONG speed;
    LONG n_boxes;
    LONG debug_count;
    LONG init_count;
    XY tlhc;
    XY brhc;
    XY p_stt[MAX_BOXES];
    XY p_end[MAX_BOXES];
    DIRECTION dir;
  }
BOXES;

/*
 * func prototypes
 */
static USHORT my_ran (int);
static int get_rand_scale (void);
static int get_rand_dir (void);
static int get_rand_point (int min, int max);
static BOOL checkbounce_start (XY * end, XY * limmax, XY * limmin);
static BOOL checkbounce_end (XY * end, XY * limmax, XY * limmin);
static void do_box (BOXES * BX, XY * start, XY * end, XY * limit);
static LONG time_diff (ULONG e_freq, ECLOCKVAL * e_val1, ECLOCKVAL * e_val2);
static BOOL open_timer (void);
static void close_timer (void);
static void DrawBounds (RASTPORT * rp, XY * p1, XY * p2);
static void init_array (BOXES * BX);
void dbg (BOXES * BX, char *ptr);

/* library stuff */

char MyBlankerName[] = "boxes.btd";
char MyBlankerID[] = "Boxes Blanker V" VERSION "." REVISION " for BTD";

/*
 * DEBUG stuff (13 Oct 97)
 */

long DBFh;
int gBDI_Left;
int gBDI_Top;
int gBDI_Width;
int gBDI_Height;

/*
 *******************************************************************
 */

ULONG
PenCountMyBlanker (TAGITEM * TagList)

{
//
  // debug
  //
  DPUTSTR ("PenCountMyBlanker\n");

  return 1;

}
/*
 *******************************************************************
 */

BTDINFO *
QueryMyBlanker (void)

{
//
  // debug
  //
  DPUTSTR ("QueryMyBlanker\n");

  return &BoxesInfo;
}
/*
 *******************************************************************
 */

BOXES *
InitMyBlanker (TAGITEM * TagList)

{
  BOXES *BX;
  BTDDRAWINFO *BTDDrawInfo;
  UWORD pen, col;
  ULONG *Error, Dummy, Instance;

//
  // debug
  //
  DPUTSTR ("InitMyBlanker\n");



  if ((BTDDrawInfo = (BTDDRAWINFO *)
       FindTagData (TagList, BTD_DrawInfo, NULL)) == NULL)
    return NULL;

  Error = (LONG *) FindTagData (TagList, BTD_Error, (ULONG) & Dummy);

  if ((BX = AllocVec (sizeof (BOXES), MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
    {
      *Error = BTDERR_Memory;
      return NULL;
    }


  BX->BTDDrawInfo = BTDDrawInfo;

  Instance = FindTagData (TagList, BTD_Instance, 0L);
  BX->speed = FindTagData (TagList, BX_Speed, 0L);

  /*
   * define the rectangle (shrink a bit)
   */

  BX->tlhc.x = BX->BTDDrawInfo->BDI_Left + MINBOUND;
  BX->tlhc.y = BX->BTDDrawInfo->BDI_Top + MINBOUND;
  BX->brhc.x = BX->BTDDrawInfo->BDI_Width + BX->tlhc.x - 2 * MINBOUND;
  BX->brhc.y = BX->BTDDrawInfo->BDI_Height + BX->tlhc.y - 2 * MINBOUND;

  /* (DEBUG) */
  gBDI_Left = BX->tlhc.x;
  gBDI_Top = BX->tlhc.y;
  gBDI_Width = BX->brhc.x;
  gBDI_Height = BX->brhc.y;


  /*
   * Init array
   */
  init_array (BX);

  col = BX->col_count;

  pen = BTDDrawInfo->BDI_Pens[0];

  BTDDrawInfo->BDI_Red[pen] = col_ray[col].r;
  BTDDrawInfo->BDI_Green[pen] = col_ray[col].g;
  BTDDrawInfo->BDI_Blue[pen] = col_ray[col].b;

  BTDDrawInfo->BDI_Changed[pen] = TRUE;
  SetAPen (BTDDrawInfo->BDI_RPort, pen);

  /*
   * get and save current E-clock value
   */
  e_freq = ReadEClock (&e_now);
  e_freq = ReadEClock (&e_then);
  e_freq = ReadEClock (&e_lcolch);

  return BX;
}


/*
 *******************************************************************
 */

void
EndMyBlanker (BOXES * BX)

{
//
  // debug
  //
  DPUTSTR ("EndMyBlanker\n");

  FreeVec (BX);
}


/*
 *******************************************************************
 */

void
AnimMyBlanker (BOXES * BX)

{
  int new_offs, erase_offs, loop;
  XY *sxy, *exy, t_sxy, t_exy;
  int xmax, ymax;
  int curr_offs;
  UWORD pen;
  int col, j;

  XY *tlhc = &BX->tlhc;
  XY *brhc = &BX->brhc;
  RASTPORT *rp = BX->BTDDrawInfo->BDI_RPort;
  BTDDRAWINFO *BTDDrawInfo = BX->BTDDrawInfo;

  //
  // debug
  //
  if (BX->debug_count++ == 0)
    DPUTSTR ("AnimMyBlanker\n");



  /*
   * get current E-clock value ; return if < delay 
   */
  e_freq = ReadEClock (&e_now);

  if (time_diff (e_freq, &e_then, &e_now) < delay[BX->speed - 1])
    {
      return;
    }

  /*
   * reset box array after 1000 loops (arbitary)
   */
  if ((BX->init_count++ % 1000) == 0)
    {
      /*
       * erase existing boxes
       */
      for (j = 0; j < MAX_BOXES; ++j)
	{
	  if (BX->p_stt[j].x != -1)
	    {
	      SetAPen (rp, BTD_BgPen);

	      do_box (BX, &BX->p_stt[j], &BX->p_end[j], brhc);

	      SetAPen (rp, BTDDrawInfo->BDI_Pens[0]);
	    }
	}
      init_array (BX);
    }
  /*
   * draw Bounds (Debug feature - visually check boxes don't overlap)
   */

  t_sxy.x = BX->BTDDrawInfo->BDI_Left;
  t_sxy.y = BX->BTDDrawInfo->BDI_Top;
  t_exy.x = BX->BTDDrawInfo->BDI_Width + t_sxy.x - 1;
  t_exy.y = BX->BTDDrawInfo->BDI_Height + t_sxy.y - 1;

  //DrawBounds (rp, &t_sxy, &t_exy);

  /*
   * time to change colour (2 secs) ?
   */
  if (time_diff (e_freq, &e_lcolch, &e_now) > 2000)
    {
      ++BX->col_count;		/* cycle colours */
      e_lcolch = e_now;
    }

  col = BX->col_count % MY_SIX;	/* cycle colours */

  pen = BTDDrawInfo->BDI_Pens[0];

  BTDDrawInfo->BDI_Red[pen] = col_ray[col].r;
  BTDDrawInfo->BDI_Green[pen] = col_ray[col].g;
  BTDDrawInfo->BDI_Blue[pen] = col_ray[col].b;

  BTDDrawInfo->BDI_Changed[pen] = TRUE;

  for (loop = 0; loop < 1; ++loop)	/* change loop count later ?? */
    {
      curr_offs = BX->xoffs;
      sxy = &BX->p_stt[curr_offs];
      exy = &BX->p_end[curr_offs];

      xmax = brhc->x;
      ymax = brhc->y;
      /*
       * draw a box...
       */
      SetAPen (rp, pen);

      do_box (BX, sxy, exy, brhc);

      /*
       * start x,y
       */
      if (checkbounce_start (sxy, brhc, tlhc))
	{
	  if (sxy->x <= tlhc->x)
	    {
	      BX->dir.start.x = 1;
	    }
	  else
	    {
	      if (sxy->x >= brhc->x)
		BX->dir.start.x = -1;
	      else
		BX->dir.start.x = get_rand_dir ();
	    }

	  if (sxy->y <= tlhc->y)
	    {
	      BX->dir.start.y = 1;
	    }
	  else
	    {
	      if (sxy->y >= brhc->y)
		BX->dir.start.y = -1;
	      else
		BX->dir.start.y = get_rand_dir ();
	    }

	  BX->g_scale1 = get_rand_scale ();
	}

      /*
       * end x,y
       */
      if (checkbounce_end (exy, brhc, tlhc))
	{
	  if (exy->x <= tlhc->x)
	    {
	      BX->dir.end.x = 1;
	    }
	  else
	    {
	      if (exy->x >= brhc->x)
		BX->dir.end.x = -1;
	      else
		BX->dir.end.x = get_rand_dir ();
	    }

	  if (exy->y <= tlhc->y)
	    {
	      BX->dir.end.y = 1;
	    }
	  else
	    {
	      if (exy->y >= brhc->y)
		BX->dir.end.y = -1;
	      else
		BX->dir.end.y = get_rand_dir ();
	    }

	  BX->g_scale2 = get_rand_scale ();
	}

      if (++BX->xoffs == MAX_BOXES)
	BX->xoffs = 0;

      erase_offs = new_offs = BX->xoffs;

      /*
       * Erase the oldest line (if entry valid)
       */

      if (BX->p_stt[erase_offs].x != -1)
	{
	  SetAPen (rp, BTD_BgPen);

	  do_box (BX, &BX->p_stt[erase_offs], &BX->p_end[erase_offs], brhc);

	  SetAPen (rp, BTDDrawInfo->BDI_Pens[0]);
	}

      /* Calculate the next point */

      BX->p_stt[new_offs].x =
	(BX->g_scale1 * BX->dir.start.x) + BX->p_stt[curr_offs].x;

      BX->p_stt[new_offs].y =
	(BX->g_scale1 * BX->dir.start.y) + BX->p_stt[curr_offs].y;

      BX->p_end[new_offs].x =
	(BX->g_scale2 * BX->dir.end.x) + BX->p_end[curr_offs].x;

      BX->p_end[new_offs].y =
	(BX->g_scale2 * BX->dir.end.y) + BX->p_end[curr_offs].y;
    }
  /*
   * get and save current E-clock value
   */
  e_freq = ReadEClock (&e_then);

}

/*
 *******************************************************************
 */

void
MyBlankerLibFree (void)

{
  //
  // debug
  //
  DPUTSTR ("MyBlankerLibFree\n");

  CloseLibrary (UtilityBase);
  CloseLibrary (&IntuitionBase->LibNode);
  CloseLibrary (&GfxBase->LibNode);

  close_timer ();

}

/*
 *******************************************************************
 */

LONG
MyBlankerLibInit (void)

{
//
  // debug
  //
  DPUTSTR ("MyBlankerLibInit\n");

  if (open_timer () == FALSE)
    {
      return FALSE;
    }

  if (GfxBase = (GFXBASE *) OpenLibrary ("graphics.library", 37L))
    {
      if (IntuitionBase = (INTUITIONBASE *) OpenLibrary ("intuition.library", 37L))
	{
	  if (UtilityBase = OpenLibrary ("utility.library", 37L))
	    return TRUE;
	  CloseLibrary (&IntuitionBase->LibNode);
	}
      CloseLibrary (&GfxBase->LibNode);
    }
  return FALSE;
}

/*
 *********************************************************************
 */

static void
do_box (BOXES * BX, XY * start, XY * end, XY * limit)

{
  RASTPORT *rp = BX->BTDDrawInfo->BDI_RPort;

  int tempx_s = limit->x - start->x;
  int tempy_s = limit->y - start->y;
  int tempx_e = limit->x - end->x;
  int tempy_e = limit->y - end->y;

  /*
   * DEBUG-start
   */


  if (start->x > gBDI_Width)
    {
      dbg (BX, "X1\n");
      return;
    }
  if (start->y > gBDI_Height)
    {
      dbg (BX, "Y1\n");
      return;
    }
  if (end->x > gBDI_Width)
    {
      dbg (BX, "X2\n");
      return;
    }
  if (end->y > gBDI_Height)
    {
      dbg (BX, "Y1\n");
      return;
    }


  /*
   * check x coords
   */
  if ((tempx_s < 0) || (tempx_s > gBDI_Width))
    {
      dbg (BX, "ST1\n");
      return;
    }
  if ((tempx_e < 0) || (tempx_e > gBDI_Width))
    {
      dbg (BX, "ST2\n");
      return;
    }
  /*
   * check y coords
   */
  if ((tempx_e < 0) || (tempx_e > gBDI_Height))
    {
      dbg (BX, "ET1\n");
      return;
    }
  if ((tempy_e < 0) || (tempy_e > gBDI_Height))
    {
      dbg (BX, "ET2\n");
      return;
    }



  /*
   * DEBUG-end
   */

  Move (rp, start->x, start->y);
  Draw (rp, end->x, end->y);

  Draw (rp, tempx_s, tempy_s);

  Draw (rp, tempx_e, tempy_e);

  Draw (rp, start->x, start->y);

}

/*
 *********************************************************************
 */

static BOOL
checkbounce_start (XY * start, XY * limmax, XY * limmin)
{

  if ((start->x >= limmax->x) || (start->y >= limmax->y)
      || (start->x <= limmin->x) || (start->y <= limmin->y))
    return TRUE;
  else
    return FALSE;
}

/*
 *********************************************************************
 */

static BOOL
checkbounce_end (XY * end, XY * limmax, XY * limmin)
{

  if ((end->x >= limmax->x) || (end->y >= limmax->y)
      || (end->x <= limmin->x) || (end->y <= limmin->y))
    return TRUE;
  else
    return FALSE;
}

/*
 *********************************************************************
 */

static int
get_rand_point (int min, int max)
{
  SHORT temp;

  temp = my_ran (max - min);

  return (temp + min);
}

/*
 *********************************************************************
 */

static int
get_rand_dir ()
{
  SHORT num;

  num = my_ran (15000);

  return (num < 5000) ? -1 : (num > 10000) ? 1 : 0;
}

/*
 *********************************************************************
 */

static int
get_rand_scale ()
{
  SHORT temp;

  temp = my_ran (MINBOUND);

  if (temp == 0)
    ++temp;

  return temp;
}
/*
 *********************************************************************
 */

static USHORT
my_ran (int lim)
{
  USHORT retval = RangeRand (lim);

  if (retval == 0)
    return 1;
  else
    return retval;

}

/*
 *********************************************************************
 */


/*
 * returns number of msecs between first ECLOCK and second ECLOCK 
 * - second ECLOCK is later.
 *
 * - not precise, but a hack ; divides (shifts) by 1024 not 1000. 
 *
 */

static LONG
time_diff (ULONG e_freq, ECLOCKVAL * e_val1, ECLOCKVAL * e_val2)

{
  LONG temp, msecs;

  if (e_val2->ev_lo > e_val1->ev_lo)
    {
      temp = e_val2->ev_lo - e_val1->ev_lo;
    }
  else
    {
      temp = e_val1->ev_lo - e_val2->ev_lo;
    }

  /*
   * convert to msecs
   */

  e_freq = e_freq >> 10;	/* roughly ,  /1024 */

  msecs = temp / e_freq;

  return msecs;
}
/*
 *********************************************************************
 */

static BOOL
open_timer (void)
{

  if (OpenDevice (TIMERNAME, UNIT_ECLOCK, (IOREQUEST *) t_ptr, 0L) != 0)
    {
      return FALSE;
    }

  TimerBase = (TIMERBASE *) t_ptr->tr_node.io_Device;

  return TRUE;
}
/*
 *********************************************************************
 */

static void
close_timer (void)
{
  if (t_ptr)
    {
      CloseDevice ((IOREQUEST *) t_ptr);
      t_ptr = NULL;
    }
}
/*
 *********************************************************************
 */
static void
DrawBounds (RASTPORT * rp, XY * tlhc, XY * brhc)
{

  Move (rp, tlhc->x, tlhc->y);

  Draw (rp, brhc->x - 1, tlhc->y);
  Draw (rp, brhc->x - 1, brhc->y - 1);
  Draw (rp, tlhc->x, brhc->y - 1);
  Draw (rp, tlhc->x, tlhc->y);
}
/*
 *********************************************************************
 */
static void
init_array (BOXES * BX)
{
  int j;

  /*
   * Init array with inactive values 
   */
  for (j = 0; j < MAX_BOXES; ++j)
    {
      BX->p_stt[j].x = -1;
      BX->p_stt[j].y = -1;
    }
  /*
   * Init starting points with random x,y values
   */

  BX->xoffs = 0;
  BX->p_stt[0].x = get_rand_point (BX->tlhc.x, BX->brhc.x);
  BX->p_stt[0].y = get_rand_point (BX->tlhc.y, BX->brhc.y);

  BX->p_end[0].x = get_rand_point (BX->tlhc.x, BX->brhc.x);
  BX->p_end[0].y = get_rand_point (BX->tlhc.y, BX->brhc.y);

  BX->dir.start.x = get_rand_dir ();
  BX->dir.start.y = get_rand_dir ();

  BX->dir.end.x = get_rand_dir ();
  BX->dir.end.y = get_rand_dir ();

  BX->g_scale1 = 2;
  BX->g_scale2 = 2;

  BX->col_count = my_ran (MY_SIX - 1);
}
/*
 *********************************************************************
 */

void
dbg (BOXES * BX, char *ptr)
{

  /* DEBUG CODE COMES HERE :) */
}
