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

/*
 * clipping-hulls
 *
 *  hull[0] drawing-hull
 *  hull[1] player-hull		((-16,-16,-24),(16,16,32))
 *  hull[2] shambler-hull	((-32,-32,-24),(32,32,64))
 */

static bool point_plane_test(register vec3_t point, register struct dplane_t *plane)
{
#if (PLANE_X == 0) && (PLANE_Y == 1) && (PLANE_Z == 2)
  vec_t val;

  if (plane->type < PLANE_Z)
    val = point[plane->type];
  else
    val = DotProduct(plane->normal, point);

  return val < plane->dist;
#else
  switch (plane->type) {
    case PLANE_X:
      return point[0] < plane->dist;
      break;
    case PLANE_Y:
      return point[1] < plane->dist;
      break;
    case PLANE_Z:
      return point[2] < plane->dist;
      break;
    default:
      return DotProduct(plane->normal, point) < plane->dist;
      break;
  }
#endif
}

short int find_clipcontents(__memBase, register short int n)
{
  struct dclipnode_t *node;

  do {
    node = &bspMem->shared.quake1.dclipnodes[n];
  } while ((n = node->children[point_plane_test(&bspMem->shared.quake1.dplanes[node->planenum])]) >= 0);

  return n;
}

/*
 * we execute as long as bsp_clip_node returns FALSE,
 * that means we get a dead-lock (endless loop) if we cannot
 * break the line any more, so we dont continue in that case
 *
 * the splitting is as follows
 *  we calculate the length between start and end point, if its 0 we break
 *  else we check for intersection, if nothing intersects we break
 *  else we check how long is the way we can go (which MUST be less than
 *  length), we go that length
 *  now we rotate the direction of the leaved move to the plane ones (not
 *  the direction of the planes normal, the direction of the planes plane),
 *  set the new player start and end and recall the splitting
 */

vec3_t player_position;
vec3_t player_target;
struct dplane_t *split_plane;

void ClipMove(void) {
  if(!VectorCompare(player_target, player_position)) {
    short int node;						/* how to calculate the node? */
  
    if(!bsp_clip_node(node)) {
      player_position = player_target;
    }
    else {
      vec3_t move_line, new_move;
      vec_t diff, angle;

      VectorSubtract(player_target, player_position, move_line);
#if 0
      player_position = Intersect(move_line, split_plane);
#else
/*
 * math

 line-equation

  alpha * n_x + b_x
  alpha * n_y + b_y
  alpha * n_z + b_z

 plane-equation
 
  distance = DotProduct(point, plane_normal) + plane_dist;
           = point_x * plane_normal_x + point_y * plane_normal_y + point_z * plane_normal_z + plane_dist;
 
 point could be solved with the line-equation

           = (alpha * n_x + b_x) * plane_normal_x + 
	     (alpha * n_y + b_y) * plane_normal_y +
	     (alpha * n_z + b_z) * plane_normal_z +
	      plane_dist;

           = (alpha * n_x * plane_normal_x) + (b_x * plane_normal_x) +
	     (alpha * n_y * plane_normal_y) + (b_y * plane_normal_y) +
	     (alpha * n_z * plane_normal_z) + (b_z * plane_normal_z) +
	      plane_dist;

	   = alpha * DotProduct(n, plane_normal) + DotProduct(b, plane_normal) + plane_dist;

 the intersection point must be on the plane, so distance must be 0

				    0 = alpha * DotProduct(n, plane_normal) + DotProduct(b, plane_normal) + plane_dist;
  alpha * DotProduct(n, plane_normal) = (-DotProduct(b, plane_normal) - plane_dist);
				alpha = (-DotProduct(b, plane_normal) - plane_dist) / DotProduct(n, plane_normal)

 */
      /* how much of move_line can we go */
      diff = (-DotProduct(player_position, split_plane->normal) - plane->dist) / DotProduct(move_line, split_plane->normal);
      VectorScale(move_line, diff, new_move);
      VectorAdd(new_move, player_position);
#endif
      VectorSubtract(player_target, player_position, move_line);
#if 0
      player_target = Rotate(moveline, split_plane);
#else
      /* without rotate, dont know how to do with */
      diff = DotProduct(player_target, split_plane->normal) + split_plane->dist;
      VectorMA(player_target, -diff, split_plane->normal, player_target);
#endif
      ClipMove();
    }
  }
}

bool bsp_clip_node(__memBase, short int node)
{
  if (node >= 0) {
    /* positivity of start and end points */
    bool pos_start, pos_end;
    struct dplane_t *plane;
    
    plane = &bspMem->shared.quake1.dplanes[node->planenum];
    pos_start = point_plane_test(player_position, plane);
    pos_end = point_plane_test(player_target, plane);
  
    /* we are on our side of the plane, both are positive */
    if (pos_start && pos_end)
      return bsp_clip_node(bspMem, bspMem->shared.quake1.dclipnodes[node].children[0]);
    /*
     * we are on the other side of the plane, both are negative
     * I think that is not possible, cause we cant be IN solid?
     */
    else if (!pos_start && !pos_end)
      return bsp_clip_node(bspMem, bspMem->shared.quake1.dclipnodes[node].children[1]);
    /* we are in the plane, one is positive and one is negative */
    else {
      /*
       * we split the move, do the partial move and return
       * must ever be positive, cause we cant be IN solid?
       */
      split_plane = plane
      return TRUE;
    }
  }
  else {
    /* we are in solid and must clip the movement */
    if (node == CONTENTS_SOLID)
      return TRUE;
    else
      return FALSE;
  }
}
