/*
 *  File:		fatbits.c
 *  Function:	fatbits editor
 *  Author:		Paul Elseth
 *				Copyright (c) 1991, Paul Elseth
 *
 *	Change History:
 *
 *		12/09/91	PBE		created
 */

#ifndef	__FATBITS__
#include "fatbits.h"
#endif

#ifndef	__EDITOR__
#include "editor.h"
#endif
#ifndef	__GLOBALS__
#include "globals.h"
#endif
#ifndef	__FILL__
#include "fill.h"
#endif
#ifndef	__UTILS__
#include "utils.h"
#endif
#ifndef	__MEMUTILS__
#include "memUtils.h"
#endif
#ifndef	__FATUTILS__
#include "fatUtils.h"
#endif
#ifndef	__OFFSCREEN__
#include "offscreen.h"
#endif
#ifndef	__COLOR__
#include "color.h"
#endif
#ifndef	__WINDOW__
#include <window.h>
#endif
#ifndef __QDAUX__
#include <qdaux.h>
#endif
#ifndef __orca__
#include <orca.h>
#endif
#ifndef	__OPTIONS__
#include "options.h"
#endif

#pragma noroot


static void	ShiftIcon(IconData **dataH, Point pt);


/*
 *	Dirty()
 *		Mark a window as changed.
 */
void Dirty(IconData **dataH)
{
	IconData	*dataP;

    dataP = *dataH;
	dataP->dirty = true;
/*	MarkResourceChange(true, rIcon, dataP->resID);	*/
}


/*
 *	GetPixelRect()
 *		Calculate the fat pixel rect for a pixel.
 */
static
void GetPixelRect(short x, short y, Rect *r)
{
	r->h1 = x * 8;
    r->h2 = r->h1 + 6;
	r->v1 = y * 4;
    r->v2 = r->v1 + 3;
}


/*
 *	Pt2Pixel()
 *		Calculate the pixel for a fat window location.
 */
void Pt2Pixel(short ox, short oy, IconData **dataH, Boolean inMask, short *x, short *y)
{
	IconData	*dataP;
	short		h, v;
	Rect		r;

	dataP = *dataH;
    h = inMask ? dataP->fatMRect.h1 : dataP->fatIRect.h1;
    v = inMask ? dataP->fatMRect.v1 : dataP->fatIRect.v1;

	*x = (unsigned short) (ox - h - 4) / 8;
	*y = (unsigned short) (oy - v - 2) / 4;
}


/*
 *	GetPixelColor()
 *		Get a pixel's color from the icon.
 */
static
short GetPixelColor(short x, short y, QDIconRecord *iconP, Boolean inMask)
{
	char	*p;
	short	color;

	p = (char *) &iconP->iconImage + (iconP->iconWidth * y + x) / 2;
	if (inMask)
	    p += iconP->iconSize;

	asm {
		lda	x
	    lsr	a
        lda	[p]
        bcs	useLo
		and	#0x00f0
        pha
	    lsr	a
	    lsr	a
	    lsr	a
	    lsr	a
		ora	1,s
	    plx
		sta	color
		bra	skip

useLo:	and	#0x000f
		pha
		asl	a
		asl	a
		asl	a
		asl	a
		ora	1,s
	    plx
		sta	color

skip:	
		}

	return color;
}


/*
 *	PickColor()
 *		Get the color the mouse was clicked on.
 */
short PickColor(short ox, short oy, IconData **dataH, Boolean inMask)
{
	short	x, y;

	Pt2Pixel(ox, oy, dataH, inMask, &x, &y);

    return GetPixelColor(x, y, *(**dataH).iconH, inMask);
}


/*	
 *	GetColor()
 *		Get the current color.
 */
short GetColor(short color, short ox, short oy, IconData **dataH, Boolean inMask)
{                      
	short	x, y;
	short	curColor, oldColor;

	Pt2Pixel(ox, oy, dataH, inMask, &x, &y);

    curColor = GetPixelColor(x, y, *(**dataH).iconH, inMask);
	oldColor = GetPixelColor(x, y, *(**dataH).oldIconH, inMask);

	if (inMask) {
		curColor ^= 0x00ff;
		if (!gColorMask)
		    return curColor ? 0x00ff : curColor;
		else
	        return (color == curColor) ? oldColor ^ 0x00ff : color;
	    }
	else			
		return (color == curColor) ? oldColor : color;
}


/*
 *	SetPixelColor()
 *		Set the pixel in an icon.
 */
static
void SetPixelColor(short x, short y, short color, QDIconRecord *iconP, Boolean inMask)
{
	char	*p;

	p = (char *) &iconP->iconImage + (iconP->iconWidth * y + x) / 2;
	if (inMask) {
	    p += iconP->iconSize;
		color ^= 0x00ff;
		}

	asm {
		lda	x
	    lsr	a
		lda	#0x000f
	    bcs	useLo
		lda	#0x00f0
useLo:	tax
		and	color
	    pha
		txa
		eor	#0xffff
		and	[p]
		ora	1,s
        sta	[p]
        plx
	    }
}


static unsigned short	OldX, OldY;

/*
 *	DoBit()
 *		Color one fat bit.
 */
static
void DoBit(short ox, short oy, short color, Boolean inMask, IconData *dataP)
{
	GrafPort		*oPort, **cacheH;
    QDIconRecord	*iconP;
	unsigned short	x, y;
    Rect			baseRect, r;
    Pattern			colorPat;

	iconP = *(dataP->iconH);

	baseRect = inMask ? dataP->fatMRect : dataP->fatIRect;

    if (ox < baseRect.h1 + 4)
		return;
	x = (ox - baseRect.h1 - 4) / 8;
	if (x >= iconP->iconWidth)
	    return;

	if (oy < baseRect.v1 + 2)
	    return;
	y = (oy - baseRect.v1 - 2) / 4;
	if (y >= iconP->iconHeight)
	    return;

	if (x == OldX && y == OldY)
	    return;

	OldX = x;
    OldY = y;

	GetPixelRect(x, y, &r);

    if (inMask && !gColorMask)
	    color ^= 0x00ff;

	MakePattern(color, colorPat);

/* update the cache: */
	oPort = GetPort();

    cacheH = inMask ? dataP->maskCache : dataP->imageCache;
    HLock((Handle) cacheH);
    SetPort(*cacheH);

	FillRect(&r, colorPat);

    SetPort(oPort);
	HUnlock((Handle) cacheH);

/* update the fatbits window: */
	OffsetRect(&r, (inMask ? dataP->fatMRect.h1 : dataP->fatIRect.h1) + 4,
			    dataP->fatIRect.v1 + 2);

	FillRect(&r, colorPat);

/* update the icon itself: */
	SetPixelColor(x, y, color, iconP, inMask);
}


/*
 *	ResizeIt()
 *		Resize the icon.
 */
short ResizeIt(IconData **dataH, unsigned short newWidth, unsigned short newHite)
{
	QDIconRecord	**iconH, *iconP, **tempIconH, *tempIconP;
    char			*p0, *p1, *p2, *p3;
    long			hSize;
    unsigned short	newSize, oldSize, oldWidth, oldHite;
    short			x, y;
	short			err;

    iconH = (**dataH).iconH;

    hSize = GetHandleSize(iconH);
	err = GetNewHandle(hSize, (Handle *) &tempIconH);
	if (err != 0)
		return err;
	HandToHand((Handle) iconH, (Handle) tempIconH, hSize);

    tempIconP = *tempIconH;

	newSize = (newWidth / 2) * newHite;

    SetHandleSize((long) newSize * 2 + sizeof(Word) * 4, (Handle) iconH);
	err = toolerror();
    if (err != 0)
	    return err;

	iconP = *iconH;

/* put in new size info for icon (saving old): */
	oldSize = iconP->iconSize;
	oldHite = iconP->iconHeight;
	oldWidth = iconP->iconWidth;
	iconP->iconSize = newSize;
	iconP->iconHeight = newHite;
	iconP->iconWidth = newWidth;

/* zero the new image and mask: */
	p0 = (char *) &iconP->iconImage;
    p2 = p0 + newSize;
    asm {	
		ldy	newSize
        ldx	#0xffff
        sep	#0x20
		dey
zLoop:	txa
		sta	[p0],y
        inc	a
        sta	[p2],y
		dey
	    bpl	zLoop
        rep	#0x20
		}

/* copy resized image back in: */
	y = newHite < oldHite ? newHite : oldHite;
	while (--y >= 0) {
		p0 = (char *) &tempIconP->iconImage + y * (oldWidth / 2);
        p2 = p0 + oldSize;

        p1 = (char *) &iconP->iconImage + y * (newWidth / 2);
        p3 = p1 + newSize;
		
		x = newWidth < oldWidth ? newWidth : oldWidth;

        asm {
			lda	x
            lsr	a
			tay
			dey
            sep	#0x20
cLoop2:		lda [p0],y
			sta	[p1],y
            lda	[p2],y
	        sta	[p3],y
			dey
			bpl	cLoop2
			rep	#0x20
            }
		}

	DisposeHandle((Handle) tempIconH);

	return 0;
}

 
/*
 *	GrowFat()
 *		Grow the icon image.
 */
static
void GrowFat(IconData **dataH, Point pt)
{
	IconData		*dataP;
	GrafPort		*oPort;
	QDIconRecord	*iconP, **iconH;
	Rect			limits, slop;
	long			newSize;
    short			newHite, newWidth;
	short			h, v;
	short			err;
    
	dataP = *dataH;

/* calculate the growth limits: */
    h = dataP->fatIRect.h1 - dataP->fatIRect.h2;
    v = dataP->fatIRect.v1 - dataP->fatIRect.v2;
						
	limits.h1 = (4 * 4) * 2 + 4 + h;
	limits.v1 = (4 * 4) + 2 + v;

    limits.h2 = (4 * 320) * 2 + 4 + h;
    limits.v2 = (4 * 100) + 2 + v;

/* use the window manager's portRect for the slop rect: */
    oPort = GetPort();
	SetPort(GetWMgrPort());
    GetPortRect(&slop);
    SetPort(oPort);

/* interact: */
	newSize = DragRect(nil, gGrayPattern, pt, &dataP->fatIRect, &limits, &slop, 0x0014);
	asm {
		lda	newSize
		sta	newHite
        lda	newSize+2
        sta	newWidth
	    }
	newHite = (newHite + dataP->fatIRect.v2 - dataP->fatIRect.v1 - 2) / 4;
	newWidth = (newWidth + dataP->fatIRect.h2 - dataP->fatIRect.h1 - 4) / 8;
	newWidth = (newWidth + 1) & 0xfffe;

	iconH = dataP->iconH;
    iconP = *iconH;

    if (newHite != iconP->iconHeight || newWidth != iconP->iconWidth) {
		CopyUndoIcon(dataH);
		err = ResizeIt(dataH, newWidth, newHite);
	    if (err != 0)
	        SysError(err);
		RedrawFat(dataH);
	    }
}


/*
 *	FatClick()
 *		Mouse down in the fatbits window.
 */
void FatClick(EventRecord *event)
{
	IconData	*dataP, **dataH;
	GrafPort	*oPort;
	WindowPtr	wp;
	Point		pt;
	short		x, y;
    short		color, tool;
	Boolean		inImage, inMask;

	oPort = GetPort();
    wp = (WindowPtr) event->wmTaskData;

	dataH = (IconData **) GetPrivateData(wp);
	HLock((Handle) dataH);
    dataP = *dataH;
	tool = dataP->tool;

	pt = event->where;

	StartDrawing(wp);
    GlobalToLocal(&pt);

	inImage = PtInRect(&pt, &dataP->fatIRect);
	inMask = PtInRect(&pt, &dataP->fatMRect);
	if (inImage || inMask) {
		switch (tool) {
	        case kPickerTool:						/* pick color */
		        color = PickColor(pt.h, pt.v, dataH, inMask);
	            if (inMask)
					color ^= 0x00ff;
				dataP->color = color;
				UpdateColor(dataH, color);
				CopyOldIcon(dataH);
				break;
			case kBucketTool:						/* fill */
				FillIcon(dataH, pt);
				break;
			case kShifterTool:						/* shift */
				ShiftIcon(dataH, pt);
		        break;
	    	case kPencilTool:						/* draw */
				CopyUndoIcon(dataH);
	            OldX = OldY = -1;

				color = GetColor(dataP->color, pt.h, pt.v, dataH, inMask);

	            do {
					DoBit(pt.h, pt.v, color, inMask, dataP);

	                GetMouse(&pt);
		            } while (StillDown(0));

				if (inMask)
		            EraseEm(dataH);
				DrawEm(dataH);
				Dirty(dataH);
	            break;
			}
	    }    
	else if (PtInRect(&pt, &dataP->sizeRect)) {
		GrowFat(dataH, pt);
		Dirty(dataH);
	    }

    SetOrigin(0, 0);
	HUnlock((Handle) dataH);
	SetPort(oPort);
}


 /*
  *	RedrawFat2()
  *		Re-cache the fatbits data.
  */
void RedrawFat2(IconData **dataH)
{
	IconData	*dataP;
    short		width, hite;

	CalcFat(dataH);

    dataP = *dataH;

	width = dataP->fatIRect.h2 - dataP->fatIRect.h1 - 8;
	hite = dataP->fatIRect.v2 - dataP->fatIRect.v1 - 4;

    AdjustOffPort(dataP->imageCache, width, hite);
    AdjustOffPort((**dataH).maskCache, width, hite);

	DrawFatImage(dataH);
	DrawFatMask(dataH);
}
    

 /*
  *	RedrawFat()
  *		Redraw the fatbits window.
  */
void RedrawFat(IconData **dataH)
{
	Rect		r;

	HideControl((**dataH).control);
	GetPortRect(&r);
	InvalRect(&r);
	EraseRect(&r);

    RedrawFat2(dataH);
}


/*
 *	EnableUndo()
 *		Enable the undo menu item.
 */
void EnableUndo(void)
{
}


/*
 *	CopyUndoIcon()
 *		Copy current icon to the "undo" icon.
 */
short CopyUndoIcon(IconData **dataH)
{
	IconData		*dataP;
	QDIconRecord	**iconH, **undoH;
	long			size;
	short			err;

    dataP = *dataH;
    iconH = dataP->iconH;
    undoH = dataP->undoIconH;

    size = GetHandleSize((Handle) iconH);
	SetHandleSize(size, (Handle) undoH);
    err = toolerror();
	if (err == 0) {
		HandToHand(iconH, undoH, size);
		err = toolerror();
		if (err == 0)
			EnableUndo();
	    }

	return err;
}


/*
 *	CopyOldIcon()
 *		Copy current icon to the "old" icon.
 */
short CopyOldIcon(IconData **dataH)
{
	IconData		*dataP;
	QDIconRecord	**iconH, **oldH;
	long			size;
	short			err;

    dataP = *dataH;
    iconH = dataP->iconH;
    oldH = dataP->oldIconH;

    size = GetHandleSize((Handle) iconH);
	SetHandleSize(size, (Handle) oldH);
    err = toolerror();
	if (err == 0) {
		HandToHand(iconH, oldH, size);
		err = toolerror();
        }

	return err;
}


static GrafPort	*TheImage;
static short	DestX, DestY;


/*
 *	ShiftDraw2()
 *		Draw an image into the current port.
 */
static
void ShiftDraw2(short x, short y, GrafPort *image)
{
	x += DestX + 4;
	y = (y & 0xfffc) + DestY + 2;
	PPToPort(&image->portInfo, &image->portRect, x, y, notXOR);
}   


#pragma databank	1
#pragma toolparms	1
/*
 *	ShiftDraw()
 *		Draw the fat image being dragged.
 */
static
void ShiftDraw(short deltaY, short deltaX, short parm)
{
	static short	L00C0, L60B4;
	short			x, y;

	x = L00C0;
	if (parm & 1)
		++x;
	L60B4 = x & 1;

	if (parm & 1)
	    if ((L00C0 & 1) != L60B4) {
			++L00C0;
			ShiftDraw2(deltaX, deltaY, TheImage);
	        }
}
#pragma databank	0
#pragma toolparms	0


/*
 *	ShiftIt()
 *		Shift the icon image & mask according to deltaX, deltaY.
 */
static
void ShiftIt(IconData **dataH, short deltaX, short deltaY)
{
	unsigned short	iSize, iHite, iWidth, iWBytes;
	short			sLine, dLine;
	QDIconRecord	*iconP;
	char			*ptr0, *ptr1, *ptr2, *ptr3;

    iconP = *((**dataH).iconH);

	iSize = iconP->iconSize;
	iHite = iconP->iconHeight;
	iWidth = iconP->iconWidth;
	iWBytes = (iWidth - 1) / 2 + 1;

/* move each line, shifting it into place: */
	if (deltaY < 0) {
		dLine = 0;
		sLine = -deltaY;
        if (sLine >= iHite)
	        return;
	    }
	else {
		dLine = iHite - 1;
        sLine = dLine - deltaY;
        if (sLine < 0)
	        return;
		}

	do {
	    ptr0 = (char *) &iconP->iconImage + (sLine * (iWidth / 2));
		ptr2 = ptr0 + iSize;

	    ptr1 = (char *) &iconP->iconImage + (dLine * (iWidth / 2));
		ptr3 = ptr1 + iSize;

		asm {
			ldx	sLine
            bmi	clrRow
            cpx	iHite
	        bcs	clrRow

			lda	deltaX		    /* first shift the row horizontally */
			beq	noShift
			bpl	goRight
			eor	#0xffff
/*			inc	a			*/
			pha
            tax
			sep	#0x20
shlLoop1:	ldy	iWBytes
			dey
			sec
loop1:		lda	[ptr0],y
			rol	a
			sta	[ptr0],y
			dey
			bpl	loop1
			dex
	        bne	shlLoop1

			plx					/* shift the mask left X bits */
shlLoop2:	ldy	iWBytes
		    dey
	        clc
loop2:		lda	[ptr2],y
			rol	a
			sta	[ptr2],y
            dey
            bpl	loop2
			dex
            bne	shlLoop2
            bra	noShift

goRight:	ldx	deltaX		    /* shift the image right X bits */
			sep	#0x20
shrLoop1:	phx
			ldy	#0
			ldx	iWBytes
	        sec
loop3:	    lda	[ptr0],y
			ror	a
            sta	[ptr0],y
	        iny
            dex
	        bne	loop3
			plx
	        dex
	        bne	shrLoop1

	        ldx	deltaX			/* shift the mask right X bits */
shrLoop2:	phx
			ldy	#0
            ldx	iWBytes
	        clc
loop4:		lda	[ptr2],y
			ror	a
	        sta	[ptr2],y
	        iny
            dex
	        bne	loop4
            plx
            dex
	        bne	shrLoop2
            bra	noShift

clrRow:		sep	#0x20
			ldy	iWBytes		    /* clear remaining rows */
			dey
            ldx	#0xffff
clrLoop:	txa
			sta	[ptr1],y
            inc	a
			sta	[ptr3],y
            dey
			bpl	clrLoop
			bra	cleared

noShift:	sep	#0x20
			ldy	iWBytes			/* now copy the newly shifted row into it's new place */
			dey
cLoop:		lda	[ptr0],y
			sta	[ptr1],y
            lda	[ptr2],y
            sta	[ptr3],y
            dey
            bpl	cLoop

cleared:	rep	#0x20
			}

		} while (deltaY < 0 ? (++sLine, ++dLine < iHite) : (--sLine, --dLine >= 0));
}


/*
 *	ShiftIcon()
 *		Interactively shift the icon image and mask.
 */
static
void ShiftIcon(IconData **dataH, Point pt)
{
	Rect		dragRect, r;
	IconData	*dataP;
	Region		**oClip;
	GrafPort	**image;
    long		result;
    short		deltaX, deltaY;

    dataP = *dataH;

	dragRect = r = dataP->fatIRect;

	DestX = r.h1;
    DestY = r.v1;
    image = dataP->imageCache;
	HLock((Handle) image);
	TheImage = *image;

	InsetRect(&r, r.h1 - r.h2, r.v1 - r.v2);
	InsetRect(&dragRect, 2, 2);

    oClip = NewRgn();				/* clip to the drag rect */
	GetClip(oClip);
    ClipRect(&dragRect);

    EraseRect(&dragRect);			/* erase the image first */

    result = DragRect(ShiftDraw, gGrayPattern, pt, &dragRect, &r, &r, 0x0888);
	asm {
		lda	result
		sta	deltaY
		lda	result+2
		sta	deltaX
	    }

	ShiftDraw2(deltaX, deltaY, TheImage);	/* redraw the icon image */
	HUnlock((Handle) image);
	SetClip(oClip);					/* restore the original clip rgn */

    deltaX = deltaX >= 0 ? deltaX / 2 : deltaX / 2 - 1;
	deltaY = deltaY >= 0 ? deltaY / 4 : deltaY / 4 - 1;

    if (deltaX != 0 || deltaY != 0) {
		CopyUndoIcon(dataH);
		ShiftIt(dataH, deltaX, deltaY);
		Dirty(dataH);
		RedrawFatbits(dataH, true);
		DrawFatImage(dataH);
		}
}
