/*
 * Copyright (c) 1993 Michael D. Bayne.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *    following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * 3. All advertising materials mentioning features or use of this software must display the following
 *    acknowledgement:
 *
 *       This product includes software developed by Michael D. Bayne.
 *
 * 4. My name may not be used to endorse or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY MICHAEL D. BAYNE ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL MICHAEL D. BAYNE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <exec/types.h>
#include <exec/memory.h>

#include <intuition/intuition.h>
#include <intuition/screens.h>

#include <libraries/gadtools.h>
#include <dos/dos.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/copper.h>
#include <graphics/videocontrol.h>
#include <hardware/custom.h>

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/alib_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/utility_protos.h>

#include "Moire.h"
#include "Moire_rev.h"
#include "/defs.h"
#include "/utility.h"

struct mPrefObject {
	LONG xSpeed, ySpeed, Trail, Cycle;
};

#pragma libcall GfxBase LoadRGB32 372 9802

VOID blank( VOID );

	struct	mPrefObject	nP;
STATIC	const	UBYTE		VersTag[] = VERSTAG;
extern	struct	Task		**Task;
extern		ULONG		Depth, Mode;
extern		UBYTE		*prefData;

VOID setMoirePrefs( VOID )
{
        GT_SetGadgetAttrs( MoireGadgets[GD_TRAIL], MoireWnd, 0L, GTSL_Level, nP.Trail, 0L );
        GT_SetGadgetAttrs( MoireGadgets[GD_XSPEED], MoireWnd, 0L, GTSL_Level, nP.xSpeed, 0L );
        GT_SetGadgetAttrs( MoireGadgets[GD_YSPEED], MoireWnd, 0L, GTSL_Level, nP.ySpeed, 0L );
	GT_SetGadgetAttrs( MoireGadgets[GD_CYCLE], MoireWnd, 0L, GTCY_Active, nP.Cycle, 0L );
}

int OKClicked( VOID )
{
	CopyMem( &nP, prefData, sizeof( struct mPrefObject ));
	return( QUIT );
}

int TESTClicked( VOID )
{
	*Task = FindTask( 0L );
	blank();
	return( CONTINUE );
}

int CANCELClicked( VOID )
{
	return( QUIT );
}

int TRAILClicked( VOID )
{
	nP.Trail = MoireMsg.Code;
	return( CONTINUE );
}

int CYCLEClicked( VOID )
{
	nP.Cycle = MoireMsg.Code;
	return( CONTINUE );
}

int XSPEEDClicked( VOID )
{
	nP.xSpeed = MoireMsg.Code;
	return( CONTINUE );
}

int YSPEEDClicked( VOID )
{
	nP.ySpeed = MoireMsg.Code;
	return( CONTINUE );
}

int MoireVanillaKey( VOID )
{
	switch( MoireMsg.Code ) {
	case 'o':
		return( OKClicked() );
	case 't':
		return( TESTClicked() );
	case 'c':
		return( CANCELClicked() );
	case 'r':
		GT_SetGadgetAttrs( MoireGadgets[GD_TRAIL], MoireWnd, 0L, GTSL_Level, ++(nP.Trail) > 256 ?
			nP.Trail = 256 : nP.Trail, 0L );
		return( CONTINUE );
	case 'R':
		GT_SetGadgetAttrs( MoireGadgets[GD_TRAIL], MoireWnd, 0L, GTSL_Level, --(nP.Trail) < 1 ?
			nP.Trail = 1 : nP.Trail , 0L );
		return( CONTINUE );
	case 'x':
		GT_SetGadgetAttrs( MoireGadgets[GD_XSPEED], MoireWnd, 0L, GTSL_Level, ++(nP.xSpeed) > 64 ?
			nP.xSpeed = 64 : nP.xSpeed, 0L );
		return( CONTINUE );
	case 'X':
		GT_SetGadgetAttrs( MoireGadgets[GD_XSPEED], MoireWnd, 0L, GTSL_Level, --(nP.xSpeed) < 1 ?
			nP.xSpeed = 1 : nP.xSpeed, 0L );
		return( CONTINUE );
	case 'y':
		GT_SetGadgetAttrs( MoireGadgets[GD_YSPEED], MoireWnd, 0L, GTSL_Level, ++(nP.ySpeed) > 64 ?
			nP.ySpeed = 64 : nP.ySpeed, 0L );
		return( CONTINUE );
	case 'Y':
		GT_SetGadgetAttrs( MoireGadgets[GD_YSPEED], MoireWnd, 0L, GTSL_Level, --(nP.ySpeed) < 1 ?
			nP.ySpeed = 1 : nP.ySpeed, 0L );
		return( CONTINUE );
	default:
		return( CONTINUE );
	}
}

VOID prefs( LONG command )
{
	switch( command ) {
	case STARTUP:
		CopyMem( prefData, &nP, sizeof( struct mPrefObject ));
		if( !SetupScreen() ) {
			if( !OpenMoireWindow()) setMoirePrefs();
			CloseDownScreen();
		}
		break;
	case IDCMP:
		if( HandleMoireIDCMP() != QUIT ) break;
	case KILL:
		CloseMoireWindow();
	}
}

LONG winSig( VOID )
{
	return( MoireWnd ? 1L << MoireWnd->UserPort->mp_SigBit : 0L );
}

VOID agaInit( struct Screen *Screen, LONG numCols )
{
	ULONG *agaTable, i, tPos;

	agaTable = AllocMem(( numCols*3 + 2 )*sizeof( ULONG ), MEMF_CLEAR );

	agaTable[0] = ( numCols<<16 );
	agaTable[1] = agaTable[2] = agaTable[3] = 0;
	tPos = 3;
	for( i = 1; i < numCols/3+1; ++i ) {
		agaTable[++tPos] = (0xFFFFFF)|((numCols-3*i)<<24);
		agaTable[++tPos] = (0xFFFFFF)|((3*i)<<24);
		agaTable[++tPos] = (0xFFFFFF);
	}
	for( i = 1; i < numCols/3+1; ++i ) {
		agaTable[++tPos] = (0xFFFFFF);
		agaTable[++tPos] = (0xFFFFFF)|((numCols-3*i)<<24);
		agaTable[++tPos] = (0xFFFFFF)|((3*i)<<24);
	}
	for( i = 1; i < numCols/3+1; ++i ) {
		agaTable[++tPos] = (0xFFFFFF)|((3*i)<<24);
		agaTable[++tPos] = (0xFFFFFF);
		agaTable[++tPos] = (0xFFFFFF)|((numCols-3*i)<<24);
	}
	agaTable[++tPos] = 0;

	LoadRGB32( &(Screen->ViewPort), agaTable );
}

VOID ecsInit( struct Screen *Screen, LONG numCols )
{
	UWORD	Table6[] = { 0, 0x0F20, 0x0E30, 0x0C50, 0x0B60, 0x0980, 0x0890, 0x06B0,
		0x05C0, 0x03E0, 0x02F0, 0x00F2, 0x00E3, 0x00C5, 0x00B6, 0x0098, 0x006B,
		0x005C, 0x003E, 0x002F, 0x020F, 0x030E, 0x050C, 0x060B, 0x0809, 0x0908,
		0x0B06, 0x0C05, 0x0E03, 0x0F02, 0x0F00, 0x0F00, 0x0F20, 0x0E30, 0x0C50,
		0x0B60, 0x0980, 0x0890, 0x06B0, 0x05C0, 0x03E0, 0x02F0, 0x00F2, 0x00E3,
		0x00C5, 0x00B6, 0x0098, 0x006B, 0x005C, 0x003E, 0x002F, 0x020F, 0x030E,
		0x050C, 0x060B, 0x0809, 0x0908, 0x0B06, 0x0C05, 0x0E03, 0x0F02, 0x0F00 };
	UWORD	Table5[] = { 0, 0x0F20, 0x0E30, 0x0C50, 0x0B60, 0x0980, 0x0890, 0x06B0,
		0x05C0, 0x03E0, 0x02F0, 0x00F2, 0x00E3, 0x00C5, 0x00B6, 0x0098, 0x006B,
		0x005C, 0x003E, 0x002F, 0x020F, 0x030E, 0x050C, 0x060B, 0x0809, 0x0908,
		0x0B06, 0x0C05, 0x0E03, 0x0F02, 0x0F00 };
	UWORD	Table4[] = { 0, 0x0E03, 0x0B06, 0x0909, 0x060B, 0x030E, 0x003E, 0x006B,
		0x0099, 0x00B6, 0x00E3, 0x03E0, 0x06B0,	0x0990, 0x0B60, 0x0E30 };
	UWORD	Table3[] = { 0, 0x0E00, 0x0770, 0x00E0, 0x0077, 0x000E,	0x0707 };
	UWORD	Table2[] = { 0, 0x0E00, 0x00E0, 0x000E };
	UWORD	Table1[] = { 0, 0x0FFF };
	UWORD	*Table[6];

	Table[0] = Table1; Table[1] = Table2; Table[2] = Table3; Table[3] = Table4;
	Table[4] = Table5, Table[5] = Table6;
	
	LoadRGB4( &(Screen->ViewPort), Table[Screen->BitMap.Depth-1], numCols );
}

VOID blank( VOID )
{
	struct	mPrefObject	*mP;
	struct	Screen		*MoireScr;
	struct 	RastPort	*rp;

	ULONG	numCols, i, j, n, Trail, xSpeed, ySpeed;
	WORD	Wid, Hei, *cx1, *cy1, *cx2, *cy2, dx1, dy1, dx2, dy2;

	if( MoireWnd ) mP = &nP;
	else mP = ( struct mPrefObject * )prefData;

	xSpeed = mP->xSpeed;
	ySpeed = mP->ySpeed;
	Trail = mP->Trail;

	MoireScr = OpenScreenTags( 0l, SA_DisplayID, Mode, SA_Depth, Depth, SA_Quiet, TRUE,
		SA_Overscan, OSCAN_STANDARD, TAG_DONE );

	rp = &( MoireScr->RastPort );
	Wid = MoireScr->Width;
	Hei = MoireScr->Height;

   	numCols = (( 1 << MoireScr->BitMap.Depth )-1)%3 ? ( 1 << MoireScr->BitMap.Depth )-1 :
		( 1 << MoireScr->BitMap.Depth );

	if( numCols == 1 ) numCols = 2;

	cx1 = AllocMem( Trail*sizeof( UWORD ), MEMF_CLEAR );
	cy1 = AllocMem( Trail*sizeof( UWORD ), MEMF_CLEAR );
	cx2 = AllocMem( Trail*sizeof( UWORD ), MEMF_CLEAR );
	cy2 = AllocMem( Trail*sizeof( UWORD ), MEMF_CLEAR );

	SetRGB4( &(MoireScr->ViewPort), 0, 0, 0, 0 );

	switch( mP->Cycle ) {
	case 0:
		if( MoireScr->BitMap.Depth > 6 ) agaInit( MoireScr, numCols );
		else ecsInit( MoireScr, numCols );
		break;
	case 1:
		SetRGB4( &(MoireScr->ViewPort), 1, VBeamPos()%15 + 1, VBeamPos()%15 + 1, VBeamPos()%15 + 1 );
		break;
        case 2:
		SetRGB4( &(MoireScr->ViewPort), 1, 15, 15, 15 );
		break;
	case 3:
		setCopperList( Hei, 1, &( MoireScr->ViewPort ));
		break;
	}
		
	cx1[1] = ( VBeamPos()%Wid )+1; cy1[1] = ( VBeamPos()%Hei )+1;
	cx2[1] = ( VBeamPos()%Wid )+1; cy2[1] = ( VBeamPos()%Hei )+1;
	dx1 = ( VBeamPos()%xSpeed )+1; dy1 = ( VBeamPos()%ySpeed )+1;
	dx2 = ( VBeamPos()%xSpeed )+1; dy2 = ( VBeamPos()%ySpeed )+1;
	i = 0; j = 0;

	while(!(SetSignal(0,0) & SIGBREAKF_CTRL_C )) {
		i = (i+1) % Trail;
		n = (i+1) % Trail;
		j = (j+1) % 255;

		if( !mP->Cycle ) SetAPen( rp, (j*(numCols-1))/255+1 );
		else SetAPen( rp, 1 );

		if( cx1[i] >= Wid ) {
			dx1 = -1*( VBeamPos()%xSpeed )-1;
			cx1[i] = Wid-1;
		} else if( cx1[i] < 0 ) {
			dx1 = ( VBeamPos()%xSpeed )+1;
			cx1[i] = 0;
		}
		if( cx2[i] >= Wid ) {
			dx2 = -1*( VBeamPos()%xSpeed )-1;
			cx2[i] = Wid-1;
		} else if( cx2[i] < 0 ) {
			dx2 = ( VBeamPos()%xSpeed )+1;
			cx2[i] = 0;
		}
		if( cy1[i] >= Hei ) {
			dy1 = -1*( VBeamPos()%ySpeed )-1;
			cy1[i] = Hei-1;
		} else if( cy1[i] < 0 ) {
			dy1 = ( VBeamPos()%ySpeed )+1;
			cy1[i] = 0;
		}
		if( cy2[i] >= Hei ) {
			dy2 = -1*( VBeamPos()%ySpeed )-1;
			cy2[i] = Hei-1;
		} else if( cy2[i] < 0 ) {
			dy2 = ( VBeamPos()%ySpeed )+1;
			cy2[i] = 0;
		}

		Move( rp, cx1[i], cy1[i] );
		Draw( rp, cx2[i], cy2[i] );

		SetAPen( rp, 0 );
		Move( rp, cx1[n], cy1[n] );
		Draw( rp, cx2[n], cy2[n] );

		cx1[n] = cx1[i] + dx1;
		cy1[n] = cy1[i] + dy1;
		cx2[n] = cx2[i] + dx2;
		cy2[n] = cy2[i] + dy2;
	}

	SetSignal( 0L, SIGBREAKF_CTRL_C );

	FreeMem( cx1, Trail*sizeof( UWORD ));
	FreeMem( cx2, Trail*sizeof( UWORD ));
	FreeMem( cy1, Trail*sizeof( UWORD ));
	FreeMem( cy2, Trail*sizeof( UWORD ));

	CloseScreen( MoireScr );
}

VOID defaults( struct bMessage *bMsg )
{
	static struct mPrefObject mPO = { 16L, 16L, 64L, 0L };

	bMsg->bm_Mod = INVALID_ID;
	bMsg->bm_Dep = 0;

	bMsg->bm_Info = "Moire (Bouncing lines) Module";
	bMsg->bm_Data = ( UBYTE * )( &mPO );
}
