
#ifndef _JL_RayCastable_H_
#define _JL_RayCastable_H_

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

#include "light.h"
#include "globals.h"
#include "bounds3d.h"
#include "jpalist.h"

class Vec4;
class Ray;
class Prim;

class KDCellList;
class _KDTreeNode;

class Hit {
public:
  Hit (void);

  //--these are all required to be filled in when you got a hit
  RayCastable *obj;
  _KDTreeNode *hitcell;
  float t;
  int texcalced;
  int normalcalced;

  //---these gotta be updated by the appropriate intersectors
  int num_sphere_intersections;
  int num_sphere_bbox_intersections;
  int num_box_intersections;
  int num_polygon_intersections;
  int num_polygon_bbox_intersections;
  int num_cylinder_intersections;
  int num_cylinder_bbox_intersections;
  int num_cone_intersections;
  int num_cone_bbox_intersections;

  int num_intersect_successes; //number of intersections that actually hit

  int num_bbox_successes; //#times bbox test returned NOHIT
  int num_bbox_quick_rejects;

  int num_kdtree_sort_list_quick_rejects;

  int num_kdcells_walked_by_kdtree;

  int num_straddlers_skipped;
  int num_straddlers_checked_twice;

  //--past this is not required

  float texs, text;
  Vec4 normal;

  int numIntersections (void); //bbox intersections, actual obj intersections, everything.
  int numObjIntersections (void);
  int numBboxIntersections (void) {return numIntersections() - numObjIntersections();}

  void ClearWorkDone (void);
  void Clear(void);
};

class _KDTreeNode;

class RayCastable {

private:
  RayCastable *                   _shadow_cache [MAX_LIGHTS];

protected:
  virtual void DrawFilled (void) const {bbox().drawFilled();}
  virtual void DrawWF (void) const {bbox().drawWireFrame();}

public: 
  enum DrawStyle {WIREFRAME, FILLED};
  virtual void Draw (DrawStyle flag = FILLED) const;
  virtual Vec4 GetRepresentativeColor (void) {return Vec4 (1., 1., 1.);}
  
  JPAList <_KDTreeNode>    _cells_we_inhabit;

  RayCastable();

  RayCastable(const RayCastable &R) { CopyFrom(R); }
  virtual ~RayCastable();

  void AddCell (_KDTreeNode *addme);
  void RemoveCell (_KDTreeNode *removeme);

  RayCastable& CopyFrom(const RayCastable&R) {         for (int i = 0; i < MAX_LIGHTS; i++)
    _shadow_cache[i] = R._shadow_cache[i];
  return *this; }

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

  virtual void Output(ostream &co) const
  { co << "Raycastable (no specialize output function)" << endl; }

  virtual RayCastable* CopyRayCastable() const = 0;

  //hit should be filled with the best so far
  //which, before we've hit anything, is NULL obj
  //and MAXFLOAT distance
  virtual void IntersectCertain(const Ray &R, Hit &hit) = 0;
  virtual void IntersectProbable(const Ray &R, Hit &hit) = 0;
  virtual void Intersect(const Ray &R, Hit &hit) = 0;

  virtual int IsConvex (void) const {return 1;}

  virtual int IntersectLinespace(const Ray &R1, const Ray &R2,
				 const Ray &R3, const Ray &R4,
				 const Vec4 &N1, const Vec4 &N2,
				 const Vec4 &N3, const Vec4 &N4) = 0;

  virtual void NormalAtPoint(const Vec4 &P, Vec4 &N) const = 0;

  virtual const Bounds3d &bbox (void) const = 0;

  virtual RayCastable *GetShadowCache (int i) {assert (i < MAX_LIGHTS); return _shadow_cache[i];}
  virtual void SetShadowCache (int i, RayCastable *obj) {assert (i < MAX_LIGHTS);_shadow_cache[i] = obj;}
  
  virtual void GetColor(const Vec4 &pt, Color &col, Hit &hit, int texlevel) const {col.diffuse.Set (1., 1., 1); return;}

  static int _should_check_success;
  static int _should_check_success_cost;
  static int _should_check_fail;
  static int _should_check_fail_cost;
  virtual int ShouldICheckYou (_KDTreeNode *checked_here_already);
  
  virtual float ObjAreaToBboxArea (void) {return 1.0;}
  
  int _num_touched_frame_id; //for objs_touched purposes
  int _num_visible_objs_frame_id; //for stat purposes - how many vis objs
  int _obj_used_frame_id; //for occlusion frontier vis purposes - whether this object was ever visible 
  int _num_visible_pixels;
  int _num_successful_intersects;
  int _num_intersect_attempts;
  int _ray_touched_id; //the id of the last ray to touch us.
  int _sent_to_gl_frame_id; //when we were last thrown to GL.
  
  int res0, res1, res2, res3, res4, res5, res6, res7; //reserved!
};

#endif

