/* 
   Skeleton Library for BTD 
   Comments by Markus Illenseer 
  
   This source tries to give valuable hints to write a Modul
   for BTD. As you will see, this is quite easy, even if it is
   the first time you write a library.

   This Skeleton Blanker draws random dots in 4 colors for a certain time,
   then cleans up the screen and starts again. Nothing sophisticated.

   Also do refer to BTD.h for global defines and structures.

*/

#include <exec/memory.h>
#include <exec/execbase.h>
#include <libraries/iffparse.h>
#include <utility/tagitem.h>

#include <clib/macros.h>

#define __USE_SYSBASE 42

#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>

#include <BTD.h>

struct ExecBase *SysBase;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

/* 
   Don't open any libraries by yourself. This _must_ be done
   in the assembler part of the library. 

   Be sure your library is re-entrant. Means, one must be able to
   open the library several times at once. So don't use global 
   variables other than static (unchangable) ones. Use ModulStruct
   for that purpose.
*/

/* #define DEBUG YES */

/*
   Remove above comment braces (uncomment) to use the Debug-Output.
   Use Terminal connected to serial line, or use sushi to
   redirect the output. Be carefull with modems!
   Remember to add the Debug.lib in the Makefile.

   DEBUG_PRINT prints only text.
   DEBUG_PRINTF prints any text and one (!) variable of any kind

   This is the only easy way to debug libaries! The SDB or other
   debuggers have some problems with libraires, at least as source
   level debuggers.

*/

#ifdef DEBUG 

void KPrintF(char *,...);

#define DEBUG_PRINTF(a,b)  KPrintF(a,b);
#define DEBUG_PRINT(a)     KPrintF(a)
#else
#define DEBUG_PRINTF(a,b)
#define DEBUG_PRINT(a)
#endif

/* Now we define the Macros for easy access to the Tag-Entries */

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

#define ST_Seconds DTAG(0)

/* How much time we give, until the screen is cleaned up again */

#define DEF_SECONDS 5L
#define MAX_SECONDS 60L

/*
   This structure defines the INTEGER parameters, which the user
   can supply.  Skeleton requires only the seconds until it will
   clean up the screen.  For other type of paramters, refer to BTD.h 
*/

struct BTDInteger SkeletonIntParams[] =
 {
  ST_Seconds,"Seconds",BTDPT_INTEGER,DEF_SECONDS,1L,MAX_SECONDS,TRUE,
 };

/*
   This Taglist is send to the Preference-Programm which will
   open a window with the hereby supplied gadgets.  Skeleton needs
   only the one Integer-Slider 
*/

struct BTDNode *SkeletonParams[] = 
 {
  &SkeletonIntParams[0].BI_Node,
  NULL
 };

/*
   This structure sends some info about the modul to the Preference-
   Programm.  MAKE_ID is required for MUI, and will make it possible
   to save the position of this specific preference window.
   First string defines the NAME of the blanker, possibly something
   different than the probaly cryptic filename of the library.
   Second string contains a short description about the blanker.  A \n will
   split up the string into a new line.
   Third string should contain your Name and a Copyright message if
   necessary.

   The Version Number is not defined here, but in the Assembler-Part
   of the source!

   Last Tag is the Node-Info, which will request the user for parameters
   with above given List in BTDNode.
*/

struct BTDInfo SkeletonInfo =
 {
  BTDI_Revision,MAKE_ID('S','K','E','L'),
  "Skeleton Blanker","Skeleton is intended for programmers\n among you peoples!\nJust a simple and boring blanker modul","Markus Illenseer 1994",
  SkeletonParams
 };


/*
   This is our 'global' structure for the skeleton blanker. I refer to
   it as ModulStruct.
   Remember that this is the only way to hold global data for each 
   invoked modul out of this library. The Skeleton Blanker will only 
   hold info about the supplied second-parameter and about the time when 
   the screen was cleaned up the last time. And of course it will hold 
   the BTDDrawInfo, which contains lot of required info, eg. RastPort.

   If you require anything sophisticated like ImageData, have a look into
   the other supplied modules. 
*/

struct SkeletonStruct
 {
  struct BTDDrawInfo *BTDDrawInfo;
  LONG Seconds;
  LONG Time;
  LONG RandN,RandF,RandI;
 };

/*
   The next two functions are some sophisticated Randomnizers
   which are based upon (mathematic) statistic defined true 
   Randomnizers. All they need is a seed, afterwards they will use
   the current time as a base for future randoms. 

   Normally we would have some global variabled for RandN, RandF and
   RandI, but as we need to be re-entrant, we need to store this info
   in the ModulStruct above. 
*/

void __regargs InitRandom(struct SkeletonStruct *Skel,ULONG Instance)

{
 ULONG Time[2];

 CurrentTime (&Time[0],&Time[1]);
 Skel->RandN=(LONG)Time[0];

 if (Time[1]<1024L) Time[1]|=1;
 else Time[1]>>=10;
 Time[1]^=Instance;

 Skel->RandF=4*Time[1]+1;
 Skel->RandI=2*Time[1]+1;
}

WORD __regargs Random(struct SkeletonStruct *Skel,WORD Max)

{
 Skel->RandN=Skel->RandF*Skel->RandN+Skel->RandI;
 if (Skel->RandN<0L) Skel->RandN=-Skel->RandN;

 return (WORD)(Skel->RandN%Max);
}


/* 
   This function is actually a copy of the FindTag-function of clib/utility,
   but this one is a bit faster and also returns a supplied Default
   when Tag-ID wasn't found 
*/

ULONG FindTagData(struct TagItem *TagList,Tag ID,ULONG Default)

{
 while (TagList)
  switch (TagList->ti_Tag)
   {
    case TAG_DONE:
     return Default;
    case TAG_MORE:
     TagList=(struct TagItem *)TagList->ti_Data;
     break;
    default:
     if (TagList->ti_Tag==ID) return TagList->ti_Data;
     else TagList++;
   }
 return Default;
}

/* 
   This is the first procedure every modul requires. It will initialize
   the modul, eat required memory, set colors, whatever. This function
   should be as short as possible. No heavy calculations belong here 

   struct ModulStruct InitModul(struct TagItem *TagList)

   Input: TagList is supplied from BTD-CX, the server, and will send
          you all the parameters you requested from the user in above
          structure BTDInfo. 

   Output: Fully initialized ModulStruct for further use, is returned
           to the server, who will then invoke AnimModul() with this
           structure. 
*/

struct SkeletonStruct *InitSkeleton(struct TagItem *TagList)

{
 struct SkeletonStruct *SkelStruct;
 struct BTDDrawInfo *BTDDrawInfo;
 ULONG *Error,Dummy,Instance,Index;

DEBUG_PRINT("Skeleton: Initialize Modul\n");

 /* Find the Entry for the BTDDrawInfo (see BTD.h) and make local copy */

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

/* 
   Get us the Dummy, which will contain Error-Messages if we fail to
   initialize our Modul. The server then knows we failed and will display
   a black part on the screen 
*/

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

/*
   Allocate the required MEM for the SkeletonStruct, on error report
   a Memory-error to the server and return home 
*/

 if ((SkelStruct=AllocVec(sizeof(struct SkeletonStruct),MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   *Error=BTDERR_Memory;
   return NULL;
  }

/*
   copy local BTDDrawInfo into global structure for future use in 
   AnimModul() 
*/

 SkelStruct->BTDDrawInfo=BTDDrawInfo;

/*
  The Instance is sort of OpenCounter and is often used to initialize
  a Randomnizer, so that same modules won't use same random-seed 
*/

 Instance=FindTagData(TagList,BTD_Instance,0L);

 InitRandom(SkelStruct,Instance);

/* Get us the user-supplied parameter SECONDS */

 SkelStruct->Seconds=FindTagData(TagList,ST_Seconds,DEF_SECONDS);


/* 
  Set an initial value, not really required to do so, as we allocated
  the SkelStruct with MEMF_CLEAR flag 
*/

 SkelStruct->Time=0L;


/* 
  Now set our requested colors to any RGB-Tripel, you MUST use
  this array, otherwise you won't get your colors, but anything
  else. Can yield to a Guru if you do that! 
*/

 for(Index=0; Index<4; Index++)
  {
   BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[Index]]=Random(SkelStruct,255);
   BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[Index]]=Random(SkelStruct,255);
   BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[Index]]=Random(SkelStruct,255);
   BTDDrawInfo->BDI_Changed[BTDDrawInfo->BDI_Pens[Index]]=TRUE;
  }

/* Everything's done. Return the structure for future use */

 return SkelStruct;
}

/* This is the second procedure required for every modul. It should
   do anything required to free resources, free mem and so on. 

   void EndSkeleton(struct ModulStruct *ModulStruct)

   Input: ModulStruct supplied from server BTD-CX
   Output: None. Should all be freed up */

void EndSkeleton(struct SkeletonStruct *SkelStruct)

{
DEBUG_PRINT("Skeleton: about to finish Modul\n");
 FreeVec (SkelStruct);
}

/* Third procedure required for a modul. This one is called by the
   server every once in a second. You are not allowed to do anything
   which would yield into a FOREVER-Loop or do anything like a busy-loop
   or something unstoppable. 

   If you need to do a loop from TOP to BOTTOM (TOP+HEIGHT), add a counter
   in ModulStruct: ypos. Best would even to add xpos also, if required.
   
   These restictions are all due to the fact, that the user want's his
   screen back as fast as possible!

   void AnimSkeleton(struct ModulStruct *ModulStruct)

   Input: ModulStruct supplied from server BTD-CX.
   OutPut: None.

*/

void AnimSkeleton(struct SkeletonStruct *SkelStruct)

{
 ULONG Seconds,Micros;

 /* Hum, below DEBUG_PRINT not that usefull. Remove it, or replace
    it with anything you need to check */

DEBUG_PRINT("Skeleton: Animate\n");

/* Get us the current time. */

 CurrentTime (&Seconds,&Micros);

 if (Seconds>=SkelStruct->Time)
  {

/* Wait for that the pointer is at the top of the screen */
   WaitTOF();

/* Calculate next timeslide */

   SkelStruct->Time=Seconds+SkelStruct->Seconds;
   
/* Set Background color, attention, have a look at the color! */

   SetAPen(SkelStruct->BTDDrawInfo->BDI_RPort,BTD_BgPen);

/* Fill our part of the screen with background color */

   RectFill (SkelStruct->BTDDrawInfo->BDI_RPort,
             SkelStruct->BTDDrawInfo->BDI_Left,
             SkelStruct->BTDDrawInfo->BDI_Top,
             SkelStruct->BTDDrawInfo->BDI_Left+SkelStruct->BTDDrawInfo->BDI_Width-1,
             SkelStruct->BTDDrawInfo->BDI_Top+SkelStruct->BTDDrawInfo->BDI_Height-1);
  }

/* Now let's do the magic Animation of Skeleton */

/* Choose Random Color and set Pen accordingly */

  SetAPen (SkelStruct->BTDDrawInfo->BDI_RPort,
           SkelStruct->BTDDrawInfo->BDI_Pens[Random(SkelStruct,4)]);

/* 
  You can speed up the process, if you store LEFT, TOP, HEIGHT. WIDTH
  in ModulStruct directly, rather than in a pointer to BTDDrawInfo 
*/

  WritePixel (SkelStruct->BTDDrawInfo->BDI_RPort,
              SkelStruct->BTDDrawInfo->BDI_Left+Random(SkelStruct,SkelStruct->BTDDrawInfo->BDI_Width),
              SkelStruct->BTDDrawInfo->BDI_Top+Random(SkelStruct,SkelStruct->BTDDrawInfo->BDI_Height));
}

ULONG PenCountSkeleton(struct TagItem *TagList)

{
 
/*
   We request to use 4 colors, we _will_ get them! 
   We would even get 324 colors, if there is a screenmode with that
   many colors, and the servers gives us that many. 
   If the servers sees that we need more colors as he can provide with
   his used screenmode, he won't use our modul 
*/

 return 4L;
}
