
#ifndef _JL_ReconMap_H_
#define _JL_ReconMap_H_

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

#include "cyclestate.h"
#include "render_worker.h"

#include "globalstate.h"

extern GlobalState *truly_GS;
enum ReconType { RECON_EMPTY, RECON_COLOR, RECON_TOKEN };

#ifdef JLLIB_LINUX
	#define GET_PIX_VAL(r,g,b)  \
	        ((b >> 16) | \
		   ((g >> 8) & 0xFF00) | \
		   (r & 0xFF0000))

#elif defined (XOLAS)
	#define GET_PIX_VAL(r,g,b) \
	           (((b >> 8) & 0x00FF00) | \
			((g) & 0x0FF0000) | \
			((r << 8) & 0x0FF000000))

#else
	#define GET_PIX_VAL(r,g,b) \
   	           ((r >> 16) | \
			((g >> 8) & 0x00FF00) | \
			(b & 0x0FF0000) )
#endif

class ReconNode {
public:
  
  //provisional r, g, b
  int		_prov_r, _prov_g, _prov_b;
  
  //the id's have to be confirmed in order for the pixel to be
  //considered done. This is so shared pixels aren't really used
  //until the frusta that share them are both subdivided equally.
  int             _frame_age, _frame_id, _sub_frame_id;
  int _confirmed_frame_id, _confirmed_sub_frame_id;

  int _real_r, _real_g, _real_b;
  float _realf_r, _realf_g, _realf_b; //for debugging, really
  int		_type; //state of real ray

  int _x, _y;

  ReconNode(void) { _type = RECON_EMPTY; _x = -1; _y = -1; _frame_age = _frame_id = _confirmed_frame_id = -1; _sub_frame_id = _confirmed_sub_frame_id = 0; }
  void SetLocation (int x, int y) {_x = x; _y = y;}

  inline void UpdateProvisional (int r, int g, int b) {
    _prov_r = r;
    _prov_g = g;
    _prov_b = b;
  }


  //XXX this ain't MP safe, but reconstruction isn't MP yet, so no biggie.
  inline ReconNode *StoreProvisional (int r, int g, int b, CycleState &CS) {
    UpdateProvisional(r,g,b);
		  
    CS._sample_list->Add (this);
    _type = RECON_COLOR;
    return this;
  }

  void BestColor (int frameid, int subframeid, int &r, int &g, int &b) {
    if ((_frame_id == frameid) && (_sub_frame_id >= subframeid) && (frameid == _confirmed_frame_id) && (_confirmed_sub_frame_id >= subframeid))
      {
	//if we've got the pixel from this frame, use it!
	r = _real_r;
	g = _real_g;
	b = _real_b;
      }
    else
      {
	r = _prov_r;
	g = _prov_g;
	b = _prov_b;
      }
  }
};


class GLPic;


class ReconMap {

private:
  JPAList <RayCastable> _gl_objs; //objs to draw using GL
  int _gl_obj_frameid;
  
  int			_size;
  ReconNode	*_nodes;
  float         *_depth_map;
  int findNextTop (int leftstart, int *upper, int *lower);
  void HWInterpolate (CycleState &CS, int left, int top, int size);
  inline int ShouldInterp (ReconNode *var1, ReconNode *damiddle, ReconNode *var2, CycleState &CS)
  {
#ifdef NORMAL_INTERPOLATION
    if (damiddle->_frame_id == frameid) return 0;
    else return 1;
#endif
    
    //if da middle doesn't have a pixel from this view, get one
    if ( (damiddle->_frame_id < CS._frame_id) || (damiddle->_confirmed_frame_id < CS._frame_id) ) return 1;
    
    //if middle is from this or a later subframe, we're done.
    if ( (damiddle->_sub_frame_id >= CS._sub_frame_id) && (damiddle->_confirmed_sub_frame_id >= CS._sub_frame_id) )return 0;
    
    if (truly_GS->_disabled_flags & DISABLE_FRAME_BLEND) return 0;
    
    //at this point we know that middle is from an earlier subframe
    if (CS._frame_id != CS._next->_frame_id) return 1;// we've moved; raycache is invalid; just interp!
    
    //try to blend - if middle pixel is from an older subframe than the parents
    //and on the same object, interpolate its value from the parents.
    
    if ( (damiddle->_sub_frame_id > var1->_sub_frame_id) || (damiddle->_sub_frame_id > var2->_sub_frame_id) ) { 
      //cerr << "kid sample older than parents!" << endl; 
      return 0; }
    
    //if the parents hit the same thing, interpolate
    RayCache *rc1 = GET_RAY_INFO_PTR (var1->_x, var1->_y);
    RayCache *rc2 = GET_RAY_INFO_PTR (var2->_x, var2->_y);
    
    if (rc1->hitsomething == YES)
      {
	if ((rc2->hitsomething == YES) && (rc2->objhit.obj == rc1->objhit.obj))
	  {
	    return 1;
	  }
      }
    else
      { //XXX not sure of this case: if they're both
	//dunno, then return 1. if they're both no, there could be an obj between them
	//so maybe we should let it go
	//			if (rc2->hitsomething != YES) return 1;
      }
    
    //we have a pixel from an earlier rendering of 
    //the same view; keep it.
    return 0;
  }
  
public: 
  ReconMap();
  ReconMap(const ReconMap &R) { _size=0; _nodes=NULL; _depth_map = NULL; CopyFrom(R); }
  virtual ~ReconMap();
  
  void Kill();
  void SetSize(int size);
  
  ReconMap& CopyFrom(const ReconMap&);
  ReconMap& operator=(const ReconMap &R) { return CopyFrom(R); }
  
  inline ReconNode* Get(int x, int y)
  {
    if ((x >= 0) && (x < _size) && (y >= 0) && (y < _size))
      return &(_nodes[y*_size + x]);
    else
      {
	cerr << "asked for " << x << ", " << y << " in reconmap of size " << _size << endl;
	assert (0);
	return NULL;
      }
  }
  
  
  int Size() { return _size; }
		int x() { return _size; }
  int y() { return _size; }
  
  void Confirm (int x, int y, int frameid, int subframeid);
  
  void AddObjForGL (RayCastable *obj, int frameid);
  //caller has to set up the camera correctly
  void DrawGLList (int frameid);
  int HasGLList (int frameid);
  
  void SetRealSample(int confirmed, int frameid, int subframeid, int frameage, int x, int y, float r, float g, float b, GLPic &p) {
    ReconNode *RN = Get(x, y);
    
    //reals are for human readability 
    //(debugging) purposes
    RN->_realf_r = r;
    RN->_realf_g = g;
    RN->_realf_b = b;

    int intr = r * 0x00FFFFFF;
    int intg = g * 0x00FFFFFF;
    int intb = b * 0x00FFFFFF;
    RN->_real_r = intr;
    RN->_real_g = intg; 
    RN->_real_b = intb;
				
    RN->UpdateProvisional (intr, intg, intb);
				
    //update the pixel buffer
    assert (x != -1);
    p.Pixel (x, y, GET_PIX_VAL (intr, intg, intb));

    if (confirmed)
      {
	assert ((RN->_confirmed_frame_id != frameid) || (RN->_confirmed_sub_frame_id != subframeid));

	RN->_confirmed_frame_id = frameid;
	RN->_confirmed_sub_frame_id = subframeid;
      }

    assert (RN->_sub_frame_id != subframeid || RN->_frame_id != frameid);
    //setting frame id has to be at end
    RN->_frame_age = frameage;
    RN->_sub_frame_id = subframeid;
    RN->_frame_id = frameid;

				//setting type needs to be at end
    RN->_type = RECON_COLOR;
  }


  float CalcFractionSampled();


  void Reset();


  void EvalToken(int x, int y);
  void BiLerp(int x, int y, int len);
  void Reconstruct(int x, int y, int len);
  void Reconstruct(GLPic &P);

  void Draw(CycleState &CS);

//guaranteed that all samples in here are from the same frame
  void Reconstruct2(CycleState &CS, GLPic &P);

  void CopyAll (GLPic &P);
  //		void CopySparse (GLPic &P);

  void AddDepthSample (int x, int y, float depth);
  //caller has to set up the camera correctly for this too.
  void DrawDepthMap (void);
};

#endif
