/*
 * CaBoom, 1995 Lee Kindness.
 *
 * Patches:
 *  intuition.library OpenWindow()
 *  intuition.library OpenWindowTagList()
 *  intuition.library CloseWindow()
 *
 * When windows open or close a small 'explosion' is done.
 *
 * This source is in the public domain, do with it as you wish...
 *
 * version 1.0
 *
 ***************************************************************************/

#include "gst.c"


/********************************* download dev/c/SFPatch.lha for documentation */

#include "SFPatch.h"


/********************************* DONT auto open... */

extern struct IntuitionBase *IntuitionBase = NULL;
extern struct Library *CxBase = NULL;
extern struct Library *IconBase = NULL;
extern struct Library *UtilityBase = NULL;
extern struct GfxBase *GfxBase = NULL;
extern struct Library *LayersBase = NULL;

/* Save a bit of typing */
#define REG(x) register __ ## x


/********************************* The function offsets */

#define OW_OFFSET -204
#define OWTL_OFFSET -606
#define CW_OFFSET -72 


/********************************* Default prefs */

#define DEF_SPEED 10000
#define DEF_LINE 0xFFFF
#define DEF_NSTEPS 21
#define MAXSTEPS 21


/********************************* types */

typedef struct Window * __asm (*OW_Caller)( REG(a0) struct NewWindow *, 
                                            REG(a6) struct Library *);
typedef struct Window * __asm (*OWTL_Caller)( REG(a0) struct NewWindow *,
                                              REG(a1) struct TagItem *,
                                              REG(a6) struct Library *);
typedef void __asm (*CW_Caller)( REG(a0) struct Window *,
                                 REG(a6) struct Library *);
struct Prefs {
	LONG Speed, Line, nSteps, Pri;
  struct {
    struct {
      int x, y;
    } topleft, botright;
  } step[MAXSTEPS];
};


/********************************* Prototypes */

struct Window * __asm OW_New( REG(a0) struct NewWindow *, 
                              REG(a6) struct Library *);
struct Window * __asm OWTL_New( REG(a0) struct NewWindow *,
                                REG(a1) struct TagItem *,
                                REG(a6) struct Library *);
void __asm CW_New( REG(a0) struct Window *,
                   REG(a6) struct Library *);

BOOL OpenLibs(void);
void CloseLibs(void);
BOOL ShowWindow(void);
void line (struct RastPort *rp, LONG x1, LONG y1, LONG x2, LONG y2);
void DrawOutline(struct RastPort *rp, struct Screen *screen, LONG xi, LONG yi, LONG xf, LONG yf);
void MoveOutline(struct Screen *screen, LONG x, LONG y, LONG w, LONG h, BOOL reverse);


/********************************* Global vars */

SetFunc *OW_SetFunc, *OWTL_SetFunc, *CW_SetFunc;
struct Remember *grk;
BOOL Active;
char vertag[] = "$VER: CaBoom 1.0 "__AMIGADATE__;
struct Prefs prefs = {
 DEF_SPEED, DEF_LINE, DEF_NSTEPS, -1,
 {
  {    0,    0,    0,    0},	/* left, top, right, bottom */
  {  135,  -22,  135,  -22 },
  {  265,  -30,  265,  -30 },
  {  389,  -25,  389,  -25 },
  {  506,   -6,  506,   -6 },
  {  615,   23,  615,   23 },
  {  717,   62,  717,   62 },
  {  811,  111,  811,  111 },
  {  895,  168,  895,  168 },
  {  971,  231,  971,  231 },
  { 1036,  300, 1036,  300 },
  { 1090,  373, 1090,  373 },
  { 1134,  449, 1134,  449 },
  { 1166,  527, 1166,  527 },
  { 1186,  605, 1186,  605 },
  { 1194,  683, 1194,  683 },
  { 1188,  759, 1188,  759 },
  { 1169,  833, 1169,  833 },
  { 1135,  902, 1135,  902 },
  { 1087,  966, 1087,  966 },
  { 1024, 1024, 1024, 1024 }	/* 21st entry */
 }
};
/*
 *  The points above were generated using an HP-28S and a bezier curve
 * plotting program. The curve was broken in 20 line segments and the
 * control points were: (0, 0) ; (922, -205) ; (1587, 665) ; (1024, 1024)
 */


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

/* main */
int main(int argc, char **argv)
{
	int ret;		
	ret = RETURN_OK;
	Active = TRUE;
	grk = NULL;
	
	/* check version */
	if (OpenLibs()) {
		struct NewBroker nb = {
			NB_VERSION,
			"CaBoom",
			&vertag[6],
			"Window explosions",
			NBU_UNIQUE | NBU_NOTIFY,
			COF_SHOW_HIDE,
			-1,
			NULL,
			0
		};
		CxObj *broker;
		
		/* Get tooltypes */
		if (argc ? FALSE : TRUE) {
			BPTR oldcd;
			struct DiskObject *dobj;
			struct WBStartup *wbs;
			#define PROGNAME wbs->sm_ArgList->wa_Name
			#define PDIRLOCK wbs->sm_ArgList->wa_Lock
			wbs = (struct WBStartup *)argv;
			/* Run from WB */
			oldcd = CurrentDir(PDIRLOCK);
			if (dobj = GetDiskObject(PROGNAME)) {
				STRPTR s;
				if (s = FindToolType(dobj->do_ToolTypes, "SPEED")) {
					stcd_l(s, &prefs.Speed);
				}
				if (s = FindToolType(dobj->do_ToolTypes, "LINE")) {
					stcd_l(s, &prefs.Line);
				}
				if (s = FindToolType(dobj->do_ToolTypes, "CX_PRIORITY")) {
					stcd_l(s, &prefs.Pri);
				}
				FreeDiskObject(dobj);
			}
			CurrentDir(oldcd);
		} else {
			struct RDArgs *rdargs;
			#define OPT_SPEED 0
			#define OPT_LINE 1
			#define OPT_CXP 2
			LONG args[3] = {0, 0, 0};
			#define TEMPLATE "SPEED/K/N,LINE/K/N,CX_PRIORITY/K/N"
			/* Run from Shell */
			if (rdargs = ReadArgs(TEMPLATE, (LONG *)&args, NULL)) {
				if (args[OPT_SPEED]) {
					prefs.Speed = *((LONG *)args[OPT_SPEED]);
				}
				if (args[OPT_LINE]) {
					prefs.Line = *((LONG *)args[OPT_LINE]);
				}
				if (args[OPT_CXP]) {
					prefs.Pri = *((LONG *)args[OPT_CXP]);
				}
				FreeArgs(rdargs);	
			}
		}
		
		if (prefs.Speed < 0)
			prefs.Speed = 0;
	
		nb.nb_Pri = prefs.Pri;
				
		if ((nb.nb_Port = CreateMsgPort()) && (broker = CxBroker(&nb, NULL))) {
			
			/* Alloc our SetFunc's */
			if((OW_SetFunc = AllocVec(sizeof(SetFunc), MEMF_CLEAR)) &&
			   (OWTL_SetFunc = AllocVec(sizeof(SetFunc), MEMF_CLEAR)) &&
			   (CW_SetFunc = AllocVec(sizeof(SetFunc), MEMF_CLEAR))) {

				/* init. sfs */
				OW_SetFunc->sf_Func = OW_New;
				OW_SetFunc->sf_Library = (struct Library *)IntuitionBase;
				OW_SetFunc->sf_Offset = OW_OFFSET;
				OW_SetFunc->sf_QuitMethod = SFQ_COUNT;
				OWTL_SetFunc->sf_Func = OWTL_New;
				OWTL_SetFunc->sf_Library = (struct Library *)IntuitionBase;
				OWTL_SetFunc->sf_Offset = OWTL_OFFSET;
				OWTL_SetFunc->sf_QuitMethod = SFQ_COUNT;
				CW_SetFunc->sf_Func = CW_New;
				CW_SetFunc->sf_Library = (struct Library *)IntuitionBase;
				CW_SetFunc->sf_Offset = CW_OFFSET;
				CW_SetFunc->sf_QuitMethod = SFQ_COUNT;

				/* Replace the functions */
				if ((SFReplace(OW_SetFunc)) &&
				    (SFReplace(OWTL_SetFunc)) &&
				    (SFReplace(CW_SetFunc))) {
					
					ULONG sig, sret;
					BOOL finished;
					
					ActivateCxObj(broker, 1L);

					finished = FALSE;
					sig = 1 << nb.nb_Port->mp_SigBit;
					
					do {
						sret = Wait(SIGBREAKF_CTRL_C | sig);
						if (sret & sig) {
							CxMsg *msg;
							while(msg = (CxMsg *)GetMsg(nb.nb_Port)) {
								switch(CxMsgType(msg)) {
									case CXM_COMMAND:
										switch(CxMsgID(msg)) {
											case CXCMD_DISABLE:
												ActivateCxObj(broker, 0L);
												Active = FALSE;
												break;
											case CXCMD_ENABLE:
												ActivateCxObj(broker, 1L);
												Active = TRUE;
												break;
											case CXCMD_KILL:
												finished = TRUE;
												break;
											case CXCMD_UNIQUE:
												finished = ShowWindow();
												break;
											case CXCMD_APPEAR:
												finished = ShowWindow();
												break;
										}
										break;
								}
								ReplyMsg((struct Message *)msg);
							}
						}
						if (sret & SIGBREAKF_CTRL_C)
							finished = TRUE;
					} while (!finished);
					ActivateCxObj(broker, 0L);
	
					/* Restore functions */
					SFRestore(CW_SetFunc);
					SFRestore(OWTL_SetFunc);
					SFRestore(OW_SetFunc);
				}
				FreeVec(CW_SetFunc);
				FreeVec(OWTL_SetFunc);
				FreeVec(OW_SetFunc);
			}
			DeleteCxObj(broker);
			DeletePort(nb.nb_Port);
		}
	}
	CloseLibs();
	return(ret);
}


/***************************************************************************/
/* Show our window... currently only a requester */
BOOL ShowWindow(void)
{
	struct EasyStruct ez = {
		sizeof(struct EasyStruct),
		0,
		"CaBoom",
		"%s ŠLee Kindness.\n\n"
		"Window explosions\n\n"
		"Read \"CaBoom.guide\" for more information\n\n"
		"(Program may take a couple of seconds to quit)",
		"Quit|Hide"
	};
	return((BOOL)EasyRequest(NULL, &ez, NULL, &vertag[6]));
}


/***************************************************************************/
/* Open all used libraries */
BOOL OpenLibs(void)
{
	BOOL ret;
	IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 37);
	CxBase = OpenLibrary("commodities.library", 37);
	IconBase = OpenLibrary("icon.library", 37);
	UtilityBase = OpenLibrary("utility.library", 37);
	GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 37);
	LayersBase = OpenLibrary("layers.library", 37);
	ret = ((IntuitionBase) && 
	       (CxBase) && 
	       (IconBase) &&
	       (UtilityBase) &&
	       (GfxBase) &&
	       (LayersBase));
	return(ret);
}


/***************************************************************************/
/* Close all libraries */
void CloseLibs(void)
{
	if (LayersBase)
		CloseLibrary(LayersBase);
	if (GfxBase)
		CloseLibrary((struct Library *)GfxBase);
	if (UtilityBase)
		CloseLibrary(UtilityBase);
	if (IconBase)
		CloseLibrary(IconBase);
	if (CxBase)
		CloseLibrary(CxBase);
	if (IntuitionBase)
		CloseLibrary((struct Library *)IntuitionBase);
}


/***************************************************************************/
/* Draws a line */
void line (struct RastPort *rp, LONG x1, LONG y1, LONG x2, LONG y2)
{
	Move(rp, x1, y1);
	Draw(rp, x2, y2);
}


/***************************************************************************/
/* Draws a box-outline */

#define MAX(a,b) (((a)>(b))?(a):(b))
#define MIN(a,b) (((a)<(b))?(a):(b))
#define xclip(x) (MAX(0, MIN(x, screen->Width - 1)))
#define yclip(y) (MAX(0, MIN(y, screen->Height - 1)))

void DrawOutline(struct RastPort *rp, struct Screen *screen, LONG xi, LONG yi, LONG xf, LONG yf)
{
	xi = xclip(xi);
	yi = yclip(yi);
	xf = xclip(xf);
	yf = yclip(yf);
	line(rp, xi, yi, xf, yi);
	line(rp, xf, yi, xf, yf);
	line(rp, xf, yf, xi, yf);
	line(rp, xi, yf, xi, yi);
}


/***************************************************************************/
/* The 'anim' */
void MoveOutline(struct Screen *screen, LONG x, LONG y, LONG w, LONG h, BOOL reverse)
{
	struct RastPort *rp;
	struct timerequest *tr;
	struct MsgPort *tmp;
	
	if ((rp = AllocVec(sizeof(struct RastPort), MEMF_CLEAR)) &&
	    (tmp = CreateMsgPort()) &&
	    (tr = (struct timerequest *)CreateIORequest(tmp, sizeof(struct timerequest))))
	{
		LONG timerres;
		register LONG i, n;
		LONG dxi, dyi, dxf, dyf;
		LONG nxi, nyi, nxf, nyf;
		LONG  xi,  yi,  xf,  yf;
		LONG x0, y0;
		
		timerres = OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest *)tr, 0);

		xi = 0;
		yi = 0;
		xf = 0;
		yf = 0;
	
		*rp = screen->RastPort;
	
		SetDrMd(rp, COMPLEMENT | JAM1);
  	SetDrPt(rp, prefs.Line);
  	SetAPen(rp, 1);
	
		x0 = screen->MouseX;   	/* assure that coordinates are */
  	y0 = screen->MouseY;   	/* inside this screen. */

		dxi = x - x0;
		dyi = y - y0;
		dxf = (x + w - 1) - x0;
		dyf = (y + h - 1) - y0;
	
		LockLayers (&screen->LayerInfo);

		for (i = 0; i < prefs.nSteps; i++)
		{
			if (reverse)
				n = prefs.nSteps - i;
			else
				n = i;
		
			nxi = x0 + ((prefs.step[n].topleft.x * dxi) / 1024);
			nyi = y0 + ((prefs.step[n].topleft.y * dyi) / 1024);
			nxf = x0 + ((prefs.step[n].botright.x * dxf) / 1024);
			nyf = y0 + ((prefs.step[n].botright.y * dyf) / 1024);
			if (n)
				DrawOutline(rp, screen, xi, yi, xf, yf);	/* erases outline */
			xi = nxi;
			yi = nyi;
			xf = nxf;
			yf = nyf;
			DrawOutline(rp, screen, xi, yi, xf, yf);	/* draws outline */
			
			if ((prefs.Speed) && (timerres == 0))
			{
				tr->tr_node.io_Command = TR_ADDREQUEST;
				tr->tr_node.io_Flags = 0;
				tr->tr_time.tv_secs = 0;
				tr->tr_time.tv_micro = prefs.Speed;
				DoIO((struct IORequest *)tr);
			}
			
			
			
		}
		DrawOutline(rp, screen, xi, yi, xf, yf);
		if (timerres == 0)
			CloseDevice((struct IORequest *)tr);
		DeleteIORequest((struct IORequest *)tr);
		DeleteMsgPort(tmp);
		FreeVec(rp);
	}
	UnlockLayers(&screen->LayerInfo);
}


/***************************************************************************/
/* The new OpenWindow() */
struct Window * __saveds __asm OW_New(REG(a0) struct NewWindow *nwin, 
                                      REG(a6) struct Library *lib)
{
	struct Window *ret;
	OW_Caller Caller;
	BOOL unlockscr;
	struct Screen *screen;
	
	/* increment count */
	Forbid();
	OW_SetFunc->sf_Count += 1;
	Permit();
	
	if (Active)
	{
		/* Get Screen that window will open on */
		unlockscr = FALSE;
		if ((nwin->Type != WBENCHSCREEN))
			screen = nwin->Screen;
		else {
			screen = LockPubScreen(NULL);
			unlockscr = TRUE;
		}
	
		if (screen)
		{
			LONG x, y, w, h;
		
			/* Calculate dimensions and positioning */
			x = nwin->LeftEdge;
			y = nwin->TopEdge;
			w = nwin->Width;
			h = nwin->Height;
		
			MoveOutline(screen, x, y, w, h, FALSE);
		
			if (unlockscr)
				UnlockPubScreen(NULL, screen);	
		}
	}
	Caller = (APTR)OW_SetFunc->sf_OriginalFunc;
	
	/* Pass the buck */
	ret = Caller(nwin, lib);
	
	/* decrement count */
	Forbid();
	OW_SetFunc->sf_Count -= 1;
	Permit();
	
	/* and return */
	return(ret);

}


/***************************************************************************/
/* The new OpenWindowTagList() */
struct Window * __saveds __asm OWTL_New(REG(a0) struct NewWindow *nwin, 
                                        REG(a1) struct TagItem *tags,
                                        REG(a6) struct Library *lib)
{
	struct Window *ret;
	OWTL_Caller Caller;
	struct TagItem *tag;
	BOOL unlockscr;
	struct Screen *screen;
	
	/* increment count */
	Forbid();
	OWTL_SetFunc->sf_Count += 1;
	Permit();
	
	if (Active)
	{
		/* Get Screen that window will open on */
		screen = NULL;
		unlockscr = FALSE;
		if (nwin)
		{
			if ((nwin->Type != WBENCHSCREEN))
				screen = nwin->Screen;
		}
		if (tag = FindTagItem(WA_CustomScreen, tags))
		{
			screen = (struct Screen *)tag->ti_Data;
		} else
		{
			if (tag = FindTagItem(WA_PubScreen, tags))
			{
				screen = (struct Screen *)tag->ti_Data;
			} else
			{
				if (tag = FindTagItem(WA_PubScreenName, tags))
				{
					screen = LockPubScreen((STRPTR)(tag->ti_Data));
					unlockscr = TRUE;
				}
			}
		}
		if (screen == NULL)
		{
			screen = LockPubScreen(NULL);
			unlockscr = TRUE;
		}
	
		if (screen)
		{
			LONG x, y, w, h;
		
			/* Calculate dimensions and positioning */
			if (nwin)
			{
				x = nwin->LeftEdge;
				y = nwin->TopEdge;
				w = nwin->Width;
				h = nwin->Height;
			} else
			{
				x = 0;
				y = 0;
				w = screen->Width;
				h = screen->Height;
			}
			if (tag = FindTagItem(WA_Left, tags))
				x = tag->ti_Data;
			if (tag = FindTagItem(WA_Top, tags))
				y = tag->ti_Data;
			if (tag = FindTagItem(WA_InnerWidth, tags))
				w = tag->ti_Data + 8;
			if (tag = FindTagItem(WA_Width, tags))
				w = tag->ti_Data;
			if (tag = FindTagItem(WA_InnerHeight, tags))
				h = tag->ti_Data + 8;
			if (tag = FindTagItem(WA_Height, tags))
				h = tag->ti_Data;
	
			MoveOutline(screen, x, y, w, h, FALSE);
		
			if (unlockscr)
				UnlockPubScreen(NULL, screen);	
		}
	}
	Caller = (APTR)OWTL_SetFunc->sf_OriginalFunc;
	
	/* Pass the buck */
	ret = Caller(nwin, tags, lib);
	
	/* decrement count */
	Forbid();
	OWTL_SetFunc->sf_Count -= 1;
	Permit();
	
	/* and return */
	return(ret);
}


/***************************************************************************/
/* The new CloseWindow() */
void __saveds __asm CW_New(REG(a0) struct Window *win, 
                           REG(a6) struct Library *lib)
{
	CW_Caller Caller;
	struct Screen *screen;
	LONG x, y, w, h;
	
	/* increment count */
	Forbid();
	CW_SetFunc->sf_Count += 1;
	Permit();
	
	if (win)
	{
		screen = win->WScreen;
		x = win->LeftEdge;
		y = win->TopEdge;
		w = win->Width;
		h = win->Height;
	}

	/* Pass the buck */
	Caller = (APTR)CW_SetFunc->sf_OriginalFunc;
	Caller(win, lib);
	
	if ((screen) && (Active))
		MoveOutline(screen, x, y, w, h, TRUE);

	
	/* decrement count */
	Forbid();
	CW_SetFunc->sf_Count -= 1;
	Permit();
}
/***************************************************************************/
