#include <assert.h>
#include <iostream.h>
#include <GL/gl.h>
#include <stdio.h>
#include "boundsvis.h"

const float cell_color[3] = {0., 1., 0};

BoundsWhereLeftVis::BoundsWhereLeftVis (const Vis *parent, const LogEntry *someevent) : Vis (parent, someevent)
{
  strcpy (_type, "BoundsWhereLeft");

  _start = VisManager::FindVisStart (this, someevent);
}

void BoundsWhereLeftVis::Display (const LogEntry *upto) const
{
  BoundsWhereLeft_Start_Entry *stdata = (BoundsWhereLeft_Start_Entry *)(_start->_data);

  glColor3fv (cell_color);
  stdata->_box.drawWireFrame();

  const LogEntry *cur = upto;

  Bounds3d curbox;

  //search back for the last face project; that's what we're on.
  while (cur != _start)
    {
      //show the exit point, if possible
      if (cur->_event == BoundsWhereLeft_End)
	{
	  BoundsWhereLeft_End_Entry *enddata = (BoundsWhereLeft_End_Entry *)(cur->_data);
	  if (enddata->_did_leftpt)
	    {
	      Bounds3d ptbox;
	      ptbox.IncludePoint (enddata->_leftpt);
	      
	      Vec4 dim = stdata->_box[1] - stdata->_box[0];
	      float mindim = MAXFLOAT;
	      if (dim[0] < mindim && dim[0] != 0.) mindim = dim[0];
	      if (dim[1] < mindim && dim[1] != 0.) mindim = dim[1];
	      if (dim[2] < mindim && dim[2] != 0.) mindim = dim[2];
	      mindim *= .1;
	      
	      ptbox[1] += Vec4 (mindim, mindim, mindim);
	      ptbox[0] -= Vec4 (mindim, mindim, mindim);
	      glColor3f (0., 0., 1.);
	      ptbox.drawFilled ();
	    }
	}
      else if (cur->_event == BoundsWhereLeft_Face_Project)
	{
	  BoundsWhereLeft_Face_Project_Entry *fpdata = (BoundsWhereLeft_Face_Project_Entry *)(cur->_data);

	  //show the face we're currently projecting on to, in red
	  curbox = stdata->_box;
	  curbox[0][fpdata->_axis] = curbox[fpdata->_dir][fpdata->_axis];
	  curbox[1][fpdata->_axis] = curbox[fpdata->_dir][fpdata->_axis];
	  curbox.IncludePoint (stdata->_R + (stdata->_D * fpdata->_t));

	  glEnable (GL_BLEND);
	  glBlendFunc (GL_CONSTANT_ALPHA_EXT, GL_ONE_MINUS_CONSTANT_ALPHA_EXT);
	  glBlendColorEXT (0, 0, 0, .5);

	  glColor3f (1., 0., 0.);
	  curbox.drawFilled();

	  //show the ray to that face in red
	  glBegin (GL_LINES);
	  glVertex3fv (stdata->_R.ArrayForGL());
	  glVertex3fv ((stdata->_R + (stdata->_D * fpdata->_t)).ArrayForGL());
	  glEnd();

	  //show the best face so far, in white
	  glColor3f (1., 1., 1.);
	  curbox = stdata->_box;
	  curbox[0][fpdata->_bestaxis] = curbox[fpdata->_bestdir][fpdata->_bestaxis];
	  curbox[1][fpdata->_bestaxis] = curbox[fpdata->_bestdir][fpdata->_bestaxis];
	  curbox.IncludePoint (stdata->_R + (stdata->_D * fpdata->_bestt));
	  curbox.drawFilled();
	  
	  //show the ray to that face in white
	  glBegin (GL_LINES);
	  glVertex3fv (stdata->_R.ArrayForGL());
	  glVertex3fv ((stdata->_R + (stdata->_D * fpdata->_bestt)).ArrayForGL());
	  glEnd();

	  glDisable (GL_BLEND);
	  break;
	}
      cur = cur->_prev;
    }
}


char * BoundsWhereLeftVis::StatusLine (const LogEntry *cur) const
{
  char *scratch;
  switch (cur->_event)
    {
    case BoundsWhereLeft_Start:
      return newStr ("looking for ray's exit face");
    case BoundsWhereLeft_Face_Project:
      BoundsWhereLeft_Face_Project_Entry *e;
      e = (BoundsWhereLeft_Face_Project_Entry *)(cur->_data);
      scratch = new char [100];
      sprintf (scratch, "projected to %c%c face", (e->_dir) ? '+' : '-', (char)(e->_axis + 'X'));
      if (e->_axis == e->_bestaxis)
	strcat (scratch, ", closest so far.");
      else
	strcat (scratch, ", not closer.");
      return scratch;
    case BoundsWhereLeft_End:
      BoundsWhereLeft_End_Entry *end;
      end = (BoundsWhereLeft_End_Entry *)(cur->_data);
      scratch = new char [100];
      sprintf (scratch, "returned %c%c face", (end->_dir) ? '+' : '-', (char)(end->_face + 'X'));
      if (end->_did_leftpt)
	{
	strcat (scratch, ", and computed exit pt.");
	}
      return scratch;
    default:
      cerr << "unknown event " << cur->_name << " for boundwhereleftvis.statusline" << endl;
      return newStr (cur->_name);
    }
}

BoundsIncludesPtVis::BoundsIncludesPtVis (const Vis *parent, const LogEntry *someevent) : Vis (parent, someevent) {
  strcpy (_type, "BoundsIncludesPt");

  _start = VisManager::FindVisStart (this, someevent);
}

void BoundsIncludesPtVis::Display (const LogEntry *upto) const
{
  BoundsIncludesPt_Start_Entry *st = (BoundsIncludesPt_Start_Entry *)_start->_data;
  glColor3fv (cell_color);
  st->_box.drawWireFrame();

  Bounds3d ptbox;
  ptbox.IncludePoint (st->_pt);

  Vec4 dim = st->_box[1] - st->_box[0];
  float mindim = MAXFLOAT;
  if (dim[0] < mindim && dim[0] != 0.) mindim = dim[0];
  if (dim[1] < mindim && dim[1] != 0.) mindim = dim[1];
  if (dim[2] < mindim && dim[2] != 0.) mindim = dim[2];
  mindim *= .1;

  ptbox[1] += Vec4 (mindim, mindim, mindim);
  ptbox[0] -= Vec4 (mindim, mindim, mindim);
  glColor3f (0., 0., 1.);
  ptbox.drawFilled ();

  glEnable (GL_BLEND);
  glBlendFunc (GL_CONSTANT_ALPHA_EXT, GL_ONE_MINUS_CONSTANT_ALPHA_EXT);
  glBlendColorEXT (0, 0, 0, .5);

  Bounds3d facebox;
  const LogEntry *cur = _start;
  while (cur != upto)
    {
      cur = cur->_next;
      assert (cur);
      if (cur->_event == BoundsIncludesPt_Face_Check)
	{
	  BoundsIncludesPt_Face_Check_Entry *e = (BoundsIncludesPt_Face_Check_Entry *)cur->_data;
	  facebox = e->_box;
	  facebox[0][e->_face] = facebox[e->_dir][e->_face];
	  facebox[1][e->_face] = facebox[e->_dir][e->_face];

	  if (e->_in)
	    glColor3fv (cell_color);
	  else
	    glColor3f (1., 0., 0.);
	     
	  facebox.drawFilled ();
	}
    }
  glDisable (GL_BLEND);
}

char * BoundsIncludesPtVis::StatusLine (const LogEntry *entry) const 
{
  char *scratch;
  switch (entry->_event)
    {
    case BoundsIncludesPt_Start:
      return newStr ("checking if point is in bbox");

    case BoundsIncludesPt_Face_Check:
      BoundsIncludesPt_Face_Check_Entry *e;
      e = (BoundsIncludesPt_Face_Check_Entry *)entry->_data;
      scratch = new char [100];
      sprintf (scratch, "compared to %c%c face", (e->_dir) ? '+' : '-', (char)(e->_face + 'X'));
      if (e->_in)
	strcat (scratch, ", classified as inside");
      else
	strcat (scratch, ", classified as outside");
      if (IsEndEvent (entry))
	strcat (scratch, ", done.");
      return scratch;
    default:
      cerr << "unknown event " << entry->_name << " for boundincludespoint.statusline" << endl;
      return newStr (entry->_name);
    }
}


int BoundsIncludesPtVis::IsEndEvent (const LogEntry *en) const 
{
  if (en->_event == BoundsIncludesPt_Face_Check)
    {
      BoundsIncludesPt_Face_Check_Entry *e;
      e = (BoundsIncludesPt_Face_Check_Entry *)en->_data;
      if (e->_in == 0)
	return 1;
      if ((e->_face == 2) && (e->_dir == 1))
	return 1;
    }
  return 0;
}

char * BoundsWhereEnteredVis::StatusLine (const LogEntry *cur) const
{
  char *scratch;
  switch (cur->_event)
    {
    case BoundsWhereEntered_Start:
      return newStr ("looking for where ray entered bbox");
    case BoundsWhereEntered_Face_Project:
      BoundsWhereEntered_Face_Project_Entry *e;
      e = (BoundsWhereEntered_Face_Project_Entry *)(cur->_data);
      scratch = new char [100];
      sprintf (scratch, "projected to %c%c face", (e->_dir) ? '+' : '-', (char)(e->_axis + 'X'));
      if (e->_axis == e->_bestaxis)
	strcat (scratch, ", closest so far.");
      else
	strcat (scratch, ", not closer.");
      return scratch;
    case BoundsWhereEntered_End:
      BoundsWhereEntered_End_Entry *end;
      end = (BoundsWhereEntered_End_Entry *)(cur->_data);
      if (end->_why == Missed)
	return newStr ("ray missed the bbox!");
      else if (end->_why == Behind)
	return newStr ("ray hit bbox behind its start point.");
      else
	assert (end->_why == FoundHit);

      scratch = new char [100];
      sprintf (scratch, "returned %c%c face", (end->_dir) ? '+' : '-', (char)(end->_face + 'X'));
      if (end->_did_enterpt)
	{
	strcat (scratch, ", and computed entrance pt.");
	}
      return scratch;
    default:
      cerr << "unknown event " << cur->_name << " for boundwhereEnteredvis.statusline" << endl;
      return newStr (cur->_name);
    }
}

BoundsWhereEnteredVis::BoundsWhereEnteredVis (const Vis *parent, const LogEntry *someevent) : Vis (parent, someevent)
{
  strcpy (_type, "BoundsWhereEntered");

  _start = VisManager::FindVisStart (this, someevent);
}

void BoundsWhereEnteredVis::Display (const LogEntry *upto) const
{
  BoundsWhereEntered_Start_Entry *stdata = (BoundsWhereEntered_Start_Entry *)(_start->_data);

  glColor3fv (cell_color);
  stdata->_box.drawWireFrame();

  const LogEntry *cur = upto;

  Bounds3d curbox;

  //search back for the last face project; that's what we're on.
  while (cur != _start)
    {
      //show the entrance point, if possible
      if (cur->_event == BoundsWhereEntered_End)
	{
	  BoundsWhereEntered_End_Entry *enddata = (BoundsWhereEntered_End_Entry *)(cur->_data);
	  if (enddata->_did_enterpt)
	    {
	      Bounds3d ptbox;
	      ptbox.IncludePoint (enddata->_enterpt);
	      
	      Vec4 dim = stdata->_box[1] - stdata->_box[0];
	      float mindim = MAXFLOAT;
	      if (dim[0] < mindim && dim[0] != 0.) mindim = dim[0];
	      if (dim[1] < mindim && dim[1] != 0.) mindim = dim[1];
	      if (dim[2] < mindim && dim[2] != 0.) mindim = dim[2];
	      mindim *= .1;
	      
	      ptbox[1] += Vec4 (mindim, mindim, mindim);
	      ptbox[0] -= Vec4 (mindim, mindim, mindim);
	      glColor3f (0., 0., 1.);
	      ptbox.drawFilled ();
	    }
	}
      else if (cur->_event == BoundsWhereEntered_Face_Project)
	{
	  BoundsWhereEntered_Face_Project_Entry *fpdata = (BoundsWhereEntered_Face_Project_Entry *)(cur->_data);

	  //show the face we're currently projecting on to, in red
	  curbox = stdata->_box;
	  curbox[0][fpdata->_axis] = curbox[fpdata->_dir][fpdata->_axis];
	  curbox[1][fpdata->_axis] = curbox[fpdata->_dir][fpdata->_axis];
	  //	  curbox.IncludePoint (stdata->_R + (stdata->_D * fpdata->_t));

	  glEnable (GL_BLEND);
	  glBlendFunc (GL_CONSTANT_ALPHA_EXT, GL_ONE_MINUS_CONSTANT_ALPHA_EXT);
	  glBlendColorEXT (0, 0, 0, .5);

	  glColor3f (1., 0., 0.);

	  curbox.drawFilled();

	  //show the ray to the current face, in red
	  glBegin (GL_LINES);
	  glVertex3fv (stdata->_R.ArrayForGL());
	  glVertex3fv ((stdata->_R + (stdata->_D * fpdata->_t)).ArrayForGL());
	  glEnd();

	  //show the best face so far, in white
	  glColor3f (1., 1., 1.);
	  curbox = stdata->_box;
	  curbox[0][fpdata->_bestaxis] = curbox[fpdata->_bestdir][fpdata->_bestaxis];
	  curbox[1][fpdata->_bestaxis] = curbox[fpdata->_bestdir][fpdata->_bestaxis];
	  //	  curbox.IncludePoint (stdata->_R + (stdata->_D * fpdata->_bestt));
	  curbox.drawFilled();
	  
	  //show the ray to that face in white
	  glBegin (GL_LINES);
	  glVertex3fv (stdata->_R.ArrayForGL());
	  glVertex3fv ((stdata->_R + (stdata->_D * fpdata->_bestt)).ArrayForGL());
	  glEnd();

	  glDisable (GL_BLEND);
	  break;
	}
      cur = cur->_prev;
    }
}

