#include <stdio.h>
#include <string.h>

#include <proto/utility.h>

#include "graffiti_render.h"

#define PointIsVisible(gd,x,y) \
	((x) >= (gd)->ClipLeft && (x) <= (gd)->ClipRight && \
	(y) >= (gd)->ClipTop && (y) <= (gd)->ClipBottom)

#define PointIsVisible3D(gd,x,y,z) \
	(PointIsVisible(gd,x,y) && \
	(!(gd)->ZBuffer || z >= (gd)->ZBuffer[(x) + (y)*(gd)->Width]))

void Graffiti_SetPixel_1 (struct GraffitiData * gd, WORD x, WORD y)
{
    if (!PointIsVisible (gd, x, y))
	return;

    gd->Plane[0][x + y*gd->BPR] = gd->gh.FG;
} /* Graffiti_SetPixel_1 */

void Graffiti_SetPixel_2 (struct GraffitiData * gd, WORD x, WORD y)
{
    if (!PointIsVisible (gd, x, y))
	return;

    gd->Plane[x & 1][(x>>1) + y*gd->BPR] = gd->gh.FG;

} /* Graffiti_SetPixel_2 */

void Graffiti_SetPixel_4 (struct GraffitiData * gd, WORD x, WORD y)
{
    if (!PointIsVisible (gd, x, y))
	return;

    gd->Plane[x & 3][(x>>2) + y*gd->BPR] = gd->gh.FG;

} /* Graffiti_SetPixel_4 */

void Graffiti_SetPixelColor_1 (struct GraffitiData * gd, WORD x, WORD y, UBYTE col)
{
    if (!PointIsVisible (gd, x, y))
	return;

    gd->Plane[0][x + y*gd->BPR] = col;
} /* Graffiti_SetPixelColor_1 */

void Graffiti_SetPixelColor_2 (struct GraffitiData * gd, WORD x, WORD y, UBYTE col)
{
    if (!PointIsVisible (gd, x, y))
	return;

    gd->Plane[x & 1][(x>>1) + y*gd->BPR] = col;

} /* Graffiti_SetPixelColor_2 */

void Graffiti_SetPixelColor_4 (struct GraffitiData * gd, WORD x, WORD y, UBYTE col)
{
    if (!PointIsVisible (gd, x, y))
	return;

    gd->Plane[x & 3][(x>>2) + y*gd->BPR] = col;

} /* Graffiti_SetPixelColor_4 */

void Graffiti_DrawPixel_x (struct GraffitiData * gd, WORD x, WORD y,
	struct GraffitiHookData * ghd)
{
    if (!PointIsVisible (gd, x, y))
	return;

    if (gd->gh.FilterList)
    {
	struct Hook * hook;
#define L(l)        ((struct List *)(l))
#define N(n)        ((struct Node *)(n))
#define ForeachListNode(l,n) \
	for (n=(void*)(L(l)->lh_Head); N(n)->ln_Succ; n=(void *)(N(n)->ln_Succ))

	ghd->x = x;
	ghd->y = y;

	ForeachListNode (gd->gh.FilterList, hook)
	    CallHookPkt (hook, gd, ghd);
    }
    else
	Graffiti_SetPixelColor (GH(gd), x, y, ghd->col);

} /* Graffiti_DrawPixel_x */

UBYTE Graffiti_GetPixel_1 (struct GraffitiData * gd, WORD x, WORD y)
{
    if (!PointIsVisible (gd, x, y))
	return 0;

    return gd->Plane[0][x + y*gd->BPR];
} /* Graffiti_GetPixel_1 */

UBYTE Graffiti_GetPixel_2 (struct GraffitiData * gd, WORD x, WORD y)
{
    if (!PointIsVisible (gd, x, y))
	return 0;

    return gd->Plane[x & 1][(x>>1) + y*gd->BPR];
} /* Graffiti_GetPixel_2 */

UBYTE Graffiti_GetPixel_4 (struct GraffitiData * gd, WORD x, WORD y)
{
    if (!PointIsVisible (gd, x, y))
	return 0;

    return gd->Plane[x & 3][(x>>2) + y*gd->BPR];
} /* Graffiti_GetPixel_4 */

/* This is an optimized version of the routine outlined in Dr. Dobb's Journal,
    July 1990 on page 98. The original code did assign 0 to code before
    the first if, which is not neccessary if the point is below or above
    the bounding box. */
#define OUTCODE(gd,x,y,code) \
    if ((y) > (gd)->ClipBottom) code = 1; \
    else if ((y) < (gd)->ClipTop) code = 2; \
    else code = 0;			       \
    if ((x) > (gd)->ClipRight) code |= 4; \
    else if ((x) < (gd)->ClipLeft) code |= 8

void Graffiti_DrawLine_x (struct GraffitiData * gd, WORD x1, WORD y1,
    WORD x2, WORD y2)
{
    LONG dx, dy, dist;
    LONG dx2, dy2;
    WORD x, y, xstep, ystep, n;
    UBYTE outcode1, outcode2;

/* printf ("Cliprect = %d,%d - %d,%d\n", gd->ClipLeft, gd->ClipTop, gd->ClipRight, gd->ClipBottom); */

    OUTCODE (gd,x1,y1,outcode1);
    OUTCODE (gd,x2,y2,outcode2);

    /* The line is completely invisible if both endpoints lie on the same
       side outside of the visible rectangle */
    if (outcode1 & outcode2)
    {
	printf ("   Invisible1: %d - %d\n", outcode1, outcode2);
	return;
    }

    dx = x2 - x1;
    dy = y2 - y1;

    dx2 = (dx+1) >> 1;
    dy2 = (dy+1) >> 1;

#define CLIPSIDE(code1,bit,c1,c2,d1,d2,clip1,clip2,code2,clip3,code3) \
	if ((code1) & bit) /* top */                                  \
	{							      \
	    c1 += (d1 * (clip1 - c2) + d2 ## 2) / d2;                 \
	    c2 = clip1; 					      \
								      \
	    if (c1 > clip2) code1 = code2;                            \
	    else if (c1 < clip3) code1 = code3;                       \
	    else code1 = 0;					      \
	}

/*
printf ("   Clip " #code "=%d," #x "=%d," #y "=%d ... ", code,x,y); \
printf (#code "=%d," #x "=%d," #y "=%d\n", code,x,y); \
*/

#define CLIPPOINT(code,x,y) \
    n=6; \
    while (code&&--n)       \
    {			    \
	CLIPSIDE(code,1,x,y,dx,dy,gd->ClipBottom,gd->ClipRight,4,gd->ClipLeft,8) \
	else CLIPSIDE(code,2,x,y,dx,dy,gd->ClipTop,gd->ClipRight,4,gd->ClipLeft,8) \
	else CLIPSIDE(code,4,y,x,dy,dx,gd->ClipRight,gd->ClipBottom,1,gd->ClipTop,2)  \
	else CLIPSIDE(code,8,y,x,dy,dx,gd->ClipLeft,gd->ClipBottom,1,gd->ClipTop,2)   \
								       \
	if (outcode1 & outcode2)                                       \
	{ \
	    printf ("   Invisible " #code ": %d - %d\n", outcode1, outcode2); \
	    return;						       \
	} \
    }

/* printf ("In Line (%d,%d) - (%d,%d)\n", x1,y1,x2,y2); */
    CLIPPOINT(outcode1,x1,y1)
if (!n)
    printf ("    Problem clipping 1: outcode=%d, x1=%d, y1=%d\n", outcode1,x1,y1);
    CLIPPOINT(outcode2,x2,y2)
if (!n)
    printf ("    Problem clipping 2: outcode=%d, x2=%d, y2=%d\n", outcode2,x2,y2);

/* printf ("   Clip (%d,%d) - (%d,%d)\n", x1,y1,x2,y2); */

    x = x1;
    y = y1;

    dx = x2 - x1;

    if (dx < 0)
	dx = -dx;

    dy = y2 - y1;

    if (dy < 0)
	dy = -dy;

    dist = dx - dy;

    n = (dx > dy) ? dx : dy;

    xstep = (x2 > x1) ? 1 : -1;
    ystep = (y2 > y1) ? 1 : -1;

#define STEPLINE	    \
	n --;		    \
	if (!n)             \
	    break;	    \
			    \
	if (dist >= 0)      \
	{		    \
	    x += xstep;     \
	    dist -= dy;     \
	}		    \
	if (dist < 0)       \
	{		    \
	    y += ystep;     \
	    dist += dx;     \
	}

    switch (gd->Depth)
    {
    case 1:
	for (;;)
	{
	    gd->Plane[0][x + y*gd->BPR] = gd->gh.FG;

	    STEPLINE
	}
	break;

    case 2:
	for (;;)
	{
	    gd->Plane[x & 1][(x>>1) + y*gd->BPR] = gd->gh.FG;

	    STEPLINE
	}
	break;

    case 4:
	for (;;)
	{
	    gd->Plane[x & 3][(x>>2) + y*gd->BPR] = gd->gh.FG;
	    /* Graffiti_SetPixel (GH(gd), x, y); */

	    STEPLINE
	}
	break;

    }
} /* Graffiti_DrawLine_x */

void Graffiti_SetPixel3D_1 (struct GraffitiData * gd, WORD x, WORD y, WORD z)
{
    if (!PointIsVisible3D (gd, x, y, z))
	return;

    if (gd->ZBuffer)
	gd->ZBuffer[x + y*gd->Width] = z;

    gd->Plane[0][x + y*gd->BPR] = gd->gh.FG;

} /* Graffiti_SetPixel3D_1 */

void Graffiti_SetPixel3D_2 (struct GraffitiData * gd, WORD x, WORD y, WORD z)
{
    if (!PointIsVisible3D (gd, x, y, z))
	return;

    if (gd->ZBuffer)
	gd->ZBuffer[x + y*gd->Width] = z;

    gd->Plane[x & 1][(x>>1) + y*gd->BPR] = gd->gh.FG;
} /* Graffiti_SetPixel3D_2 */

void Graffiti_SetPixel3D_4 (struct GraffitiData * gd, WORD x, WORD y, WORD z)
{
    if (!PointIsVisible3D (gd, x, y, z))
	return;

    if (gd->ZBuffer)
	gd->ZBuffer[x + y*gd->Width] = z;

    gd->Plane[x & 3][(x>>2) + y*gd->BPR] = gd->gh.FG;

} /* Graffiti_SetPixel3D_4 */

void Graffiti_SetPixel3DColor_1 (struct GraffitiData * gd, WORD x, WORD y, WORD z, UBYTE col)
{
    if (!PointIsVisible3D (gd, x, y, z))
	return;

    if (gd->ZBuffer)
	gd->ZBuffer[x + y*gd->Width] = z;

    gd->Plane[0][x + y*gd->BPR] = col;

} /* Graffiti_SetPixel3DColor_1 */

void Graffiti_SetPixel3DColor_2 (struct GraffitiData * gd, WORD x, WORD y, WORD z, UBYTE col)
{
    if (!PointIsVisible3D (gd, x, y, z))
	return;

    if (gd->ZBuffer)
	gd->ZBuffer[x + y*gd->Width] = z;

    gd->Plane[x & 1][(x>>1) + y*gd->BPR] = col;

} /* Graffiti_SetPixel3DColor_2 */

void Graffiti_SetPixel3DColor_4 (struct GraffitiData * gd, WORD x, WORD y, WORD z, UBYTE col)
{
    if (!PointIsVisible3D (gd, x, y, z))
	return;

    if (gd->ZBuffer)
	gd->ZBuffer[x + y*gd->Width] = z;

    gd->Plane[x & 3][(x>>2) + y*gd->BPR] = col;

} /* Graffiti_SetPixel3DColor_4 */

void Graffiti_DrawPixel3D_x (struct GraffitiData * gd, WORD x, WORD y, WORD z,
	struct GraffitiHookData * ghd)
{
    if (!PointIsVisible3D (gd, x, y, z))
	return;

    if (gd->ZBuffer)
	gd->ZBuffer[x + y*gd->Width] = z;

    if (gd->gh.FilterList)
    {
	struct Hook * hook;
#define L(l)        ((struct List *)(l))
#define N(n)        ((struct Node *)(n))
#define ForeachListNode(l,n) \
	for (n=(void*)(L(l)->lh_Head); N(n)->ln_Succ; n=(void *)(N(n)->ln_Succ))

	ghd->x = x;
	ghd->y = y;
	ghd->z = z;

	ForeachListNode (gd->gh.FilterList, hook)
	    CallHookPkt (hook, gd, ghd);
    }
    else
	Graffiti_SetPixel3DColor (GH(gd), x, y, z, ghd->col);

} /* Graffiti_DrawPixel_x */


