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

#include <exec/memory.h>
#include <dos/dos.h>
#include <devices/timer.h>
#include <utility/hooks.h>

#include "graffiti.h"

#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/timer.h>

void __regargs __chkabort (void);
void __regargs __chkabort (void)
{
}

extern WORD fastsin (WORD, WORD);
extern WORD fastcos (WORD, WORD);

/* #define fastsqrt(val) ((int) sqrt ((double)(val))) */

long fastsqrt (long a)
{
    static long last=1,x=1;
    long xn;
    long t;

    if (!a)
	return 0;

    t = (a+1)>>1;
    xn = (last > a) ? t : x;

    t=0;

    do
    {
	x=xn;
	xn=(a/x+x+1) >> 1;
    }
    while (x!=xn && t++<16);

    last = x;

    return x;
}

struct SurfDesc
{
    UBYTE r,g,b;  /* color of the surface */
    WORD x,y,z;   /* Direction vector */
};

#define LEN_LS	    (1.732050808)

#if 0 /* Moiree */
UBYTE GetSurfColor (struct GraffitiHandle * gh, struct SurfDesc * sd)
{
    LONG phi;
    UBYTE r,g,b;
    register LONG x,y,z;

    x = sd->x << 8;
    y = sd->y << 8;
    z = sd->z << 8;

    phi = (-x - y + z) /
	    ((fastsqrt(x*x + y*y + z*z)
	    *1732050)/1000000);

    r = (sd->r*phi) >> 8;
    g = (sd->g*phi) >> 8;
    b = (sd->b*phi) >> 8;

    return Graffiti_FindBestMatch (gh, r,g,b);
} /* GetSurfColor */
#endif

UBYTE GetSurfColor (struct GraffitiHandle * gh, struct SurfDesc * sd)
{
    LONG phi;
    UBYTE r,g,b;
    register LONG x,y,z;

    x = sd->x;
    y = sd->y;
    z = sd->z;

    phi = ((-x - y + z)<<8) /
	    ((fastsqrt(x*x + y*y + z*z)*173)/100);

    phi = 402-phi-((phi*phi*phi)>>16)/6; /* arccos */

#define AMBIENT 100

    phi = (256 - phi) + AMBIENT;

    /* phi = (phi * 9) / 6; */

    if (phi > 255)
    {
	r=g=b=255;
    }
    else
    {
	if (phi < AMBIENT)
	    phi = AMBIENT;

	r = (sd->r*phi) >> 8;
	g = (sd->g*phi) >> 8;
	b = (sd->b*phi) >> 8;
    }

    return Graffiti_FindBestMatch (gh, r,g,b);
} /* GetSurfColor */

void Rect (struct GraffitiHandle * gh, int x1, int y1, int x2, int y2, int z)
{
    int x, y;

    z <<= 4;

    for (y=y1; y<y2; y++)
	for (x=x1; x<x2; x++)
	    Graffiti_SetPixel3D (gh, x, y, z);
} /* Rect */

void Cyl (struct GraffitiHandle * gh, int x1, int y1,
    int x2, int y2, int z1,
    int r, int rot, int gruen, int blau)
{
    int x, y, z, r2, r3, t;
    UBYTE col;
    struct SurfDesc sd;

    r2 = r*r;
    r3 = r<<4;

    z1 <<= 4;

    sd.r = rot;
    sd.g = gruen;
    sd.b = blau;

    if (y1==y2)
    {
	sd.x = -r;

	for (y=-r; y<=+r; y++)
	{
	    z = fastsqrt((r2 - y*y)<<8);

	    sd.y = y;
	    sd.z = z>>4;

	    /* col = Graffiti_FindBestMatch (gh, rot*z/r3, gruen*z/r3, blau*z/r3); */
	    col = GetSurfColor (gh, &sd);

	    z += z1;
	    t = y1+y;

	    for (x=x1; x<=x2; x++)
	    {
		Graffiti_SetPixel3DColor (gh, x, t, z, col);
	    }
	}
    }
    else
    {
	sd.y = -r;

	for (x=-r; x<=+r; x++)
	{
	    z = fastsqrt((r2 - x*x)<<8);

	    sd.x = x;
	    sd.z = z>>4;

	    /* col = Graffiti_FindBestMatch (gh, rot*z/r3, gruen*z/r3, blau*z/r3); */
	    col = GetSurfColor (gh, &sd);

	    z += z1;
	    t = x1+x;

	    for (y=y1; y<=y2; y++)
	    {
		Graffiti_SetPixel3DColor (gh, t, y, z, col);
	    }
	}
    }
} /* Cyl */

void Ball (struct GraffitiHandle * gh, int cx, int cy, int cz, int r,
    int rot, int gruen, int blau)
{
    int x, y, z, r2, y2, r3;
    int xmin, xmax, ymin, ymax;
    UBYTE col;
    struct SurfDesc sd;

    r2 = r*r;
    r3 = r<<4;

    cz <<= 4;

    xmin = ymin = -r;
    xmax = ymax = +r;

    if (ymin + cy < 0)
	ymin = -cy;
    if (xmin + cx < 0)
	xmin = -cx;

    sd.r = rot;
    sd.g = gruen;
    sd.b = blau;

    for (y=ymin; y<=ymax; y++)
    {
	y2 = y + cy;
	sd.y = y;

	for (x=xmin; x<=xmax; x++)
	{
	    sd.x = x;
	    z = x*x + y*y;

	    if (z <= r2)
	    {
		z = fastsqrt ((r2 - z)<<8);
		/* col = Graffiti_FindBestMatch (gh, rot*z/r3, gruen*z/r3, blau*z/r3); */
		sd.z = z>>4;
		col = GetSurfColor (gh, &sd);
		Graffiti_SetPixel3DColor (gh, x+cx, y2, z+cz, col);
	    }
	}
    }
} /* Ball */

int main (int argc, char ** argv)
{
    struct GraffitiHandle * gh;
    struct IntuiMessage * im;
    ULONG width;
    int mode;
    int white;
    UBYTE col;

    /* Init card */
    if (argc == 1)
    {
	gh = Graffiti_Init (
	    GTI_3D, TRUE,
	    TAG_END);
    }
    else
    {
	mode = atoi (argv[1]);

	if (mode < 0 || mode > 1)
	{
	    fprintf (stderr, "Mode must be between 0 or 1\n");
	    return 10;
	}

	gh = Graffiti_Init (
	    GTI_RESOLUTION, mode ? GRAFFITI_RES_640 : GRAFFITI_RES_320,
	    GTI_3D, TRUE,
	    TAG_END);
    }

    Graffiti_InstallDefaultPalette (gh);

    Graffiti_GetAttr (gh, GTI_WIDTH, &width);

    white = Graffiti_FindBestMatch (gh, 255,255,255);

    Graffiti_SetFG (gh, white);
    Rect (gh, 30,40, 270,110, 30);

    Cyl (gh, 20,128, 300,128, 0, 40, 255,255,255);
    Cyl (gh, 160,28, 160,238, 0, 40, 0,255,0);
    Cyl (gh, 68,180, 220,180, 20, 10, 255,0,0);

    Ball (gh, 270,50,50, 30, 0,0,255);
    Ball (gh, 70,80,-50, 110, 0,0,255);

    Ball (gh,  30,230,20, 30, 0,0,255);
    Ball (gh,  80,230,20, 30, 0,0,255);
    Ball (gh, 130,230,20, 30, 0,0,255);
    Ball (gh, 180,230,20, 30, 0,0,255);
    Ball (gh, 230,230,20, 30, 0,0,255);

#if 0
    col = Graffiti_FindBestMatch (gh, 255,0,0);
    Graffiti_SetFG (gh, col);
    Rect (gh, 10, 20, 60, 25, 0x80);

    col = Graffiti_FindBestMatch (gh, 0,255,0);
    Graffiti_SetFG (gh, col);
    Rect (gh, 20, 10, 25, 40, 0x40);

    col = Graffiti_FindBestMatch (gh, 0,0,255);
    Graffiti_SetFG (gh, col);
    Rect (gh, 40, 10, 45, 40, 0xc0);
#endif

    im = Graffiti_WaitEvent (gh);

    if (im)
	ReplyMsg ((struct Message *)im);

    /* Exit demo */
    Graffiti_Exit (gh);

    return 0;
} /* main */
