#ifndef PLANE_CACHE_H
#define PLANE_CACHE_H

#include "assert.h"
#include "vec4.h"
#include "camera.h"

typedef Vec4 Plane;

struct PlaneStruct {
  int dim;
  int where;
  Plane plane;
  char calced;
  struct PlaneStruct *next;
};

class PlaneCache {
public:
  PlaneCache (void) : _biggestdim (0), _list_head (NULL), _list_tail (NULL), _plane_cache (NULL), _eye_ray_cache(NULL), _width (-1.), _height (-1.), _fovx (-1), _fovy(-1), _dot_product_cache (NULL)
{
}

  ~PlaneCache();

  void initialize (int width, int height);



  //uses cache, offsets where by +0.5 to get middle of pixel.
  //returns 1 if we had to actually calc it
  int calcPlane (int dim, int where, Plane &plane);

  void clear(void); 

  void getHats (Vec4 &xhat, Vec4 &yhat, Vec4 &zhat) {xhat = _xhat; yhat = _yhat; zhat = _zhat;}

  void newViewpoint (Camera *cam);

  void GetFilmPlane (Plane &plane) {plane = _filmplane;}

  void ComputeEyeRayDirection(int x, int y, Vec4 &dir);
  Vec4 ComputeEyeRayDirection(int x, int y);

//given a t along the given ray, returns the corresponding distance along
//the viewvec. 
  float ComputeViewVecT (int x, int y, float rayt);

protected:
  PlaneStruct *_plane_cache;
  PlaneStruct *_list_head;
  PlaneStruct *_list_tail;
  int _biggestdim; //# of pixels in biggest dimension
  Plane _filmplane;
  Vec4 *_eye_ray_cache;
  float *_dot_product_cache;

  //viewpoit info
  Vec4 _xhat, _yhat, _zhat;

  Vec4 _eye;
  float _width, _height;
  float _fovx, _fovy;

  void realCalcPlane (int dim, int where, Plane &plane);

  void makeEyeRayAndDPCache ();

  inline PlaneStruct *accessPlane (int dim, int where)
  {
    assert (_plane_cache);
    assert ((dim == 0) || (dim == 1));
    assert ((where < _biggestdim) && (where >= 0));
    PlaneStruct *daplane = &_plane_cache[dim * _biggestdim + where];
    assert (daplane->dim == dim);
    assert (daplane->where == where);
    return daplane;
  }


  inline void plane_from_point_normal(const Vec4 &p, const Vec4 &n, Plane &H);

};
#endif
