#ifndef __OGL2_BOUNDING_BOX__
#define __OGL2_BOUNDING_BOX__

#ifdef WIN32
#include <windows.h>
#endif

#ifndef WIN32
#define APIENTRY
#define CALLBACK
#endif

#include <GL/gl.h>
#include "glmatrix.h"

extern "C++" {

class BoundingBox {

  GLubyte eval(GLfloat x, GLfloat y, GLfloat z, GLfloat w) {
    GLubyte c=0;
    if(x>w)  c|=1;
    if(x<-w) c|=2;
    if(y>w)  c|=4;
    if(y<-w) c|=8;
    if(z>w)  c|=16;
    if(z<-w) c|=32;
    return c;
  }

  GLfloat mx, my, mz;
  GLfloat Mx, My, Mz;
  bool initialized1,
       initialized2,
       initialized;

public:
  BoundingBox () :
    initialized1(false), initialized2(false), initialized(false) {}

  void Corner1(GLfloat x, GLfloat y, GLfloat z) {
    initialized1 = true;
    mx = x;
    my = y;
    mz = z;
    if(initialized2) 
      initialized = true;
  }
  void Corner2(GLfloat x, GLfloat y, GLfloat z) {
    initialized2 = true;
    Mx = x;
    My = y;
    Mz = z;
    if(initialized1) 
      initialized = true;
  }
  bool IsVisible() {
    if(!initialized)
      return true;
    GLmatrix matrix1, matrix2;

    glGetFloatv(GL_PROJECTION_MATRIX, matrix1());
    glGetFloatv(GL_MODELVIEW_MATRIX, matrix2());
    matrix1 = matrix1*matrix2;

    GLfloat x, y, z, w;
    GLubyte outcode;

    x=mx; y=my; z=mz; w=1.0;
    matrix1.MultVertex4f(x, y, z, w);
    outcode = eval(x, y, z, w);
    if(outcode==0)  return true;

    x=Mx; y=my; z=mz; w=1.0;
    matrix1.MultVertex4f(x, y, z, w);
    outcode &= eval(x, y, z, w);
    if(outcode==0)  return true;

    x=Mx; y=My; z=mz; w=1.0;
    matrix1.MultVertex4f(x, y, z, w);
    outcode &= eval(x, y, z, w);
    if(outcode==0)  return true;

    x=mx; y=My; z=mz; w=1.0;
    matrix1.MultVertex4f(x, y, z, w);
    outcode &= eval(x, y, z, w);
    if(outcode==0)  return true;


    x=mx; y=my; z=Mz; w=1.0;
    matrix1.MultVertex4f(x, y, z, w);
    outcode &= eval(x, y, z, w);
    if(outcode==0)  return true;

    x=Mx; y=my; z=Mz; w=1.0;
    matrix1.MultVertex4f(x, y, z, w);
    outcode &= eval(x, y, z, w);
    if(outcode==0)  return true;

    x=Mx; y=My; z=Mz; w=1.0;
    matrix1.MultVertex4f(x, y, z, w);
    outcode &= eval(x, y, z, w);
    if(outcode==0)  return true;

    x=mx; y=My; z=Mz; w=1.0;
    matrix1.MultVertex4f(x, y, z, w);
    outcode &= eval(x, y, z, w);
    if(outcode==0)  return true;

    return false;
  }

  // Union with another bounding box
  BoundingBox& operator +=(const BoundingBox& B)
  {
    if(!B.initialized) {
      initialized1 = initialized2 = initialized = false;
      return *this;
    }
    if(initialized) {
      if(B.mx<mx)  mx = B.mx;
      if(B.my<my)  my = B.my;
      if(B.mz<mz)  mz = B.mz;
      if(B.Mx>Mx)  Mx = B.Mx;
      if(B.My>My)  My = B.My;
      if(B.Mz>Mz)  Mz = B.Mz;
      }
    else {
      mx = B.mx; my = B.my; mz = B.mz;
      Mx = B.Mx; My = B.My; Mz = B.Mz;
      initialized1 = initialized2 = initialized = true;
      }
    return *this;
  }

  void Render()
  {
    if(!initialized)
      return;
    glDisable(GL_TEXTURE_2D);
    glColor3f(1, 1, 1);
    glBegin(GL_LINE_LOOP);
      glVertex3f(mx, my, mz);
      glVertex3f(Mx, my, mz);
      glVertex3f(Mx, My, mz);
      glVertex3f(mx, My, mz);
    glEnd();
    glBegin(GL_LINE_LOOP);
      glVertex3f(mx, my, Mz);
      glVertex3f(Mx, my, Mz);
      glVertex3f(Mx, My, Mz);
      glVertex3f(mx, My, Mz);
    glEnd();
    glBegin(GL_LINE_LOOP);
      glVertex3f(mx, my, mz);
      glVertex3f(Mx, my, mz);
      glVertex3f(Mx, my, Mz);
      glVertex3f(mx, my, Mz);
    glEnd();
    glBegin(GL_LINE_LOOP);
      glVertex3f(mx, My, mz);
      glVertex3f(Mx, My, mz);
      glVertex3f(Mx, My, Mz);
      glVertex3f(mx, My, Mz);
    glEnd();
  }

};

} // extern "C++"

#endif

