#include "TGLClip.h"
#include "TGLIncludes.h"
#include "TGLRnrCtx.h"
#include "TGLManipSet.h"
#include "TGLFaceSet.h"
#include "TBuffer3D.h"
#include "TBuffer3DTypes.h"
namespace
{
class TGLClipPlaneLogical : public TGLLogicalShape
{
protected:
   virtual void DirectDraw(TGLRnrCtx & rnrCtx) const
   {
      glBegin(rnrCtx.IsDrawPassFilled() ? GL_QUADS : GL_LINE_LOOP);
      glNormal3d (0.0, 0.0, 1.0);
      glVertex3dv(fBoundingBox[4].CArr());
      glVertex3dv(fBoundingBox[7].CArr());
      glVertex3dv(fBoundingBox[6].CArr());
      glVertex3dv(fBoundingBox[5].CArr());
      glEnd();
   }
public:
   TGLClipPlaneLogical() : TGLLogicalShape() { fDLCache = kFALSE; }
   virtual ~TGLClipPlaneLogical() {}
   void Resize(Double_t ext)
   {
      fBoundingBox.SetAligned(TGLVertex3(-ext, -ext, 0),
                              TGLVertex3( ext,  ext, 0));
      UpdateBoundingBoxesOfPhysicals();
   }
};
class TGLClipBoxLogical : public TGLLogicalShape
{
protected:
   virtual void DirectDraw(TGLRnrCtx & rnrCtx) const
   {
      glEnable(GL_NORMALIZE);
      fBoundingBox.Draw(rnrCtx.IsDrawPassFilled());
      glDisable(GL_NORMALIZE);
   }
public:
   TGLClipBoxLogical() : TGLLogicalShape() { fDLCache = kFALSE; }
   virtual ~TGLClipBoxLogical() {}
   void Resize(const TGLVertex3 & lowVertex, const TGLVertex3 & highVertex)
   {
      fBoundingBox.SetAligned(lowVertex, highVertex);
      UpdateBoundingBoxesOfPhysicals();
   }
};
}
ClassImp(TGLClip);
TGLClip::TGLClip(const TGLLogicalShape & logical, const TGLMatrix & transform, const float color[4]) :
   TGLPhysicalShape(0, logical, transform, kTRUE, color),
   fMode      (kInside),
   fTimeStamp (1),
   fValid     (kFALSE)
{
   
   logical.StrongRef(kTRUE);
}
TGLClip::~TGLClip()
{
   
}
void TGLClip::Setup(const TGLVector3&, const TGLVector3&)
{
   
   
   
   Warning("TGLClip::Setup", "Called on base-class -- should be re-implemented in derived class.");
   
}
void TGLClip::Draw(TGLRnrCtx & rnrCtx) const
{
   
   
   glDepthMask(GL_FALSE);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glDisable(GL_CULL_FACE);
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   TGLPhysicalShape::Draw(rnrCtx);
   glPolygonMode(GL_FRONT, GL_FILL);
   glEnable(GL_CULL_FACE);
   glDisable(GL_BLEND);
   glDepthMask(GL_TRUE);
}
ClassImp(TGLClipPlane);
const float TGLClipPlane::fgColor[4] = { 1.0, 0.6, 0.2, 0.5 };
TGLClipPlane::TGLClipPlane() :
   TGLClip(* new TGLClipPlaneLogical, TGLMatrix(), fgColor)
{
   
   
   
   
   
   
   
   
   
   
   
   
   SetManip(EManip(kTranslateAll | kRotateX | kRotateY));
   TGLPlane plane(0.0, -1.0, 0.0, 0.0);
   Set(plane);
   fValid = kFALSE;
}
TGLClipPlane::~TGLClipPlane()
{
   
}
void TGLClipPlane::Setup(const TGLBoundingBox & bbox)
{
   
   Double_t extents = bbox.Extents().Mag();
   TGLClipPlaneLogical* cpl = (TGLClipPlaneLogical*) GetLogical();
   cpl->Resize(extents);
   if (!fValid) {
      SetTransform(TGLMatrix(bbox.Center(), BoundingBox().GetNearPlane().Norm()));
   }
   IncTimeStamp();
   fValid = kTRUE;
}
void TGLClipPlane::Setup(const TGLVector3& point, const TGLVector3& normal)
{
   
   
   
   
   
   
   
   
   
   TGLVector3 n(normal);
   Double_t extents = n.Mag();
   if (extents > 0)
   {
      n /= extents;
      TGLClipPlaneLogical* cpl = (TGLClipPlaneLogical*) GetLogical();
      cpl->Resize(extents);
      SetTransform(TGLMatrix(point, n));
      IncTimeStamp();
      fValid = kTRUE;
   }
   else
   {
      Warning("TGLClipPlane::Setup", "Normal with zero length passed.");
   }
}
void TGLClipPlane::Set(const TGLPlane& plane)
{
   
   
   TGLVertex3 oldCenter = BoundingBox().Center();
   TGLVertex3 newCenter = plane.NearestOn(oldCenter);
   SetTransform(TGLMatrix(newCenter, plane.Norm()));
   IncTimeStamp();
   fValid = kTRUE;
}
void TGLClipPlane::PlaneSet(TGLPlaneSet_t& set) const
{
   
   set.resize(1);
   set[0] = BoundingBox().GetNearPlane();
   set[0].Negate();
}
ClassImp(TGLClipBox);
const float TGLClipBox::fgColor[4] = { 1.0, 0.6, 0.2, 0.3 };
TGLClipBox::TGLClipBox() :
   TGLClip(* new TGLClipBoxLogical, TGLMatrix(), fgColor)
{
   
   
   
}
TGLClipBox::~TGLClipBox()
{
   
}
void TGLClipBox::Setup(const TGLBoundingBox& bbox)
{
   
   TGLVector3 halfLengths = bbox.Extents() * 0.2501;
   TGLVertex3 center      = bbox.Center() + halfLengths;
   TGLClipBoxLogical* cbl = (TGLClipBoxLogical*) GetLogical();
   cbl->Resize(center - halfLengths, center + halfLengths);
   IncTimeStamp();
   fValid = kTRUE;
}
void TGLClipBox::Setup(const TGLVector3& min_point, const TGLVector3& max_point)
{
   
   
   
   
   
   
   
   TGLClipBoxLogical* cbl = (TGLClipBoxLogical*) GetLogical();
   cbl->Resize(min_point, max_point);
   IncTimeStamp();
   fValid = kTRUE; 
}
void TGLClipBox::PlaneSet(TGLPlaneSet_t& set) const
{
   
   
   BoundingBox().PlaneSet(set);
   TGLPlaneSet_i i = set.begin();
   while (i != set.end()) {
      i->Negate();
      ++i;
   }
}
ClassImp(TGLClipSet);
TGLClipSet::TGLClipSet() :
   TGLOverlayElement(kViewer), 
   fClipPlane   (new TGLClipPlane),
   fClipBox     (new TGLClipBox),
   fCurrentClip (0),
   fAutoUpdate  (kTRUE),
   fShowClip    (kFALSE),
   fShowManip   (kFALSE),
   fManip       (new TGLManipSet)
{
   
}
TGLClipSet::~TGLClipSet()
{
   
   delete fClipPlane;
   delete fClipBox;
   delete fManip;
}
Bool_t TGLClipSet::MouseEnter(TGLOvlSelectRecord& selRec)
{
   
   
   return fManip->MouseEnter(selRec);
}
Bool_t TGLClipSet::MouseStillInside(TGLOvlSelectRecord& selRec)
{
   
   
   return fManip->MouseStillInside(selRec);
}
Bool_t TGLClipSet::Handle(TGLRnrCtx& rnrCtx, TGLOvlSelectRecord& selRec,
                          Event_t* event)
{
   
   
   return fManip->Handle(rnrCtx, selRec, event);
}
void TGLClipSet::MouseLeave()
{
   
   
   return fManip->MouseLeave();
}
void TGLClipSet::Render(TGLRnrCtx& rnrCtx)
{
   
   if (!fCurrentClip) return;
   rnrCtx.SetShapeLOD(TGLRnrCtx::kLODHigh);
   rnrCtx.SetDrawPass(TGLRnrCtx::kPassFill);
   if (fShowClip && ! rnrCtx.Selection())
   {
      fCurrentClip->Draw(rnrCtx);
   }
   if (fShowManip)
   {
      fManip->Render(rnrCtx);
   }
}
void TGLClipSet::FillPlaneSet(TGLPlaneSet_t& set) const
{
   
   if (fCurrentClip)
      fCurrentClip->PlaneSet(set);
}
void TGLClipSet::SetupClips(const TGLBoundingBox& sceneBBox)
{
   
   fLastBBox = sceneBBox;
   fClipPlane->Setup(sceneBBox);
   fClipBox  ->Setup(sceneBBox);
}
void TGLClipSet::SetupCurrentClip(const TGLBoundingBox& sceneBBox)
{
   
   fLastBBox = sceneBBox;
   if (fCurrentClip)
      fCurrentClip->Setup(sceneBBox);
}
void TGLClipSet::SetupCurrentClipIfInvalid(const TGLBoundingBox& sceneBBox)
{
   
   fLastBBox = sceneBBox;
   if (fCurrentClip && ! fCurrentClip->IsValid())
      fCurrentClip->Setup(sceneBBox);
}
void TGLClipSet::InvalidateClips()
{
   
   fClipPlane->Invalidate();
   fClipBox  ->Invalidate();
}
void TGLClipSet::InvalidateCurrentClip()
{
   
   if (fCurrentClip)
      fCurrentClip->Invalidate();
}
void TGLClipSet::GetClipState(TGLClip::EType type, Double_t data[6]) const
{
   
   
   
   
   
   switch (type)
   {
      case TGLClip::kClipNone:
         break;
      case TGLClip::kClipPlane:
      {
         if (!fClipPlane->IsValid())
            fClipPlane->Setup(fLastBBox);
         TGLPlaneSet_t planes;
         fClipPlane->PlaneSet(planes);
         data[0] = planes[0].A();
         data[1] = planes[0].B();
         data[2] = planes[0].C();
         data[3] = planes[0].D();
         break;
      }
      case TGLClip::kClipBox:
      {
         if (!fClipBox->IsValid())
            fClipBox->Setup(fLastBBox);
         const TGLBoundingBox & box = fClipBox->BoundingBox();
         TGLVector3 ext = box.Extents();
         data[0] = box.Center().X();
         data[1] = box.Center().Y();
         data[2] = box.Center().Z();
         data[3] = box.Extents().X();
         data[4] = box.Extents().Y();
         data[5] = box.Extents().Z();
         break;
      }
      default:
         Error("TGLClipSet::GetClipState", "invalid clip type '%d'.", type);
         break;
   }
}
void TGLClipSet::SetClipState(TGLClip::EType type, const Double_t data[6])
{
   
   
   
   
   
   
   switch (type) {
      case TGLClip::kClipNone: {
         break;
      }
      case TGLClip::kClipPlane: {
         TGLPlane newPlane(-data[0], -data[1], -data[2], -data[3]);
         fClipPlane->Set(newPlane);
         break;
      }
      case TGLClip::kClipBox: {
         
         
         const TGLBoundingBox & currentBox = fClipBox->BoundingBox();
         TGLVector3 shift(data[0] - currentBox.Center().X(),
                          data[1] - currentBox.Center().Y(),
                          data[2] - currentBox.Center().Z());
         fClipBox->Translate(shift);
         
         TGLVector3 currentScale = fClipBox->GetScale();
         TGLVector3 newScale(data[3] / currentBox.Extents().X() * currentScale.X(),
                             data[4] / currentBox.Extents().Y() * currentScale.Y(),
                             data[5] / currentBox.Extents().Z() * currentScale.Z());
         fClipBox->Scale(newScale);
         break;
      }
   }
}
TGLClip::EType TGLClipSet::GetClipType() const
{
   
   
  TGLClip::EType type;
   if (fCurrentClip == 0) {
      type = TGLClip::kClipNone;
   } else if (fCurrentClip == fClipPlane) {
      type = TGLClip::kClipPlane;
   } else if (fCurrentClip == fClipBox) {
      type = TGLClip::kClipBox;
   } else {
      Error("TGLClipSet::GetClipType" , "Unknown clip type");
      type = TGLClip::kClipNone;
   }
   return type;
}
void TGLClipSet::SetClipType(TGLClip::EType type)
{
   
   
   switch (type) {
      case TGLClip::kClipNone: {
         fCurrentClip = 0;
         break;
      }
      case TGLClip::kClipPlane: {
         fCurrentClip = fClipPlane;
         break;
      }
      case TGLClip::kClipBox: {
         fCurrentClip = fClipBox;
         break;
      }
      default: {
         Error("TGLClipSet::SetClipType" , "Unknown clip type");
         break;
      }
   }
   fManip->SetPShape(fCurrentClip);
}