
#ifndef _JL_UI_Thread_H_
#define _JL_UI_Thread_H_


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

#include "forms.h"
#include "new_forms.h"

#include "examine_nav.h"
#include "jltrackball.h"
#include "jlmouse.h"
#include "glwin.h"
#include "thread.h"

#include "jpalist.h"

class Navigator;
class WorkRec;
class Frustum;
class RayCastable;

class _KDTreeNode;

#define SET_LABEL(var,uielement) if (Render_Worker::_frame_req.var) \
{  fl_set_object_label (_Main_Form->uielement, "on"); }\
else \
fl_set_object_label (_Main_Form->uielement, "off");

#define BLEND_ON  glEnable (GL_BLEND) 
#define Z_ON  glEnable (GL_DEPTH_TEST) 
#define BLEND_OFF  glDisable (GL_BLEND)
#define Z_OFF  glDisable (GL_DEPTH_TEST)

void DrawFrustumPlanes (WorkRec *wr, CycleState &CS, float planesize, const Vec4 &basecolor, int back = 0);

class UI_Thread : public Thread {
  friend WorkRec;
  friend Examine_Nav;

private:
  FD_Main *_Main_Form;
  FD_Instrumentation	*_Stats_Form;
  FD_IO	                *_IO_Form;
  FD_Debug	                *_Debug_Form;
  JLMouse				_mouse, _debug_mouse;
  Navigator			* _navigators [NUMBUTTONS];
  //alt_navigators work when Alt is pressed down
  Navigator			* _alt_navigators [NUMBUTTONS];
  Navigator			* _debug_navigator;

  //used by debug win
  JLTrackball                     _TB; 
  Vec4                            _center_of_interest;
  Vec4                            _start_eye_dir;
  real                            _dist_from_center;
  real                            _zoom_speed;
  int					_first_work;
  int					_navigated;
  int					_debug_navigated;

  real                     *_frame_rate_history;
  real                     *_rays_cast_history;
  real                     *_simple_count;
  int                      _cur_frame_num; //in terms of _frame_rate history

  GLWin * _glwin; //we own the glwin
  GLWin * _debug_glwin; //we own the debug glwin
  static UI_Thread	*_CB_Object;
  static GlobalState	*_CB_GS;

  friend void QuitHit(FL_OBJECT *, long);
  friend void ThreadNumChanged(FL_OBJECT *, long);
  friend void FrameRateChanged(FL_OBJECT *, long);
  friend void AmbientChanged(FL_OBJECT *, long);
  friend void DiffuseChanged(FL_OBJECT *, long);
  friend void SpecularChanged(FL_OBJECT *, long);
  friend void ReflectionDepthChanged(FL_OBJECT *, long);
  friend void RefractionDepthChanged(FL_OBJECT *, long);
  friend void TextureChoiceChanged(FL_OBJECT *, long);
  friend void WinSizeChanged(FL_OBJECT *, long);
  friend void DepthComplexityHit(FL_OBJECT *, long);
  friend void ShadowDepthChanged(FL_OBJECT *, long);
  friend void NumPixelsPerFrameChanged(FL_OBJECT *obj, long);
  friend void PegHit(FL_OBJECT *, long);
  friend void HWCutoffChanged(FL_OBJECT *, long);
  friend void HeadlightHit(FL_OBJECT *, long);		
  friend void NeverShadowHit(FL_OBJECT *, long);		
  friend void PegasMaxHit(FL_OBJECT *, long);
  friend void DontHit(FL_OBJECT *, long);
  friend void DontHit2(FL_OBJECT *, long);
  friend void SliderChanged(FL_OBJECT *, long);
  friend void CounterChanged(FL_OBJECT *, long);
  friend void ButtonHit(FL_OBJECT *, long);
  friend void KDTreeHit(FL_OBJECT *, long);
  friend void FrustaHit(FL_OBJECT *, long);
  friend void DCounterHit(FL_OBJECT *, long);
  friend void StyleHit(FL_OBJECT *, long);
  friend void CurPixelChoice(FL_OBJECT *, long);
  friend void InputChanged(FL_OBJECT *, long);
  friend void LogHit(FL_OBJECT *, long);
  friend void QueryHit (FL_OBJECT *obj, long which);

  friend void GetCurPixelWR (int &rwown, int &frustumnum);
  friend WorkRec *GetWorkRec (void);

  int _which_frustum, _which_rw;
  float _frusta_plane_size;
  void DrawFrustum (Frustum &f, CycleState &CS);

  void ShowWhatRayIntersected (int x, int y, CycleState &CS, _KDTreeNode *start, float t); //takes a t if we hit inside the current cell; otherwise, if t passed in as -1, we traverse the k-d tree in the usual way.
  float *GenerateGLTextureMap (CycleState &CS);
  void DrawFrustumInPixelBuffer (const CycleState &CS, const Frustum &frustum);
  void DrawFrustumContents (WorkRec *betty);
  void DrawRays (WorkRec *curfrustum);
  void DrawGLFirstHalf (CycleState &CS);
  void DrawGLSecondHalf (CycleState &CS);
  void UpdateMainGUI(CycleState &CS);
  void DrawAllFrustaLeaves (CycleState &CS);
  void DrawOcclusionFrontier (CycleState &CS);
  int _show_x, _show_y; //x and y typed into the edit window
  float _fp_dist; //dist from eye that film plane is drawn in

  void DrawExitPts (Frustum &f, _KDTreeNode *cell, CycleState &CS);
  void DrawExitPt (int face, const Vec4 &pt);
  float _exit_box_size; //for when we show exit pts

  void Animate (WorkRec *wr, int showpurefc, CycleState &CS);
  void GetViewVecTBounds (_KDTreeNode *curcell, Frustum &f, CycleState &CS, float &firstentryt, float &lastexitt);
  float CalcEndT (WorkRec *wr, CycleState &CS);
  _KDTreeNode *_animate_cell;
  float _animate_t;
  WorkRec *_old_animate;
  float _animate_speed;

  int _debug_cam_moved;
  void InitDebugView (void);
  void EyePtDebugView (void);
  void SetDebugCam (void);

  void PrintState (void);

  void DrawFrustaCells (void);
  int _frusta_level;

  _KDTreeNode *_current_cell; //in cell vis
  void NewCurCell (_KDTreeNode *newcur);
  CycleState *_CS;

  JPAList <_KDTreeNode> _cells_viewed;

  void DrawFrustaScrnSpace (CycleState &CS);
  void DrawObj (RayCastable *obj);
  void DrawCell (_KDTreeNode *cell);
  void GetMouseCoords (int &x, int &y) const;

  void UpdateDebugWin (CycleState &CS);

  void DrawScreenSpaceDebugInfo(CycleState &CS);
  void DrawFrustumGL (const Frustum &f);
  
  //the query mechanism
  JPAList <RayCastable> _obj_bboxes;
  JPAList <RayCastable> _obj_filled;
  JPAList <Vec4> _rays;
  JPAList <float> _ray_ts;
  JPAList <Vec4> _ray_colors;
  enum QueryReq {
    RaysHitCurObj = 0,
    RaysHitCurCell,
  } _cur_query;
  void ClearOldQuery(void);
  void AddQueryObj (RayCastable *obj, int filled);
  void AddQueryRay (const Vec4 &D, float t, const Vec4 &color);
  void RenderQuery (void);
  
  RayCastable *_cur_obj; //that we're pointing to.
  
  void QueryRaysCurObj (void);
  void QueryRaysCurCell (void);
public: 
  UI_Thread();
  UI_Thread(const UI_Thread &U) { CopyFrom(U); }
  virtual ~UI_Thread();

  UI_Thread& CopyFrom(const UI_Thread&);
  UI_Thread& operator=(const UI_Thread &U) { return CopyFrom(U); }

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


  void showNewTimings ();

  virtual void InitThread(GlobalState&);

  virtual void BeginCycle(CycleState&, GlobalState&);
  virtual int Work(CycleState&, GlobalState&);
  virtual int AnyWorkLeft(CycleState&, GlobalState&);
  virtual void EndCycle(CycleState&, GlobalState&);

  virtual void ShutdownThread();

};

#endif
