/*
 *    $VER:SnapGfx.c - (11.09.95) 17:39:11 Copyright © 1995 by Mikael Karlsson & Sylvain ROUGIER
 *
 *      Created: 1988
 *      Modified:	11 Sep 1995	17:39:11
 *
 *      Make>> smake
 *
 * 09/09/95:
 *		fix, to save 8bit per gun palette under V39+
 *
 *	10/09/95:
 *		Now Alloc dynamaicaly BitMap struct. (future usage of AllocBitMap())
 */

#include "Snap.h"

#include "proto/Misc.h"
#include "proto/GfxSnap.h"

IMPORT BOOL Kick36;

IMPORT struct GfxBase *GfxBase;

IMPORT struct SnapRsrc *SnapRsrc;
IMPORT struct Image DiskImage;
IMPORT struct Image ClipImage;
IMPORT struct Gadget DiskGad;
IMPORT struct Gadget ClipGad;
IMPORT struct Gadget VProp, HProp;
IMPORT struct PropInfo VInfo, HInfo;
IMPORT struct Image VImage, HImage;
IMPORT struct NewWindow Nw;
IMPORT UBYTE *WindowTitle;

IMPORT LONG xl;			/* leftmost x position */
IMPORT LONG xr;			/* rightmost x position */
IMPORT LONG yt;			/* topmost y position */
IMPORT LONG yb;			/* bottommost y position */
IMPORT LONG mx, my;		/* Mouse position in pixels */

#define GfxFrame 4L
IMPORT Point OldFrame[ ];
IMPORT Point NewFrame[ ];
IMPORT LONG OFType;		/* Old frame type: ShortFrame/LongFrame */
IMPORT UWORD Ptrn;
IMPORT WORD Pattern[ ];

IMPORT struct RastPort MyRP;
IMPORT struct BitMap MyBM;
IMPORT struct Screen *theScreen;
IMPORT struct RastPort rp;

IMPORT struct timerequest MyTR;

IMPORT LONGBITS cancelsignal, donesignal, movesignal;
IMPORT LONGBITS clicksignal, ticksignal, timersignal;
IMPORT WORD action;

LONG TopBar;
LONG LeftBar;
LONG RightBar;
LONG BottomBar;
LONG ScreenFontHeight;
struct TextFont *ScreenFont;

VOID FixHeights( )
{
	struct Screen WBScreen;

	OpenWorkBench( );
	if ( GetScreenData( ( char *)&WBScreen, ( LONG) sizeof ( struct Screen), WBENCHSCREEN, NULL))
	{
		/* Now this is a good practice */
		TopBar = WBScreen.WBorTop + WBScreen.RastPort.TxHeight + 1;
		LeftBar = WBScreen.WBorLeft;
		RightBar = WBScreen.WBorRight;
		BottomBar = WBScreen.WBorBottom;
		ScreenFont = WBScreen.RastPort.Font;
		if ( !ScreenFont)
		{
			Forbid( );
			ScreenFont = GfxBase->DefaultFont;
			Permit( );
		}
		ScreenFontHeight = ScreenFont->tf_YSize;
	}
	else
	{
		/* Sorry, but I don't realise how this could fail.
		   Well, if we're snapping on another screen and
		   WB is closed and it can't be opened by GetScreenData( )...
		   Anyway, IF this should happen -- Use Topaz 8 */
		TopBar = 10;
		LeftBar = 2;
		RightBar = 2;
		BottomBar = 1;
		ScreenFontHeight = 8;
		Forbid( );
		ScreenFont = GfxBase->DefaultFont;
		Permit( );
	}
}


struct GfxSnap *CreateGfxSnap( void)
{
	struct GfxSnap *GfxSnap;

	if ( GfxSnap = AllocVec( sizeof( struct GfxSnap), MEMF_ANY|MEMF_CLEAR))
	{
		return GfxSnap;
	}

	return NULL;
}

void DeleteGfxSnap( struct GfxSnap *GfxSnap)
{
	if ( GfxSnap)
	{
		if ( GfxSnap->BitMap)
		{
			Snp_FreeBitMap( GfxSnap->BitMap, GfxSnap->width, GfxSnap->height);
		}
		FreeVec( GfxSnap);
	}

	return;
}

static void CopyColorDefs( ULONG Depth, struct ColorMap *ColorMap, UBYTE RGB8[][3])
{
	ULONG i = Depth;

	if ( SysBase->LibNode.lib_Version < 39)
	{
		ULONG col;

		while ( i-- && ( col = GetRGB4( ColorMap, i)) != -1L)
		{
			RGB8[ i][ 0] = RGB4ToRR( col);
			RGB8[ i][ 1] = RGB4ToGG( col);
			RGB8[ i][ 2] = RGB4ToBB( col);
		}
	}
	else
	{
		while ( i--)
		{
			ULONG RGB[ 3];

			GetRGB32( ColorMap, i, 1, RGB);
			RGB8[ i][ 0] = RGB32ToRGB8( RGB[ 0]);
			RGB8[ i][ 1] = RGB32ToRGB8( RGB[ 1]);
			RGB8[ i][ 2] = RGB32ToRGB8( RGB[ 2]);
		}
	}
}

/* SnapGfx is the actual graphics snapper.
   ** It steals the bitmap data from the given rastport
   ** and puts it in a separate bitmap.
   ** It also prepares the NewWindow structure according
   ** to the snapped bitmap.
   ** The coordinates are assumed to be valid.
 */
struct GfxSnap * SnapGfx( struct RastPort *rp)
{
	struct GfxSnap *GS;
	LONG i;

	GS = CreateGfxSnap();
	if ( !GS)
	{
		return NULL;
	}

	GS->x = xl;
	GS->y = yt;
	GS->width = xr - xl + 1;
	GS->height = yb - yt + 1;
	if ( xr < xl + MINWIDTH)
	{			/* Can't have too small windows */
		xr = xl + MINWIDTH;
	}
	if ( yb < yt + MINHEIGHT)
	{
		yb = yt + MINHEIGHT;
	}
	GS->depth = rp->BitMap->Depth;
	GS->viewmode = theScreen->ViewPort.Modes;
	GS->pagew = theScreen->Width;
	GS->pageh = theScreen->Height;
	i = ( GS->viewmode & HAM ? 16 : 1L << GS->depth);
	/* Copy the color map in case we should need it later */
	CopyColorDefs(
		GS->viewmode & HAM ?
			16 :
			1L << GS->depth,
		theScreen->ViewPort.ColorMap,
		GS->rgb);

	/* Set up a nice bitmap */
	InitRastPort( &MyRP);
	MyRP.BitMap = GS->BitMap = AllocBitMap( GS->width, GS->height, GS->depth, BMF_DISPLAYABLE, rp->BitMap);
	if ( !GS->BitMap)
	{
		DeleteGfxSnap( GS);
		return NULL;
	}

	/* Copy the selected part of the screen */
	ClipBlit( rp, xl, yt, &MyRP, 0L, 0L, GS->width, GS->height, 0xC0L);
	if ( !Kick36)
	{
		VInfo.Flags |= PROPBORDERLESS;
		HInfo.Flags |= PROPBORDERLESS;
	}
	CopyMem( ( char *)&DiskGad, ( char *)&GS->DiskGad, ( LONG) sizeof ( DiskGad) + sizeof ( ClipGad) + sizeof ( VProp) + sizeof ( HProp) + sizeof ( VInfo) + sizeof ( HInfo) + sizeof ( VImage) + sizeof ( HImage));
	GS->topbar = TopBar;
	GS->leftbar = LeftBar;
	GS->rightbar = 18;	/* ( Kick36 ? RightBar : 18); */
	GS->bottombar = 10;

	Nw.LeftEdge = xl;
	Nw.TopEdge = yt;
	Nw.Width = xr - xl + 1 + GS->leftbar + GS->rightbar;
	Nw.Height = yb - yt + 1 + GS->topbar + GS->bottombar;
	Nw.Flags &= ~( SIMPLE_REFRESH | SMART_REFRESH | NOCAREREFRESH);
	Nw.Flags |= ( SnapRsrc->flags & SIMPLEREFRESH ?
		SIMPLE_REFRESH :
		SMART_REFRESH | NOCAREREFRESH);

	Nw.Title = WindowTitle;
	Nw.MinWidth = MINWIDTH + 1 + GS->leftbar + GS->rightbar;
	Nw.MinHeight = MINHEIGHT + 1 + GS->topbar + GS->bottombar;
	Nw.MaxWidth = Nw.Width;
	Nw.MaxHeight = Nw.Height;

	if ( Kick36)
		Nw.Flags |= SIZEBBOTTOM | SIZEBRIGHT;

	return GS;
}

VOID ExtendGfx( )
{
	/* Fix which row we're talking about */
	if ( my - yt < yb - my)
	{			/* Find closest row */
		yt = my;	/* change top row */
	}
	else
	{
		yb = my;	/* change bottom row */
	}
	if ( mx - xl < xr - mx)
	{
		xl = mx;
	}
	else
	{
		xr = mx;
	}
}

VOID gfx_frame( )
{
	NewFrame[ 0].x = xl;
	NewFrame[ 0].y = yt;
	NewFrame[ 1].x = xr;
	NewFrame[ 1].y = yt;
	NewFrame[ 2].x = xr;
	NewFrame[ 2].y = yb;
	NewFrame[ 3].x = xl;
	NewFrame[ 3].y = yb;
	NewFrame[ 4].x = xl;
	NewFrame[ 4].y = yt;
	draw_frame( GfxFrame);
}

WORD HandleGfx( void)
{
	struct Layer_Info *LockedLayerInfo;

	theScreen = WhichScreen( );	/* Find out where we are */
	if ( !theScreen)
	{	/* Don't know? Forget it. */
		action = noaction;
		return 0;
	}
	/* Lock everything - find out what happens */
	LockedLayerInfo = &theScreen->LayerInfo;
//	LockLayers( LockedLayerInfo);

	/* Get a copy. Don't mess with somebody else's RP. */
	CopyMem( ( char *)&theScreen->RastPort, ( char *)&rp, ( long)sizeof ( struct RastPort));

	SetDrMd( &rp, COMPLEMENT);

	xl = theScreen->MouseX + theScreen->ViewPort.RasInfo->RxOffset;
	if ( xl < 0)
	{
		xl = 0;
	}
	if ( xl >= theScreen->Width)
	{	/* Check those corners. Check those corners. */
		xl = theScreen->Width - 1;
	}
	yt = theScreen->MouseY + theScreen->ViewPort.RasInfo->RyOffset;
	if ( yt < 0)
	{
		yt = 0;
	}
	if ( yt >= theScreen->Height)
	{
		yt = theScreen->Height - 1;
	}
	xr = xl;
	yb = yt;
	Ptrn = ( SnapRsrc->CrawlPtrn ? SnapRsrc->CrawlPtrn : Pattern[ UNIT_FRAME]);
	OFType = 0L;
	gfx_frame( );

	FOREVER
	{
		REGISTER LONGBITS sig;

		MyTR.tr_time.tv_secs = 0;
		MyTR.tr_time.tv_micro = 500000;
		MyTR.tr_node.io_Command = TR_ADDREQUEST;
		SendIO( ( struct IORequest *)&MyTR);

		sig = Wait( movesignal | clicksignal | cancelsignal | donesignal | ticksignal | timersignal);

		if ( CheckIO( ( struct IORequest *)&MyTR))
		{
			WaitIO( ( struct IORequest *)&MyTR);
			erase_frame( );
//			UnlockLayers( LockedLayerInfo);	/* Unlock things */
			sig = Wait( ticksignal);		/* Wait for input handler to become avtive */
//			LockLayers( LockedLayerInfo);	/* Re-lock */
			/* I guess I should check to see that the window is still
			   there. Aw, what the heck. Apologies to anyone who gets
			   bitten by this. */
			DisplayBeep( NULL);
			gfx_frame( );
		}
		else
		{
			AbortIO( ( struct IORequest *)&MyTR);
			WaitIO( ( struct IORequest *)&MyTR);
			SetSignal( 0, timersignal);
		}

		if ( ( sig & ticksignal) && ( SnapRsrc->CrawlPtrn != 0xffff))
		{
			crawl_frame( 1L);
		}

		if ( sig & movesignal || sig & clicksignal)
		{
			mx = theScreen->MouseX + theScreen->ViewPort.RasInfo->RxOffset;
			if ( mx < 0)
			{
				mx = 0;
			}
			if ( mx >= theScreen->Width)
			{
				mx = theScreen->Width - 1;
			}
			my = theScreen->MouseY + theScreen->ViewPort.RasInfo->RyOffset;
			if ( my < 0)
			{
				my = 0;
			}
			if ( my >= theScreen->Height)
			{
				my = theScreen->Height - 1;
			}
			ExtendGfx( );
			gfx_frame( );
		}
		if ( sig & cancelsignal)
		{	/* Cancelled? */
			erase_frame( );
//			UnlockLayers( LockedLayerInfo);
			return 0;
		}
		if ( sig & donesignal)
		{	/* Finished. Copy gfx. */
			struct GfxSnap *GS;

			erase_frame( );
			if ( xl == xr || yt == yb)
			{
				action = noaction;
//				UnlockLayers( LockedLayerInfo);
				return 0;
			}
			if ( xr >= theScreen->Width)
			{
				xl -= xr - theScreen->Width - 1;
				xr = theScreen->Width - 1;
			}
			if ( yb >= theScreen->Height)
			{
				yt -= yb - theScreen->Height - 1;
				yb = theScreen->Height - 1;
			}
			FixHeights( );
			GS = SnapGfx( &rp);	/* Snap! */
//			UnlockLayers( LockedLayerInfo);
			if ( GS)
			{
				if ( GS->window = opensharedwindow( &Nw))
				{
					if ( SnapRsrc->flags & SIMPLEREFRESH)
					{
						ModifyIDCMP( GS->window,
							    GS->window->IDCMPFlags | REFRESHWINDOW);
					}
					if ( Kick36)
					{
						GS->topbar = GS->window->BorderTop;
						GS->leftbar = GS->window->BorderLeft;
						GS->rightbar = GS->window->BorderRight;
						GS->bottombar = 10;	/*GS->window->BorderBottom; */

						GS->HProp.TopEdge = -7;
						GS->HProp.Height = 6;
					}

					GS->DiskGad.TopEdge = GS->topbar;
					GS->DiskGad.LeftEdge = ( Kick36 ? -17 : -14);
					GS->DiskGad.Width = ( Kick36 ? 18 : 14);
					GS->ClipGad.TopEdge = GS->DiskGad.TopEdge + 12;
					GS->ClipGad.LeftEdge = ( Kick36 ? -17 : -14);
					GS->ClipGad.Width = ( Kick36 ? 18 : 14);
					GS->HProp.GadgetRender = ( APTR) & GS->HImage;
					GS->HProp.SpecialInfo = ( APTR) & GS->HInfo;
					GS->VProp.TopEdge =
						GS->ClipGad.TopEdge + ( Kick36 ? 14 : 15);
					GS->VProp.LeftEdge = ( Kick36 ? 5 : 7) - GS->rightbar;
					GS->VProp.Width = GS->rightbar - ( Kick36 ? 8 : 10);
					GS->VProp.Height =
						( Kick36 ? -28 : -27) - GS->bottombar - GS->topbar;
					GS->VProp.GadgetRender = ( APTR) & GS->VImage;
					GS->VProp.SpecialInfo = ( APTR) & GS->VInfo;

					GS->DiskGad.NextGadget = &GS->ClipGad;
					GS->ClipGad.NextGadget = &GS->VProp;
					GS->VProp.NextGadget = &GS->HProp;
					GS->HProp.NextGadget = NULL;

					AddGList( GS->window, &GS->DiskGad, -1, -1, NULL);
					GS->window->UserData = ( BYTE *) GS;
					/* Put gfx in our new window */
					AdjustSize( GS);
				}
				else
				{
					DeleteGfxSnap( GS);
				}
			}
			else
			{	/* Good question */
			}
			action = noaction;
			return 0;
		}
	}
}
