
#ifndef _JL_Render_Worker_H_
#define _JL_Render_Worker_H_


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

#include "universal_threads.h"
#include "syncedobject.h"
#include "workqueue.h"
#include "ray.h"
#include "cyclestate.h"
#include "bufpool.h"

class Render_Thread;
class WorkRec;
//class CycleState;
class GlobalState;

class _KDTreeNode;
class Hit;

struct FrameReq {
  int _do_specular, _do_ambient, _do_diffuse;
  int _shadow_depth;
  int _reflection_depth;
  int _refraction_depth;
  int _texturing;
  int _age; //age of frame
};


typedef int FrameID;

#define GET_RAY_INFO_PTR(x,y) &Render_Worker::_ray_cache [y * Render_Worker::_window_width +x];

#define GET_SHADE_INFO_PTR(x,y) &Render_Worker::_ray_cache [y * Render_Worker::_window_width +x];
#define GET_INTERSECT_INFO_PTR(x,y) &Render_Worker::_ray_cache [y * Render_Worker::_window_width +x];

enum CalcStatus {DUNNO, NO, YES};

struct RayCache {
  //Vec4's have w > 0 if they're set, < 0 if not
  
  Ray ray;//always set
  
  CalcStatus hitsomething; //-1 if not set, 0 if false, 1 if true
  Hit objhit; //struct detailing the object hit and where it was hit
  Vec4 P; //set if obj is set. Point of object intersection with ray
  Color objcolor; //set when P is set
  int colortexlevel; //texture level of the color
  
  Vec4 toV; //vector from P to the camera point
  
  Vec4 toL[MAX_LIGHTS]; //vectors to light, one per light; depends on P
  float distToLight [MAX_LIGHTS]; //set if toL is
  RCFloat NdotL [MAX_LIGHTS]; //this is calced if toL is; depends on N
  Vec4 diffusecontrib;
  
  Vec4 specularcontrib;
  
  CalcStatus shadowed [MAX_LIGHTS];
  
  Vec4 R; //reflection vector
  
  Vec4 curcolor;

  int _shadow_done;

  int _used_kdtree; 
  int _shaded;
  FrameID frameID; 

  int _rw_did; //which render worker did this? (!= _rw_owned if work-stolen)
  int _rw_owned; //which render worker owned this?
  int _frusta_num; //which frusta was this a part of?
};


class LPMem;

class Render_Worker : public SyncedObject {

	friend class Render_Thread;

	private:
                static int InShadow (const Vec4 &pt, int i, RayCastable *obj, RCFloat lightt, const Vec4 &tolight, CycleState &CS);

                LPMem * _lp_mem;

	        Pool            *_pool;
		Render_Thread	*_parent;

		UNIV_THREAD		_thread;

		int				_id; //id of this render worker
		static int		_count; //used to give each render worker a unique id

		// messages from Render_Thread:
		volatile int	_exit;

		int _skipped; //how many frusta, aka jobs, we skipped by not subdividing further. The load-balancing algorithm in render_thread::gimme_work uses this to tell when we've made & done all the frusta.

                static real DistanceAtten (real dist);

	public: 
//lock to change the current offset into this queue
//UI thread uses this.
		ProtectedWorkQueue	_local_work_queue;

		//reconstruct thread uses this too, when it's feeling bold.
	        static RayCache   *_ray_cache;
		static int _window_width; //set by newWindowSize; tells us how to index into _ray_cache
		static int _window_height; //set by newWindowSize; lets us know if window really resized or not

		int  _rays_cast; //written by this, readable by all

		static void freeCache (void);
		static void newWindowSize (int newx, int newy);
		static             FrameReq    _frame_req; //how much work to get done per ray for this frame. Should be set at the beginning of each frame.
											       static   FrameID _frame_id; //when user moves, we get a new id
											       static FrameID _sub_frame_id; //when we finish a frame, this goes up
         	static int _frustum_casting; //on or off

	        void ClearStats(void);
	//stats:
//how many objects were classified as in or out of frusta this frame.
	        int _num_objs_in_frusta; 
	        int _num_objs_out_frusta;


		Render_Worker();
		Render_Worker(Render_Thread *p);
		Render_Worker(const Render_Worker &R) { CopyFrom(R); }
		virtual ~Render_Worker();

		Render_Worker& CopyFrom(const Render_Worker&);
		Render_Worker& operator=(const Render_Worker &R) { return CopyFrom(R); }

//		friend ostream& operator<<(ostream&, const Render_Worker&);
//		friend istream& operator>>(istream&, Render_Worker&);

static void Render_Worker::SanityCheck (int x, int y, RayCache *rayinfo, _KDTreeNode *drawhere, _KDTreeNode *starthere, _KDTreeNode *passedinstart, CycleState &CS);
static void Render_Worker::DoOneRayFC(int rwnum, WorkRec *WR, const Ray &ray, 
				      int x, int y, CycleState &CS, 
				      int confirm, int alltheway, _KDTreeNode *next_cell);

static void Render_Worker::InitRayCacheEntry (RayCache *rayinfo, const Ray &ray, int rwnum);

//the WR is only passed in for debugging purposes, to associate it
//with the ray shot.
  static void Render_Worker::DoOneRay(int rwnum, WorkRec *WR, const Ray &ray, int x, int y,
				    CycleState &CS, int confirm, _KDTreeNode *starthere, Hit *givenhit = NULL);

static void Shade (int x, int y, CycleState &CS, int confirm);

//rwnum and WR are only passed in for debugging purposes, to associate 'em
//with the ray shot.
static void GetObjHit (int rwnum, WorkRec *WR, const Ray &ray, int x, int y, CycleState &CS, _KDTreeNode *starthere, Hit *givenhit);

static void Render_Worker::TakeHit (int x, int y, RayCache *rayinfo, Hit *givenhit, CycleState &CS, int conclusive);


//returns # of jobs we were able to skip - b/c frustum casting allows us
//to skip a lot of them. 
		int Work(int rwnum, WorkRec *wr);
		void WorkNotFC(int rwnum, WorkRec *wr);

		void WorkLoop(int threadless = 0);
            
                LPMem *GetLPMem (void) {return _lp_mem;}

		static UNIV_THREAD_RETURN_TYPE _Do_Work_Proc(void *arg);

};


#endif
