#define	LIBQDISPLAY_CORE
#include "../include/libqdisplay.h"

/* CLIPPOLY.c - LOTS OF WORK TO DO :) */

static point_3d clipPoints[16], *clipList1[40], *clipList2[40];

#define X p[0]
#define Y p[1]
#define Z p[2]

static void intersect(point_3d * out, point_3d * a, point_3d * b, float where)
{
  /* intersection occurs 'where' % along the line from a to b */
  out->X = a->X + (b->X - a->X) * where;
  out->Y = a->Y + (b->Y - a->Y) * where;
  out->Z = a->Z + (b->Z - a->Z) * where;

  transform_rotated_point(out);
}

/* compute 'where' for various clip planes */
static float left_loc(point_3d * a, point_3d * b)
{
  return -(a->Z + a->X * clipScaleX) / ((b->X - a->X) * clipScaleX + b->Z - a->Z);
}

static float right_loc(point_3d * a, point_3d * b)
{
  return (a->Z - a->X * clipScaleX) / ((b->X - a->X) * clipScaleX - b->Z + a->Z);
}

static float top_loc(point_3d * a, point_3d * b)
{
  return (a->Z - a->Y * clipScaleY) / ((b->Y - a->Y) * clipScaleY - b->Z + a->Z);
}

static float bottom_loc(point_3d * a, point_3d * b)
{
  return -(a->Z + a->Y * clipScaleY) / ((b->Y - a->Y) * clipScaleY + b->Z - a->Z);
}

/* clip the polygon to each of the view frustrum planes */
int clip_poly(int n, point_3d ** vl, int codes_or, point_3d *** out_vl)
{
  int i, j, k, p = 0;						/* p = index into temporary point pool */

  point_3d **cur;

  if (codes_or & CC_OFF_LEFT) {
    cur = clipList1;
    k = 0;
    j = n - 1;
    for (i = 0; i < n; ++i) {
      /*
       * process edge from j..i
       * if j is inside, add it
       */
      if (!(vl[j]->ccodes & CC_OFF_LEFT))
	cur[k++] = vl[j];
      /* if it crosses, add the intersection point */
      if ((vl[j]->ccodes ^ vl[i]->ccodes) & CC_OFF_LEFT) {
	intersect(&clipPoints[p], vl[i], vl[j], left_loc(vl[i], vl[j]));
	cur[k++] = &clipPoints[p++];
      }
      j = i;
    }
    /* move output list to be input */
    n = k;
    vl = cur;
  }

  if (codes_or & CC_OFF_RIGHT) {
    cur = (vl == clipList1) ? clipList2 : clipList1;
    k = 0;
    j = n - 1;
    for (i = 0; i < n; ++i) {
      if (!(vl[j]->ccodes & CC_OFF_RIGHT))
	cur[k++] = vl[j];
      if ((vl[j]->ccodes ^ vl[i]->ccodes) & CC_OFF_RIGHT) {
	intersect(&clipPoints[p], vl[i], vl[j], right_loc(vl[i], vl[j]));
	cur[k++] = &clipPoints[p++];
      }
      j = i;
    }
    n = k;
    vl = cur;
  }
  if (codes_or & CC_OFF_TOP) {
    cur = (vl == clipList1) ? clipList2 : clipList1;
    k = 0;
    j = n - 1;
    for (i = 0; i < n; ++i) {
      if (!(vl[j]->ccodes & CC_OFF_TOP))
	cur[k++] = vl[j];
      if ((vl[j]->ccodes ^ vl[i]->ccodes) & CC_OFF_TOP) {
	intersect(&clipPoints[p], vl[i], vl[j], top_loc(vl[i], vl[j]));
	cur[k++] = &clipPoints[p++];
      }
      j = i;
    }
    n = k;
    vl = cur;
  }
  if (codes_or & CC_OFF_BOT) {
    cur = (vl == clipList1) ? clipList2 : clipList1;
    k = 0;
    j = n - 1;
    for (i = 0; i < n; ++i) {
      if (!(vl[j]->ccodes & CC_OFF_BOT))
	cur[k++] = vl[j];
      if ((vl[j]->ccodes ^ vl[i]->ccodes) & CC_OFF_BOT) {
	intersect(&clipPoints[p], vl[i], vl[j], bottom_loc(vl[i], vl[j]));
	cur[k++] = &clipPoints[p++];
      }
      j = i;
    }
    n = k;
    vl = cur;
  }

  for (i = 0; i < n; ++i)
    if (vl[i]->ccodes & CC_BEHIND)
      return 0;

  *out_vl = vl;
  return n;
}
