//
//  Vrml 97 library
//  Copyright (C) 1998 Chris Morley
//
//  %W% %G%
//  VrmlNodePlaneSensor.cpp

#include "config.h"
#include "VrmlNodePlaneSensor.h"
#include "VrmlNodeType.h"

#include "MathUtils.h"
#include "System.h"
#include "Viewer.h"
#include "VrmlScene.h"

// PlaneSensor factory. 

static VrmlNode *creator( VrmlScene *scene ) 
{
  return new VrmlNodePlaneSensor(scene);
}


// Define the built in VrmlNodeType:: "PlaneSensor" fields

VrmlNodeType *VrmlNodePlaneSensor::defineType(VrmlNodeType *t)
{
  static VrmlNodeType *st = 0;

  if (! t)
    {
      if (st) return st;		// Only define the type once.
      t = st = new VrmlNodeType("PlaneSensor", creator);
    }

  VrmlNodeChild::defineType(t);	// Parent class
  t->addExposedField("autoOffset", VrmlField::SFBOOL);
  t->addExposedField("enabled", VrmlField::SFBOOL);
  t->addExposedField("maxPosition", VrmlField::SFVEC2F);
  t->addExposedField("minPosition", VrmlField::SFVEC2F);
  t->addExposedField("offset", VrmlField::SFVEC3F);
  t->addEventOut("isActive", VrmlField::SFBOOL);
  t->addEventOut("translation_changed", VrmlField::SFVEC3F);
  t->addEventOut("trackPoint_changed", VrmlField::SFVEC3F);

  return t;
}

VrmlNodeType *VrmlNodePlaneSensor::nodeType() const 
{ return defineType(0); }


VrmlNodePlaneSensor::VrmlNodePlaneSensor( VrmlScene *scene ) :
  VrmlNodeChild(scene),
  d_autoOffset(true),
  d_enabled(true),
  d_maxPosition(-1.0, -1.0),
  d_isActive(false),
  d_parentTransform(0)
{
  setModified();
}

// need copy constructor for d_parentTransform ...

VrmlNodePlaneSensor::~VrmlNodePlaneSensor()
{
}


VrmlNode *VrmlNodePlaneSensor::cloneMe() const
{
  return new VrmlNodePlaneSensor(*this);
}


VrmlNodePlaneSensor* VrmlNodePlaneSensor::toPlaneSensor() const
{ return (VrmlNodePlaneSensor*) this; }


ostream& VrmlNodePlaneSensor::printFields(ostream& os, int indent)
{
  if (! d_autoOffset.get()) PRINT_FIELD(autoOffset);
  if (! d_enabled.get())    PRINT_FIELD(enabled);
  if (! FPEQUAL(d_maxPosition.x(), -1.0) ||
      ! FPEQUAL(d_maxPosition.y(), -1.0))
    PRINT_FIELD(maxPosition);
  if (! FPEQUAL(d_minPosition.x(), -1.0) ||
      ! FPEQUAL(d_minPosition.y(), -1.0))
    PRINT_FIELD(minPosition);

  if (! FPZERO(d_offset.x()) ||
      ! FPZERO(d_offset.y()) ||
      ! FPZERO(d_offset.z()) )
    PRINT_FIELD(offset);

  return os;
}

// Cache a pointer to (one of the) parent transforms for converting
// hits into local coords.

void VrmlNodePlaneSensor::accumulateTransform( VrmlNode *parent )
{
  d_parentTransform = parent;
}

VrmlNode* VrmlNodePlaneSensor::getParentTransform() { return d_parentTransform; }


// This is not correct. The local coords are computed for one instance,
// need to convert p to local coords for each instance (DEF/USE) of the 
// sensor...

void VrmlNodePlaneSensor::activate( double timeStamp,
				    bool isActive,
				    double *p )
{
  // Become active
  if ( isActive && ! d_isActive.get() )
    {
      d_isActive.set(isActive);

      float V[3] = { (float)p[0], (float)p[1], (float)p[2] };
      double M[4][4];
      inverseTransform( M );
      VM( V, M, V );
      d_activationPoint.set( V[0], V[1], V[2] );
#if 0
      theSystem->warn(" planesensor: activate at (%g %g %g)\n",
		      p[0],p[1],p[2]);
      theSystem->warn(" planesensor: local coord (%g %g %g)\n",
		      V[0],V[1],V[2]);
#endif
      eventOut( timeStamp, "isActive", d_isActive );
    }

  // Become inactive
  else if ( ! isActive && d_isActive.get() )
    {
#if 0
      theSystem->warn(" planesensor: deactivate\n");
#endif
      d_isActive.set(isActive);
      eventOut( timeStamp, "isActive", d_isActive );

      // auto offset
      if ( d_autoOffset.get() )
	{
	  d_offset = d_translation;
	  eventOut( timeStamp, "offset_changed", d_offset );
	}
    }

  // Tracking
  else if ( isActive )
    {
      float V[3] = { (float)p[0], (float)p[1], (float)p[2] };
      double M[4][4];
      inverseTransform( M );
      VM( V, M, V );
#if 0
      theSystem->warn(" planesensor: track at (%g %g %g)\n",
		      p[0],p[1],p[2]);
      
      theSystem->warn(" planesensor: local cd (%g %g %g)\n",
		      V[0],V[1],V[2]);
#endif
      d_trackPoint.set( V[0], V[1], V[2] );
      eventOut( timeStamp, "trackPoint_changed", d_trackPoint );

      float t[3];
      t[0] = V[0] - d_activationPoint.x() + d_offset.x();
      t[1] = V[1] - d_activationPoint.y() + d_offset.y();
      t[2] = 0.0;

      if ( d_minPosition.x() == d_maxPosition.x() )
	t[0] = d_minPosition.x();
      else if ( d_minPosition.x() < d_maxPosition.x() )
	{
	  if (t[0] < d_minPosition.x())
	    t[0] = d_minPosition.x();
	  else if (t[0] > d_maxPosition.x())
	    t[0] = d_maxPosition.x();
	}

      if ( d_minPosition.y() == d_maxPosition.y() )
	t[1] = d_minPosition.y();
      else if ( d_minPosition.y() < d_maxPosition.y() )
	{
	  if (t[1] < d_minPosition.y())
	    t[1] = d_minPosition.y();
	  else if (t[1] > d_maxPosition.y())
	    t[1] = d_maxPosition.y();
	}

      d_translation.set( t[0], t[1], t[2] );
      eventOut( timeStamp, "translation_changed", d_translation );
    }
}

// Set the value of one of the node fields.

void VrmlNodePlaneSensor::setField(const char *fieldName,
				   const VrmlField &fieldValue)
{
  if TRY_FIELD(autoOffset, SFBool)
  else if TRY_FIELD(enabled, SFBool)
  else if TRY_FIELD(maxPosition, SFVec2f)
  else if TRY_FIELD(minPosition, SFVec2f)
  else if TRY_FIELD(offset, SFVec3f)
  else
    VrmlNodeChild::setField(fieldName, fieldValue);
}

