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


#ifdef _MSC_VER
#include <windows.h>
#include <GL/glu.h>
#include <GL/glut.h>
#elif __APPLE__
#include <GLUT/glut.h>
#include <OpenGl/glu.h>
#include <stdbool.h>
#else
#include <GL/glut.h>
#include <GL/glu.h>
#include <stdbool.h>
#endif

#ifndef __VIEWCULL_H__
#define __VIEWCULL_H__
namespace show{

/////////////////////////  Variable declarations...
/** The 6 planes of the viewing frustum */
extern float frustum[6][4];

/** the modelview * projection matrix to map a model point to an onscreen coordinate */
extern float matrix[16];
/** some useful variables for faster calculation of the pixel coordinates */
extern short VP[4];
/** a unit vector pointing to the right of the screen */
extern float right[3];
/** how much detail is shown, 0 means everything is plotted */
extern short DETAIL;

extern double SX, SY, SZ, EX, EY, EZ;
extern float origin[3], dir[3];   /*ray */
extern float dist;
extern int rayX,rayY;
extern float  rayVP[4];

#define NUMDIM  3
#define RIGHT 0
#define LEFT  1
#define MIDDLE  2

extern float minB[NUMDIM], maxB[NUMDIM];    /*box */
extern float coord[NUMDIM];       /* hit point */


template <class T> bool HitBoundingBox(const T center[3], T size )
{
  minB[0] = center[0] - size;
  minB[1] = center[1] - size;
  minB[2] = center[2] - size;

  maxB[0] = center[0] + size;
  maxB[1] = center[1] + size;
  maxB[2] = center[2] + size;

  bool inside = true;
  char quadrant[NUMDIM];
  register int i;
  int whichPlane;
  float maxT[NUMDIM];
  float candidatePlane[NUMDIM];

  // Find candidate planes; this loop can be avoided if
  //  rays cast all from the eye(assume perpsective view) 
  for (i=0; i<NUMDIM; i++)
    if(origin[i] < minB[i]) {
      quadrant[i] = LEFT;
      candidatePlane[i] = minB[i];
      inside = false;
    }else if (origin[i] > maxB[i]) {
      quadrant[i] = RIGHT;
      candidatePlane[i] = maxB[i];
      inside = false;
    }else {
      candidatePlane[i] = 0.0;
      quadrant[i] = MIDDLE;
    }

  // Ray origin inside bounding box 
  if(inside)  {
    return (true);
  }


  // Calculate T distances to candidate planes
  for (i = 0; i < NUMDIM; i++)
    if (quadrant[i] != MIDDLE && dir[i] !=0.)
      maxT[i] = (candidatePlane[i]-origin[i]) / dir[i];
    else
      maxT[i] = -1.;

  // Get largest of the maxT's for final choice of intersection 
  whichPlane = 0;
  for (i = 1; i < NUMDIM; i++)
    if (maxT[whichPlane] < maxT[i])
      whichPlane = i;

  // Check final candidate actually inside box 
  if (maxT[whichPlane] < 0.) return (false);
  for (i = 0; i < NUMDIM; i++)
    if (whichPlane != i) {
      coord[i] = origin[i] + maxT[whichPlane] *dir[i];
      if (coord[i] < minB[i] || coord[i] > maxB[i])
        return (false);
    } else {
      coord[i] = candidatePlane[i];
    }
  return (true);        // ray hits box
} 

void calcRay(int x, int y, double znear, double zfar);  

#include <float.h>
template <class T> 
float RayDist(T *point)
{
  return point[0] * dir[0] + point[1] * dir[1] + point[2] * dir[2] - dist;
}

template <class T> 
short ScreenDist(T *point) {
  float pn[3];
  // x coordinate on screen, not normalized
  pn[0] = point[0] * matrix[0] + point[1] * matrix[4] + point[2] * matrix[8]  + matrix[12];
  pn[1] = point[0] * matrix[1] + point[1] * matrix[5] + point[2] * matrix[9]  + matrix[13];
  // normalization
  pn[2] = point[0] * matrix[3] + point[1] * matrix[7] + point[2] * matrix[11] + matrix[15];

  // normalized x coordinate on screen
  pn[0] /= pn[2];
  pn[1] /= pn[2];

  // true x coordinate in viewport coordinate system
  //Xi = pn[0]*VP[0] + VP[1];
  //fTempo[4]*0.5+0.5)*viewport[2]+viewport[0];
  
  float XX = ( (pn[0])*rayVP[0] + rayVP[1]);
  float YY = ( (pn[1])*rayVP[2] + rayVP[3]);

  short dx, dy;
  if (XX > rayX) dx = XX-rayX;
  else dx = rayX-XX;
  
  if (YY > rayY) dy = YY-rayY;
  else dy = rayY-YY;

  // for the benefit of visual studio's compiler cast to float
  return sqrt((float)(dx*dx + dy*dy));
}

void ExtractFrustum(short detail);
void ExtractFrustum(float *frust[6]);


bool CubeInFrustum( float x, float y, float z, float size );
int  CubeInFrustum2( float x, float y, float z, float size );
char PlaneAABB( float x, float y, float z, float size, float *plane );

void remViewport();
bool LOD(float x, float y, float z, float size);
int LOD2(float x, float y, float z, float size);

}
#endif
