/****************************************************************************
*
* VERSION
*	$VER: analog.c 1.66 (08.12.93)
*
* DESCRIPTION
*	Analog clock display code...
*
* AUTHOR
*	Rune Johnsrud
*
* COPYRIGHT
*	(c) 1993 Amiga Freelancers
*
*****************************************************************************/

#include <graphics/gfxmacros.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <utility/date.h>

#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/diskfont.h>
#include <proto/gadtools.h>
#include <proto/layers.h>

#include "global.h"

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

extern struct GlobalData *gd;
extern struct ClockInfo *ci;
extern struct Gadget ClockGadget;
extern WORD SinTab[60];
extern WORD CosTab[60];

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

struct RectPoints
{
	WORD x;
	WORD y;
	WORD w;
	WORD h;
};

#define NUM_OF_VECTORS		100
#define MAXVECTORS 			NUM_OF_VECTORS / 5
#define MINTERM_VCOPY 		0xC0
#define CB_COPY				TRUE
#define CB_PASTE			FALSE
#define DT_RENDER_BITMAP	TRUE
#define DT_GET_ATTRS		FALSE
#define DIV_SCALER			1024

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

static VOID CalcWinDims(VOID);
static WORD DrawClock(VOID);
static VOID DrawTimeI(WORD x, WORD y, UBYTE pen);

static VOID SortBlitPoints(WORD x, WORD y, WORD w, WORD h, struct RectPoints *p);
static VOID BlitClip(WORD x, WORD y, WORD ox, WORD oy, BOOL source);

static VOID SaveDisplay(VOID);
static VOID RestoreDisplay(VOID);

WORD CreateAnalogClock(VOID);
VOID RefreshAnalogClock(VOID);
WORD DeleteAnalogClock(VOID);

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


static VOID CalcWinDims(VOID)
{
	if (ci->WinBevel)
	{
		ci->InnerTop    = 1;
		ci->InnerLeft   = 1;
		ci->InnerWidth  = ci->Width  - 1;
		ci->InnerHeight = ci->Height - 1;
	}
	else
	{
		ci->InnerTop    = 0;
		ci->InnerLeft   = 0;
		ci->InnerWidth  = ci->Width;
		ci->InnerHeight = ci->Height;
	}

	if (ci->PlaceClock)
	{
		ci->ap.CtX = ((ci->ap.ClockLeft + ci->ap.ClockWidth)  / 2);
		ci->ap.CtY = ((ci->ap.ClockTop  + ci->ap.ClockHeight) / 2);
		ci->ap.SzX = (ci->ap.ClockWidth  / 2);
		ci->ap.SzY = (ci->ap.ClockHeight / 2);

		if (ci->ap.SzX > (ci->Width / 2))
			ci->ap.SzX = (ci->Width / 2);

		if (ci->ap.SzY > (ci->Height / 2))
			ci->ap.SzY = (ci->Height / 2);

		if ((ci->ap.CtX + ci->ap.SzX) >= ci->Width)
			ci->ap.CtX = (ci->Width - ci->ap.SzX) - 1;

		if ((ci->ap.CtY + ci->ap.SzY) >= ci->Height)
			ci->ap.CtY = (ci->Height - ci->ap.SzY) - 1;
	}
	else
	{
		ci->ap.CtX = (ci->InnerWidth  / 2);
		ci->ap.CtY = (ci->InnerHeight / 2);
		ci->ap.SzX = (ci->ap.CtX - ci->EdgeSpace);
		ci->ap.SzY = (ci->ap.CtY - ci->EdgeSpace);
	}

	ci->ap.SzX -= (ci->ap.SzX / ci->ap.MIXScale);
	ci->ap.SzY -= (ci->ap.SzY / ci->ap.MIYScale);

	if (ci->ShowDial)
	{
		ci->ap.SzX -= 2;
		ci->ap.SzY -= 2;
	}

	ci->ap.Dec = ((ci->ap.SzX + ci->ap.SzY) / 5);
}


static WORD DrawClock(VOID)
{
	struct RastPort *rp = gd->ClockWindow->RPort;
	struct TmpRas tmpras;
	struct AreaInfo areainfo;
	VOID *rasdata = NULL;
	WORD *vecbuff = NULL;
	ULONG rsize;
	WORD a, x, y, sx, sy, errcode = NOERROR;

	sx = (ci->ap.SzX / ci->ap.MIXScale);
	sy = (ci->ap.SzY / ci->ap.MIYScale);

	if (ci->ShowDial)
	{
		rsize   = RASSIZE(ci->Width, ci->Height);
		rasdata = (VOID *) AllocVec(rsize, MEMF_CHIP);
		vecbuff = (WORD *) AllocVec(NUM_OF_VECTORS, MEMF_CLEAR);

		if (rasdata && vecbuff)
		{
			InitTmpRas(&tmpras, rasdata, rsize);
			rp->TmpRas = &tmpras;

			InitArea(&areainfo, vecbuff, MAXVECTORS);
			rp->AreaInfo = &areainfo;

			SetAPen(rp, ci->apn.DialOPen);
			AreaEllipse(rp, ci->ap.CtX, ci->ap.CtY, 2 + ci->ap.SzX + sx, 2 + ci->ap.SzY + sy);
			AreaEnd(rp);

			SetAPen(rp, ci->apn.DialPen);
			AreaEllipse(rp, ci->ap.CtX, ci->ap.CtY, 1 + ci->ap.SzX + sx, 1 + ci->ap.SzY + sy);
			AreaEnd(rp);
		}
		else
			errcode = GEN_ERR_ALLOCMEM;

		if (rasdata)
		{
			FreeVec(rasdata);
			rp->TmpRas = NULL;
		}

		if (vecbuff)
		{
			FreeVec(vecbuff);
			rp->AreaInfo = NULL;
		}
	}

	SetAPen(rp, ci->apn.MinSmallPen);
	for (a=0; a<60; a++)
	{
		x = ci->ap.CtX + ((SinTab[a] * ci->ap.SzX) / DIV_SCALER);
		y = ci->ap.CtY - ((CosTab[a] * ci->ap.SzY) / DIV_SCALER);

		WritePixel(rp, x, y);
	}

	SetAPen(rp, ci->apn.MinLargePen);
	for (a=0; a<60; a+=5)
	{
		x = ci->ap.CtX + ((SinTab[a] * ci->ap.SzX) / DIV_SCALER);
		y = ci->ap.CtY - ((CosTab[a] * ci->ap.SzY) / DIV_SCALER);

		RectFill(rp, x - sx, y - sy, x + sx, y + sy);
	}

	return errcode;
}


static VOID DrawTimeI(WORD x, WORD y, UBYTE pen)
{
	struct RastPort *rp = gd->ClockWindow->RPort;

	SetAPen(rp, pen);
	Move(rp, ci->ap.CtX, ci->ap.CtY);
	Draw(rp, x, y);
}


WORD CreateAnalogClock(VOID)
{
	struct Gadget *g = &ClockGadget;
	struct RastPort *rp;
	UBYTE depth;

	if (gd->ClockWindow) CleanUpClock();

//	gd->TextFont = OpenDiskFont(&gd->TextAttr);
//	if (!gd->TextFont) return GEN_ERR_OPENFONT;

	gd->VScreen = LockPubScreen(ci->PubScreenName);
	if (!gd->VScreen) return GEN_ERR_LOCKSCREEN;

	gd->VisualInfo = GetVisualInfo(gd->VScreen, NULL);
	if (!gd->VisualInfo) return GEN_ERR_GETVISUALINFO;

	if ((ci->AutoSize) && (ci->BDRPic)) Error("Datatypes", ReadDTPic(DT_GET_ATTRS));

	CalcWinDims();

	gd->ClockWindow = OpenWindowTags(NULL,
						WA_PubScreen,	gd->VScreen,
						WA_Top,			ci->Top,
						WA_Left,		ci->Left,
						WA_InnerWidth,	ci->Width,
						WA_InnerHeight,	ci->Height,
						WA_Activate,	TRUE,
						WA_Borderless,	TRUE,
						WA_Backdrop,	ci->BDClock,
						WA_DragBar,		FALSE,
						WA_NewLookMenus,TRUE,
						WA_IDCMP,		IDCMP_MENUPICK | IDCMP_IDCMPUPDATE,
						TAG_DONE);

	if (!gd->ClockWindow) return GEN_ERR_OPENWINDOW;

	depth = GetBitMapAttr(gd->VScreen->RastPort.BitMap, BMA_DEPTH);

	gd->SaveBM = AllocBitMap(ci->Width, ci->Height, depth, BMF_INTERLEAVED, NULL);
	if (!gd->SaveBM) return GEN_ERR_ALLOCMEM;

	InitRastPort(&gd->SaveRP);
	gd->SaveRP.BitMap = gd->SaveBM;

	if (!InitMainMenus()) return GEN_ERR_MENUS;

	g->Width  		= ci->InnerWidth;
	g->Height 		= ci->InnerHeight;
	gd->ClockGadget = g;
	rp 				= gd->ClockWindow->RPort;

//	gd->FillRP = *rp;
//	SetAPen(&gd->FillRP, ci->BGPen);

	SetAPen(rp, ci->BGPen);
	RectFill(rp, 0, 0, ci->InnerWidth, ci->InnerHeight);

	if (ci->WinBevel)
	{
		SetAPen(rp, ci->ShinePen);
		Move(rp, 0, ci->InnerHeight);
		Draw(rp, 0, 0);
		Draw(rp, ci->InnerWidth, 0);

		SetAPen(rp, ci->ShadowPen);
		Move(rp, ci->InnerWidth, 1);
		Draw(rp, ci->InnerWidth, ci->InnerHeight);
		Draw(rp, 0, ci->InnerHeight);

		ci->InnerWidth--;
		ci->InnerHeight--;
	}

	if (ci->BDRPic) Error("Datatypes", ReadDTPic(DT_RENDER_BITMAP));

	if (gd->VScreen)
	{
		UnlockPubScreen(NULL, gd->VScreen);
		gd->VScreen = NULL;
	}

	Error("Draw Clock", DrawClock());

//	SetABPenDrMd(rp, ci->TextPen, ci->BGPen, JAM2);
//	SetFont(rp, gd->TextFont);

	AddGadget   (gd->ClockWindow, gd->ClockGadget, -1);
	RefreshGList(gd->ClockGadget, gd->ClockWindow, NULL, -1);

	gd->CData.min  = 99;
	gd->CData.hour = 99;

	GetCurrSysTime();
	SaveDisplay();
	RefreshAnalogClock();

	return NOERROR;
}


static VOID SortBlitPoints(WORD x, WORD y, WORD w, WORD h, struct RectPoints *p)
{
	if (x <= w)
	{
		p->x = x - 1;
		p->w = w + 1;
	}
	else
	{
		p->x = w - 1;
		p->w = x + 1;
	}

	if (y <= h)
	{
		p->y = y - 1;
		p->h = h + 1;
	}
	else
	{
		p->y = h - 1;
		p->h = y + 1;
	}
}


static VOID BlitClip(WORD x, WORD y, WORD ox, WORD oy, BOOL source)
{
	struct RectPoints p;
	struct RastPort *rp = gd->ClockWindow->RPort;

	SortBlitPoints(x, y, ci->ap.CtX, ci->ap.CtY, &p);

	if (source)
	{
		ClipBlit(rp, p.x, p.y, &gd->SaveRP, ox, oy,
				(p.w - p.x), (p.h - p.y), MINTERM_VCOPY);
	}
	else
	{
		ClipBlit(&gd->SaveRP, ox, oy, rp, p.x, p.y,
				(p.w - p.x), (p.h - p.y), MINTERM_VCOPY);
	}
}


static VOID SaveDisplay(VOID)
{
	WORD i;

	if (ci->ShowSec)
	{
		i         = gd->CData.sec;
		ci->ap.sx = ci->ap.CtX + ((SinTab[i] * ci->ap.SzX) / DIV_SCALER);
		ci->ap.sy = ci->ap.CtY - ((CosTab[i] * ci->ap.SzY) / DIV_SCALER);

		BlitClip(ci->ap.sx, ci->ap.sy, 0, 0, CB_COPY);
	}

	if (gd->NewMin)
	{
		i         = gd->CData.min;
		ci->ap.mx = ci->ap.CtX + ((SinTab[i] * ci->ap.SzX) / DIV_SCALER);
		ci->ap.my = ci->ap.CtY - ((CosTab[i] * ci->ap.SzY) / DIV_SCALER);

		BlitClip(ci->ap.mx, ci->ap.my, (ci->Width / 2), 0, CB_COPY);

		gd->NewMin = FALSE;
	}

	if (gd->NewHour)
	{
		i         = (((gd->CData.hour % 12) * 5) + (gd->CData.min / 12));
		ci->ap.hx = ci->ap.CtX + ((SinTab[i] * (ci->ap.SzX - ci->ap.Dec)) / DIV_SCALER);
		ci->ap.hy = ci->ap.CtY - ((CosTab[i] * (ci->ap.SzY - ci->ap.Dec)) / DIV_SCALER);

		BlitClip(ci->ap.hx, ci->ap.hy, 0, (ci->Height / 2), CB_COPY);

		gd->NewHour = FALSE;
	}
}


static VOID RestoreDisplay(VOID)
{
	if (ci->ShowSec)
		BlitClip(ci->ap.sx, ci->ap.sy, 0, 0, CB_PASTE);

	if (gd->NewMin)
		BlitClip(ci->ap.mx, ci->ap.my, (ci->Width / 2), 0, CB_PASTE);

	if (gd->NewHour)
		BlitClip(ci->ap.hx, ci->ap.hy, 0, (ci->Height / 2), CB_PASTE);
}


VOID RefreshAnalogClock(VOID)
{
	GetCurrSysTime();

	RestoreDisplay();
	SaveDisplay();

	if (ci->ShowSec)
	{
		DrawTimeI(ci->ap.hx, ci->ap.hy, ci->apn.HourPen);
		DrawTimeI(ci->ap.mx, ci->ap.my, ci->apn.MinPen);
		DrawTimeI(ci->ap.sx, ci->ap.sy, ci->apn.SecPen);
	}

	if (gd->NewTime)
	{
		DrawTimeI(ci->ap.hx, ci->ap.hy, ci->apn.HourPen);
		DrawTimeI(ci->ap.mx, ci->ap.my, ci->apn.MinPen);

		gd->NewTime = FALSE;
	}
}


WORD DeleteAnalogClock(VOID)
{
	ci->Top  = gd->ClockWindow->TopEdge;
	ci->Left = gd->ClockWindow->LeftEdge;

	if (gd->VScreen)	 UnlockPubScreen(NULL, gd->VScreen);
	if (gd->SaveBM)		 FreeBitMap(gd->SaveBM);
	if (gd->ClockGadget) RemoveGadget(gd->ClockWindow, gd->ClockGadget);
//	if (gd->TextFont)    CloseFont(gd->TextFont);
	if (gd->ClockWindow) ClearMenuStrip(gd->ClockWindow);
	if (gd->MainMenu)	 FreeMenus(gd->MainMenu);
	if (gd->VisualInfo)	 FreeVisualInfo(gd->VisualInfo);
	if (gd->ClockWindow) CloseWindow(gd->ClockWindow);

	gd->VScreen     = NULL;
	gd->ClockGadget = NULL;
	gd->VisualInfo  = NULL;
	gd->MainMenu    = NULL;
//	gd->TextFont    = NULL;
	gd->ClockWindow = NULL;

	return NOERROR;
}


/* End Of File */
