#include <GL/glu.h>
#include <iostream.h>
#include <assert.h>

#include "visman.h"
#include "boundsvis.h"
#include "kdtreefindpointvis.h"
#include "kdtreefindstartcellvis.h"
#include "kdtreesubdividevis.h"
#include "kdtreesubdividerejectvis.h"
#include "kdcreatevis.h"
#include "kdtreestepvis.h"
#include "kdtreeintersectvis.h"
#include "kdtreenodeintersectvis.h"
#include "scenevis.h"
#include "rtvis.h"
#include "rwvis.h"
#include "wrvis.h"
#include "ivis.h"
#include "forms.h"
#include "mainVisForm.h"

extern FD_main *mainVisForm;

#define TOPREV {_cur_entry = _cur_entry->_prev; _cur_entry_num--; assert (_cur_entry);}
#define TONEXT {_cur_entry = _cur_entry->_next; _cur_entry_num++; assert (_cur_entry);}

#define ISEND(entry) (!entry->_next)
#define NOTEND(entry) (entry->_next)

void LogText::FillBrowser (int startat)
{
  TextLine *current = _first;
  
  for (int i = 0; i < startat; i++)
    {
      current = current->_next;
      assert (current);
    }
  
  while (current)
    {
      fl_add_browser_line (mainVisForm->LogBrowser, current->_text);
      current = current->_next;
    }
  
}

void LogText::AddLine (char *line, int entrynum, int depth)
{
  //fill a new TextLine struct
  TextLine *newline = new TextLine;
  //make room for the text, the tabs, and the ending NULL character.
  newline->_text = new char [strlen (line) + depth + 1];
  newline->_text[0] = NULL;
  for (int i = 0; i < depth; i++)
    strcat (newline->_text, "\t");
  strcat (newline->_text, line);
  newline->_entry = entrynum;
  newline->_next = NULL;
  
  //add it to our linked list
  if (!_first)
    _first = newline;
  else
    {
      TextLine *walk = _first;
      while (walk->_next) walk = walk->_next;
      walk->_next = newline;
    }
}

//returns the # lines we preserved and the entry # of the last line
//we have.
void LogText::ClipToLatest (int newnum, int &numlinespreserved, int &latestentrynum)
{
  numlinespreserved = latestentrynum = 0;

  if (!_first)
    return;
  else
    {
      TextLine *walk = _first;

      if (_first->_entry > newnum)
	{
	  Clear(NULL);
	  return;
	}
      
      do
	{
	  if ( (walk->_entry == newnum) || (!walk->_next) || (walk->_next->_entry > newnum) )
	    {
	      Clear (walk);
	      latestentrynum = walk->_entry;
	      return;
	    }

	  walk = walk->_next;
	  numlinespreserved++;
	} while (walk);
    }
}

void LogText::Clear (TextLine *starthere)
{
  TextLine *cur;
  if (!starthere)
    {
      cur = _first;
      _first = NULL;
    }
  else
    cur = starthere->_next;
  
  if (!cur)
    return;
  
  TextLine *next;
  do
    {
      next = cur->_next;
      delete[] cur->_text;
      delete cur;
    } while (cur = next);
}

VisManager::VisManager (void) : _cur_vis (NULL), _cur_entry (NULL)
{
  JumpToStart();
}

Vis * VisManager::ConstructVis (const Vis *parent, const LogEntry *ev)
{
  switch (ev->_event) {
  case Log_Start:
    return new DefaultVis (parent, ev);
    //    return new LogStartVis (ev);

  case SceneReadFile_Start:
  case SceneReadFile_End:
    return new SceneVis (parent, ev);

  case KDTreeCreate_Start:
  case KDTreeCreate_End:
    return new KDCreateVis (parent, ev);

  case KDTreeSubdivide_Reject:
    return new KDTreeSubdivideRejectVis(parent, ev);

  case KDTreeSubdivide_Start:
  case KDTreeSubdivide_TryPlane:
  case KDTreeSubdivide_End:
    //return new KDCreateVis (parent, ev);
    return new KDTreeSubdivideVis(parent, ev);

  case KDTreeFindPoint:
    return new KDTreeFindPointVis(parent, ev);

  case KDTreeStep_Start:
  case KDTreeStep_Ascend:
  case KDTreeStep_End:
  case KDTreeStep_ToSibling:
    return new KDTreeStepVis(parent, ev);

  case KDTreeIntersect_Start:
  case KDTreeIntersect_End:
    return new KDTreeIntersectVis(parent, ev);

  case KDTreeNodeIntersect_Start:
  case KDTreeNodeIntersect_NewBest:
  case KDTreeNodeIntersect_Obj:
  case KDTreeNodeIntersect_End:
    return new KDTreeNodeIntersectVis(parent, ev);

  case KDTreeFindStartCell_Start:
  case KDTreeFindStartCell_End:
    return new KDTreeFindStartCellVis(parent, ev);

  case BoundsWhereLeft_Start:
  case BoundsWhereLeft_Face_Project:
  case BoundsWhereLeft_End:
    return new BoundsWhereLeftVis(parent, ev);

  case BoundsWhereEntered_Start:
  case BoundsWhereEntered_Face_Project:
  case BoundsWhereEntered_End:
    return new BoundsWhereEnteredVis(parent, ev);
 
  case BoundsIncludesPt_Start:
  case BoundsIncludesPt_Face_Check:
    return new BoundsIncludesPtVis(parent, ev);
    
  case RT_NewRootFrustum_Start:
  case RT_NewRootFrustum_End:
    return new RTNewRootFrustumVis (parent, ev);
    
  case RT_SampleFourCorners_Start:
  case RT_SampleFourCorners_End:
    return new RTSampleFourCornersVis (parent, ev);
    
  case RT_Rendering_Start:
  case RT_Rendering_End:
    return new RTRenderingVis (parent, ev);
    
  case RT_NewFrame:
    return new RTNewFrameVis (parent, ev);
    
  
  case RW_UseKDTree_Start:
  case RW_UseKDTree_End:
    return new RWUseKDTreeVis (parent, ev);
    
  case RW_DoOneRayFC_Start:
  case RW_DoOneRayFC_End:
    return new RWDoOneRayFCVis (parent, ev);
    
  case RW_Work_Start:
  case RW_Work_CastRay:
  case RW_Work_ConfirmRay:
  case RW_Work_End:
    return new RWWorkVis (parent, ev);
    
  case WR_FrustumSubdivided:
    return new WRFrustumSubdividedVis (parent, ev);
  case WR_EatCurCell:
    return new WREatCurCellVis (parent, ev);
  case WR_FrustumWalked:
    return new WRFrustumWalkedVis (parent, ev);
    
  case WR_FindNextCells_Start:
  case WR_FindNextCells_End:
    return new WRFindNextCellsVis (parent, ev);
    
  case WR_FloodYes:
  case WR_FloodNo:
    return new WRFloodVis (parent, ev);
    
  case Int_ObjList_Start:
  case Int_ObjList_End:
  case Int_Avoided:
  case Int_Obj:
    return new IntVis (parent, ev);
    
  case RT_Stats:
    return new RTStatsVis (parent, ev);
    
  case KDTreeNodeNewIntersect_Start:
  case KDTreeNodeNewIntersect_End:
    return new KDTreeNodeNewIntersectVis (parent, ev);
    
  default:
    cerr << "unknown log entry " << ev->_name << ", making default vis." << endl;
    return new DefaultVis (parent, ev);
  }
}

const LogEntry *VisManager::FindVisStart (const Vis *vis, const LogEntry *starthere)
{
  const LogEntry *cur = starthere;
  while (!vis->IsStartEvent (cur))
    {
      cur = cur->_prev;
      assert (cur);
    }
  return cur;
}


const LogEntry *VisManager::FindVisEnd (const Vis *vis, const LogEntry *starthere)
{
  const LogEntry *cur = starthere;
  while (!vis->IsEndEvent (cur))
    {
      cur = cur->_next;
      assert (cur);
    }
  return cur;
}

int VisManager::Display (void)
{
  //if we displayed this last time, be smart about it!
  static const LogEntry *last_cur = NULL;
  static GLuint disp_list = glGenLists (1);

  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  if (last_cur == _cur_entry)
    {
      glCallList (disp_list);
      return 0; //not displaying anything new
    }
  else
    {
      last_cur = _cur_entry;

      glDeleteLists (disp_list, 1);

      disp_list = glGenLists (1);
      glNewList (disp_list, GL_COMPILE_AND_EXECUTE);
      
      int numsaved, latestentry;
      _text.ClipToLatest (-1, numsaved, latestentry);

      if (_cur_vis->_parent)
	DisplayUpTo (_cur_vis, _cur_vis->_parent->GetStartEntry()->_next, _cur_vis->GetStartEntry()->_prev);
      else
	{
	  if (_cur_entry != rclog.First())
	    DisplayUpTo (_cur_vis, rclog.First(), _cur_vis->GetStartEntry()->_prev);
	}
      
      DisplayCurrent (_cur_vis->GetStartEntry());
      glEndList();
      
      fl_freeze_form (mainVisForm->main);
      fl_clear_browser (mainVisForm->LogBrowser);
      _text.FillBrowser (0);
      int total_lines = fl_get_browser_maxline(mainVisForm->LogBrowser);
      int num_vis_lines = fl_get_browser_screenlines (mainVisForm->LogBrowser);
      if (num_vis_lines > total_lines)
	fl_set_browser_topline(mainVisForm->LogBrowser, 0);
      else
	fl_set_browser_topline(mainVisForm->LogBrowser, total_lines - (num_vis_lines / 2));
      fl_unfreeze_form (mainVisForm->main);
      //      cerr << "------" << endl;
      //      cerr << status;
      return 1; // displayed something new
    }
}

void VisManager::DisplayCurrent (const LogEntry *start)
{
  const LogEntry *walk = start; //FindVisStart (cur, _cur_entry);
  do 
    {
      assert (walk->_num <= _cur_entry->_num);
      if (_cur_vis->IsOurs (walk))
	{
	  AddToStatus (_cur_vis, walk);
	}
      else
	{
	  //print the kid's start & end events.
	  Vis *kidvis = ConstructVis (_cur_vis, walk);
	  assert (kidvis->IsStartEvent (walk));
	  AddToStatus (kidvis, walk);
	  if (!kidvis->IsEndEvent (walk))
	    {
	      while (!kidvis->IsEndEvent (walk))
		{
		  assert (walk->_num <= _cur_entry->_num);
		  walk = walk->_next;
		}
	      AddToStatus (kidvis, walk);
	    }
	}
      if (walk == _cur_entry)
	break;
      walk = walk->_next;
      assert (walk);
    } while (1);
  _cur_vis->Display (_cur_entry);
}

//should be called with curvis, curvis's parent start + 1, and curvis.start - 1
//if curvis has no parent,
//should be called with curvis, rclog.First(), and curvis.start - 1
void VisManager::DisplayUpTo (const Vis *curvis, const LogEntry *start, const LogEntry *end)
{
  assert (start && end && curvis);
  if (curvis->_parent)
    {
      const LogEntry *newstart = curvis->_parent->GetStartEntry();
      assert (newstart);
      const LogEntry *newend = start->_prev;
      assert (newend);
      assert (newend->_num >= newstart->_num);
      DisplayUpTo (curvis->_parent, newstart, newend);
    }
  else
    {
      start = rclog.First();
    }
  
  if (end->_num < start->_num) return;

  //now we walk from start to end, printing out all the start & end events
  //we can find.
  const LogEntry *cur = start;
  int didmove = 0; //to avoid double printing; if we didn't have to move to get from the start event to the end event, they're the same entry and we shouldn't print it twice.
  
  //print it
  while (1)
    {
      assert (cur);
      assert (cur->_num <= end->_num);
      curvis = ConstructVis (curvis->_parent, cur);
      
      //print the start event
      AddToStatus (curvis, cur);
      
      //find our end event or the given one, whichever comes first
      didmove = 0;
      while ((!curvis->IsEndEvent (cur)) && (cur != end))
	{
	  cur = cur->_next;
	  assert (cur->_num <= end->_num);
	  didmove = 1;
	}
      
      //if we got to our end event, print it.
      if (didmove && (curvis->IsEndEvent (cur)))
	AddToStatus (curvis, cur);
      
      //if we got to the given end event, leave!
      if (cur == end)
	{
	  curvis->Display (end);
	  delete curvis;
	  return;
	}
      
      cur = cur->_next;
    }
}


//we want to add a line for entry; curvis knows how to do it.
void VisManager::AddToStatus (const Vis *curvis, const LogEntry *entry) 
{
  char *status = curvis->StatusLine (entry);
  if (status)
    {
      if (!curvis->IsStartEvent (entry) && !curvis->IsEndEvent (entry))
	{
	  _text.AddLine (status, entry->_num, curvis->_depth + 1);
	}
      else
	_text.AddLine (status, entry->_num, curvis->_depth);
    }
}
  
void VisManager::StepUp (void)
{
  if (ISEND(_cur_entry))
    return;

  if (!_cur_vis->_parent) return;

  while (!_cur_vis->_parent->IsOurs (_cur_entry))
    {
    TONEXT;
    }
  delete _cur_vis;
  _cur_vis = _cur_vis->_parent;
}

void VisManager::Rewind (void)
{
  if (!_cur_entry->_prev) return;
  
  //are we leaving cur_vis?
  if (_cur_vis->IsStartEvent (_cur_entry))
    {
      TOPREV;

      const Vis *parent = _cur_vis->_parent;
      delete _cur_vis; //bye, cur_vis.

      if (parent && parent->IsOurs (_cur_entry))
	{
	  _cur_vis = parent;
	  return;
	}
      else
	{
	  _cur_vis = ConstructVis (parent, _cur_entry);
	  return;
	}
    }

  TOPREV;

  //are we staying at the same level?
  if (_cur_vis->IsOurs (_cur_entry))
    { //all set!
      return;
    }
  else //we're within cur_vis, but there must be a kid of ours at work here.
    {
      _cur_vis = ConstructVis (_cur_vis, _cur_entry);
    }
}

//get to previous event at this level; if none, go to the parent.
void
VisManager::RewindOver (void)
{
  if (!_cur_entry->_prev) return;
  
  //are we leaving cur_vis?
  if (_cur_vis->IsStartEvent (_cur_entry))
    {
      TOPREV;

      const Vis *parent = _cur_vis->_parent;
      delete _cur_vis; //bye, cur_vis.

      if (parent && parent->IsOurs (_cur_entry))
	{
	  _cur_vis = parent;
	  return;
	}
      else
	{
	  _cur_vis = ConstructVis (parent, _cur_entry);
	  _cur_entry = FindVisStart (_cur_vis, _cur_entry);
	  return;
	}
    }

  TOPREV;

  while (!_cur_vis->IsOurs (_cur_entry))
    {
      TOPREV;
    }
}


void VisManager::RewindUp (void)
{
  if (!_cur_vis->_parent) return;

  while (!_cur_vis->_parent->IsOurs (_cur_entry))
    {
    TOPREV;
    }
  const Vis *parent = _cur_vis->_parent;
  delete _cur_vis;
  _cur_vis = parent;
}

void VisManager::RewindToStart (void)
{
  assert (_cur_entry);
  assert (_cur_vis);

  //get to one past the end of the current vis
  while (!_cur_vis->IsStartEvent (_cur_entry))
    {
      TOPREV;
    }
}


void VisManager::GetPast (void)
{
  assert (_cur_entry);
  assert (_cur_vis);

  //get to one past the end of the current vis
  while (!_cur_vis->IsEndEvent (_cur_entry))
    {
      TONEXT;
    }

  LeaveCurEvent();
}

void VisManager::StepOver (void)
{
  if (ISEND(_cur_entry))
    return;

  assert (_cur_entry);
  assert (_cur_vis);

  assert (_cur_vis->IsOurs (_cur_entry));

  //if we're at the start of something, get to its end
  //(so we can go one past it)
  if (_cur_vis->IsStartEvent (_cur_entry))
    {
      while (!_cur_vis->IsEndEvent (_cur_entry))
	{
	  TONEXT;
	}
    }

  //if we're at the end, go to one past it.
  if (_cur_vis->IsEndEvent (_cur_entry))
    {
      LeaveCurEvent();
      return;
    }

  //otherwise, move through it.
  do
    {
      TONEXT;
    } while (!_cur_vis->IsOurs (_cur_entry));  
}

void VisManager::LeaveCurEvent (void)
{
  assert (_cur_vis->IsEndEvent (_cur_entry));
  
  if (ISEND(_cur_entry))
    return;
  
  const Vis *parent = _cur_vis->_parent;
  delete _cur_vis;

  //figure out if we're proceeding up or across
  TONEXT;

  if (parent && parent->IsOurs (_cur_entry)) 
    { //up
      _cur_vis = parent;
    }
  else 
    { //across
      _cur_vis = ConstructVis (parent, _cur_entry);
    }
}

void VisManager::Step (void)
{
  assert (_cur_entry);
  assert (_cur_vis);

  if (ISEND(_cur_entry))
    return;

  //if curvis is done, move on to the next event
  if (_cur_vis->IsEndEvent (_cur_entry))
    {
      LeaveCurEvent();
      return;
    }

  TONEXT;

  //go to the next log entry; did we step across or step in?
  if (_cur_vis->IsOurs (_cur_entry))  
    { //we stepped across
      return;
    }
  else
    {//we stepped down.
      _cur_vis = ConstructVis (_cur_vis, _cur_entry);
      return;
    }
}

void VisManager::GotoEvent(const LogEntry *en)
{
  if (en->_num < _cur_entry->_num)
    JumpToStart();
  while (_cur_entry != en)
    Step();
}

void VisManager::JumpToEnd()
{
  while (NOTEND(_cur_entry))
    StepOver();
}

void VisManager::JumpToStart()
{
  CleanStack();
  _cur_entry = rclog.First();
  _cur_entry_num = 0;
  _cur_vis = ConstructVis (NULL, _cur_entry);
}

void VisManager::CleanStack (void)
{
  if (!_cur_vis) return;

  const Vis *cur = _cur_vis;
  const Vis *parent;
  do
    {
      parent = cur->_parent;
      delete cur;
      cur = parent;
    } while (cur);

  _cur_vis = NULL;
}

Vis::Vis (const Vis *parent, const LogEntry *someevent) : _parent (parent) 
{
  strcpy (_type, "unnamedVis");
  if (_parent)
    _depth = _parent->_depth + 1;
  else
    _depth = 0;
}

void VisManager::JumpUp (void)
{
  if (_cur_vis->IsStartEvent (_cur_entry))
    RewindUp();
  else
    RewindToStart();
}

char *newStr (const char *copythis)
{
  char *copy = new char [strlen (copythis) + 1];
  strcpy (copy, copythis);
  return copy;
}

