#ifndef _WorkRec_H_
#define _WorkRec_H_

#include "assert.h"
#include <stdlib.h>
#include <iostream.h>

#include "cyclestate.h"
#include "jpalist.h"
#include "frustum.h"
#include "protectedobject.h"
#include "planecache.h"
#include "raycastable.h"
#include "globalstate.h"
#include "common.h"

class WorkRec;
typedef WorkRec * PWorkRec;

class RayCastableList;
class WorkQueue;
class _KDTreeNode;
class ObjInfo;

typedef JPAList<ObjInfo> MPObjList;

//tells us when to stop subdividing, given the size of the current frustum
//and its subdivision level (0 is the root)
#define LAST_SUBD_LEVEL(xsize, subdlevel) ((xsize <= 3) || ((subdlevel + 1) >= truly_GS->_max_subd_level))

class WorkRec {
  friend class UI_Thread;
public:
		Frustum			_frustum;
		int                     _f_num;
		int                     _rw_own; //which render worker owns us?
		int				_subd_count;
		WorkRec			*_parent;

		MPObjList              *_straddlers;
                MPObjList              *_incident;
		MPObjList              _real_str_list; //when kdtreenode is managing the lists and this frustum isn't associated with any one yet, _straddlers points here. When the workrecs are managing the lists (the old way), _straddlers always points here and _own_list is set, meaning we have to handle clearing them.
		MPObjList              _real_inc_list; //for when we're not associated with any cell and we want clear lists;point the _straddlers & _incident here.
                int                    _own_list; //if this is true, we're responsible for clearing our lists; otherwise the kdtreenode class manages them.

                PWorkRec              _kids[4];


                CycleState *_CS; 

		_KDTreeNode *_cur_cell;

		Vec4 _debug_color;
		static float _cr, _cg, _cb; //used for cycling debug color

		_KDTreeNode *_cell_corner_goesto[4];
		int _face_corner_goesto[4];
		int _dir_corner_goesto[4];

		Ray _corner_rays[4];
		int _have_curcell_objs;
		int _just_shade; //some frusta are plain lucky!
		int _num_kdcells_walked; //how many cells the whole frustum walked thru
		int _num_kdcells_traversed; //how many traversals we calced
				    
		int _got_total_hit;
		Hit totalhit; //this hit matters a whole lot.

  protected:

		//returns # of rays shaded
		int ShadeSmart (void);

                //input: fiverays, whether they enter the same cell from _cur_cell, output: cells
                void CalcCells (Ray fiverays[5], int same, _KDTreeNode *cells[5], int faces[5], int dirs[5]);

                //input: coords, output: fiverays
                void MakeRays (Vec4 coords[5], const Vec4 &origin, Ray fiverays[5]);

                //i/o: see the actual func definition
                void CastRays (int rwnum, int numrays, Vec4 *coords, Ray *fiverays, _KDTreeNode **destcells, int checkprevint, int alltheway, int *doshoot, int *andconfirm, int &numrayscast);


                void ShootRaysAndSubdivide (int rwnum, int same, WorkQueue *queue, int &numrayscast);

		int NumObjects() { return (_incident->Size() + _straddlers->Size()); }
		int NumInsideRays();

		WorkRec() {
				cerr << "Error: WorkRec default constructor called" << endl;
				exit(-1);
			}

	public:

		//make sure that whenever you create a WorkRec, you
		    //specify its owner before using; makes a lot of 
							//debugging code work.
		void SetOwner (int fnum, int rwown) {_f_num = fnum; _rw_own = rwown;}

//this should be called in the FC case before a workrec gets executed
                void Init (CycleState *CS, const Ray *cornerrays[4], _KDTreeNode *curcell) {
                           _CS = CS;
                           _have_curcell_objs = 0;

		           for (int i = 0; i < 4; i++) {
				_corner_rays[i] = *cornerrays[i];
				_cell_corner_goesto[i] = NULL;
				_face_corner_goesto[i] = 0;
				_dir_corner_goesto[i] = 0;
                            }
                           _cur_cell = curcell;
			   totalhit.obj = NULL;

			    _num_kdcells_walked = _num_kdcells_traversed = 0;

                             if (!_parent)
				{
				_just_shade = 0;
				_got_total_hit = 0;
				}
			    else
				{
//I don't know why this is propagated. CFT
				 _just_shade = _parent->_just_shade;

//if the parent knows what it's hitting, so do its kids.
				 _got_total_hit = _parent->_got_total_hit;
				 if (_got_total_hit)
				     totalhit = _parent->totalhit; //mhmm
				}

			     //did we own the lists in the last frame?
                             if (_own_list)
			       {
				 assert (_incident == &_real_inc_list);
				 assert (_straddlers == &_real_str_list);
				 _incident->Clear();
				 _straddlers->Clear();
			       }

			     _own_list = 0;
                             _incident = &_real_inc_list;
                             _straddlers = &_real_str_list;
                } 

		WorkRec(CycleState *CS, WorkRec *parent) : _frustum(),  _CS (CS), _parent (parent), _have_curcell_objs (0), _f_num (-1), _rw_own (-1), _cur_cell (NULL), _got_total_hit (0), _num_kdcells_walked (0), _num_kdcells_traversed (0), _just_shade (0), _incident (&_real_inc_list), _straddlers (&_real_str_list), _own_list (0)
				{ 
                                    
                                    if (!parent)
                                          {
                			    _cr = _cg = _cb = 0.2f;
                                            _subd_count = 0;
                                          }
                                   else
                                       _subd_count = parent->_subd_count + 1;

				  //assign a debug color for our rays 
				  _debug_color = Vec4(_cr, _cg, _cb);
				  //advance the color for the next workrec
				  _cr += 0.13579; if ( _cr > 0.8f ) _cr = 0.2f;
				  _cg += 0.19753; if ( _cg > 0.8f ) _cg = 0.2f;
				  _cb += 0.17391; if ( _cb > 0.8f ) _cb = 0.2f;

                                  for (int i = 0; i < 4; i++) _kids[i] = NULL; 
                                  }

		~WorkRec() {
		}

               void RayCast(const Ray &ray, Hit &hit, int x, int y, int drawing_flags = 0);

	       int SameExits();
	       int AdjacentExitFaces();
	       int Empty (void) {if (_incident->Size() == 0 & _straddlers->Size() == 0) return 1; else return 0;}

	       //returns numskipped, inc's raycast by the # we did.
	       int Work (int rwnum, int &rayscast, CycleState &latestCS, WorkQueue *queue);

               void PassDownStraddlers (void); //to kids

//which rw is doing this; we need to pass this down to the LP code
	       void EatCurCell (int rw);

//nobody has NULL as an ancestor
               int HasAncestor (WorkRec *parent); 

               WorkRec *GetFloodParent (void);

	       static int _frustum_straddler_intersect_calls;
	       static int _frustum_straddler_intersect_success;
	       static int _frustum_straddler_bbox_success;
	       static int _frustum_incident_intersect_calls;
	       static int _frustum_incident_intersect_success;
	       static int _frustum_incident_bbox_success;

	       static void ClearStats (void) {
	        _frustum_straddler_intersect_calls=0;
	        _frustum_straddler_intersect_success=0;
	        _frustum_straddler_bbox_success=0;
	        _frustum_incident_intersect_calls=0;
	        _frustum_incident_intersect_success=0;
	        _frustum_incident_bbox_success=0;}

};

#endif
