/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1998  Riley Rainey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundaation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

#include "Vlib.h"
#include <math.h>

#define mag(x,y,z) sqrt ( (x) * (x) + (y) * (y) + (z) * (z) )

struct _euler {
	double    yaw;
	double    pitch;
};

struct entry {
	int       id;
	double    d;
	struct entry *next;
};

/*
 *  Views corresponding to each of the eight defined major aspects
 */

static struct _euler view[] =
{
	{DEGtoRAD(90.0), DEGtoRAD(0.0)},	/* 0 - right */
	{DEGtoRAD(-90.0), DEGtoRAD(0.0)},	/* 1 - left */
	{DEGtoRAD(45.0), DEGtoRAD(45.0)},	/* 2 - front right bottom */
	{DEGtoRAD(45.0), DEGtoRAD(-45.0)},	/* 3 - front right top    */
	{DEGtoRAD(-45.0), DEGtoRAD(45.0)},	/* 4 - front left bottom  */
	{DEGtoRAD(-45.0), DEGtoRAD(-45.0)},		/* 5 - front left top     */
	{DEGtoRAD(135.0), DEGtoRAD(45.0)},	/* 6 - aft right bottom   */
	{DEGtoRAD(135.0), DEGtoRAD(-45.0)},		/* 7 - aft right top      */
	{DEGtoRAD(-135.0), DEGtoRAD(45.0)},		/* 8 - aft left bottom    */
	{DEGtoRAD(-135.0), DEGtoRAD(-45.0)},	/* 9 - aft left top       */
};

static char *aspect_name[] =
{
	"left",
	"right",
	"front right bottom",
	"front right top",
	"front left bottom",
	"front left top",
	"aft right bottom",
	"aft right top",
	"aft left bottom",
	"aft left top"
};

void
VComputePolygonOrdering(VObject * obj)
{
	register int i, j, k, inserted;
	register double d, dn;
	VMatrix   mtx;
	VPoint    pt1, pt2;
	VPolygon **p;
	struct entry ent[VmaxVP], *head, *e, *last_e;

	obj->order = (unsigned short *)
		Vmalloc(sizeof(unsigned short) * NUM_ASPECTS * obj->numPolys);

	for (i = 0; i < NUM_ASPECTS; ++i) {

		VIdentMatrix(&mtx);
		VRotate(&mtx, YRotation, -view[i].pitch);
		VRotate(&mtx, ZRotation, view[i].yaw);
		pt1.x = obj->extent * 10.0;
		pt1.y = pt1.z = 0.0;
		VTransform(&pt1, &mtx, &pt2);
		head = (struct entry *) NULL;
		for (j = 0, p = obj->polygon; j < obj->numPolys; ++j) {

/*
 *  Compute the distance to the farthest vertex on this polygon
 */

			dn = mag(p[j]->vertex[0].x - pt2.x,
					 p[j]->vertex[0].y - pt2.y,
					 p[j]->vertex[0].z - pt2.z);

			for (k = 1; k < p[j]->numVtces; ++k) {
				d = mag(
						   p[j]->vertex[k].x - pt2.x,
						   p[j]->vertex[k].y - pt2.y,
						   p[j]->vertex[k].z - pt2.z);
				if (d > dn) {
					dn = d;
				}
			}

			ent[j].id = j;
			ent[j].d = dn;

/*
 *  Insert this entry into the descending sorted list of polygons
 */

			if (!head) {
				ent[j].next = head;
				head = &ent[j];
			}
			else {
				last_e = (struct entry *) NULL;
				inserted = 0;
				for (e = head; e; e = e->next) {
					if (e->d < ent[j].d) {
						if (last_e) {
							ent[j].next = last_e->next;
							last_e->next = &ent[j];
						}
						else {
							ent[j].next = e;
							head = &ent[j];
						}
						inserted = 1;
						break;
					}
					last_e = e;
				}
				if (inserted == 0) {
					last_e->next = &ent[j];
					ent[j].next = (struct entry *) NULL;

				}
			}
		}

/*
 *  Copy this list as the hints for this quadrant.
 */

		k = obj->numPolys * i;
		for (j = 0, e = head; j < obj->numPolys; ++j, e = e->next) {
			obj->order[k + j] = e->id;
		}

	}
}

/*
 *  Determine if an object should be ordered.  Objects that are not made of
 *  entirely the same color, or that have colored back-sides probably should
 *  be ordered.
 */

int
VObjectNeedsOrdering(VObject * obj)
{
	VPolygon **p;
	VColor   *c;
	int       i;

	if (obj->order || obj->numPolys == 0) {
		return 0;
	}

	c = obj->polygon[0]->color;

	for (i = 0, p = obj->polygon; i < obj->numPolys; ++i) {
#ifdef notdef
		if (p[i]->color != c) {
			return 1;
		}
#endif
		if (p[i]->backColor) {
			return 1;
		}
	}

	return 0;
}

int
VComputeObjectAspect(VObject * obj, VPoint * loc)
{
	register int q;
	register double a, b, c, m;

	m = mag(loc->x, loc->y, loc->z);

	a = VDotProd(loc, &obj->xaxis);
	b = VDotProd(loc, &obj->yaxis) / m;
	c = VDotProd(loc, &obj->zaxis);

/*
 *  If the absolute angle between the Y axis and viewing vector is less than 30
 *  degrees, then it is either a right or left aspect.
 */

	if (b > 0.866) {
		return 1;
	}
	else if (b < -0.866) {
		return 0;
	}

	if (a < 0.0) {
/* front */
		if (b < 0.0) {
/* front right */
			if (c < 0.0) {
/* front right bottom */
				q = 2;
			}
			else {
/* front right top */
				q = 3;
			}
		}
		else {
/* front left */
			if (c < 0.0) {
/* front left bottom */
				q = 4;
			}
			else {
/* front left top */
				q = 5;
			}
		}
	}
	else {
/* aft */
		if (b < 0.0) {
/* aft right */
			if (c < 0.0) {
/* aft right bottom */
				q = 6;
			}
			else {
/* aft right top */
				q = 7;
			}
		}
		else {
/* aft left */
			if (c < 0.0) {
/* aft left bottom */
				q = 8;
			}
			else {
/* aft left top */
				q = 9;
			}
		}
	}

	return q;
}

char     *
VGetAspectName(int aspect)
{
	return aspect_name[aspect];
}
