/*****************************
File:     RealtimeAttractor.h
Language:   C++ (header)
Project:    H3DNetworkingUtils
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is H3DNetworkingUtils v1.0.

The Initial Developer of the Original Code is CSIRO.
Portions created by the Initial Developer are Copyright (C) 1009 CSIRO. All Rights Reserved.
Contributor(s):
    Chris Gunn  <Chris.Gunn@csiro.au> <ChrisJGunn@gmail.com>
***************************/

/// \file RealtimeAttractor.h
/// \brief Header file for RealtimeAttractor, an Attractor that modifies its attract point at haptics rates.

#ifndef RealtimeAttractor_H
#define RealtimeAttractor_H
//
//
// -----------------------------------------------------------------------------


#include "H3DNetworkingUtils/Config.h"
#include "H3DNetworkingUtils/Attractor.h"
#include <H3DUtil/Threads.h>
#include "H3DUtil/TimeStamp.h"

namespace H3DNetworkingUtils {
   
/// \class RealtimeAttractor
/// The RealtimeAttractor class is an Attractor that can have its "suck-to" point
/// changed at real time (haptic) rates. (~1000hz).  Typically you would route
/// from another haptic tool's haptic loop. Route to realtimePoint - don't use
/// the parent class's point field.
/// This class works with 1 or 2 haptic devices.   \n
/// <b>Examples:</b>
///   - <a href="../../examples/RealtimeAttractorTestServer.x3d">RealtimeAttractorTestServer.x3d</a>
///   - <a href="../../examples/RealtimeAttractorTestClient.x3d">RealtimeAttractorTestClient.x3d</a>

class H3D_NETWORKING_UTILS_DLL_SPEC RealtimeAttractor : public Attractor {
public:
   class LockableSFVec3f;
	
	/// Constructor
   RealtimeAttractor(H3D::Inst<LockableSFVec3f> _realtimePoint = 0);

   /// Destructor
	virtual ~RealtimeAttractor();
	
	/// X3D database
   static H3D::H3DNodeDatabase database;

   /// The global point of attraction which can be updated in the haptics thread.
	/// This serves the same purpose as theh 'point' field of the parent class but 
	/// is reproduced with a different name to emphasise the issue that it is set 
	/// and updated from the haptics thread at haptics rates \n
	/// It should be routed to 
	/// from a remoteField with isHapticField set to true
   /// access type: inputOutput \n
   /// basic type: SFVec3f \n
   /// default value: 0 0 0
	auto_ptr<LockableSFVec3f> realtimePoint;

   /// An SFVec3f that has thread-locked access
	class LockableSFVec3f : public H3D::AutoUpdate<H3D::SFVec3f> {
   public:
     
     virtual const H3D::Vec3f & getValue(int id = 0) {
       point_lock.lock();
       ret_value = H3D::SFVec3f::getValue(id);

       point_lock.unlock();
       return ret_value;
     }

	  virtual void propagateEvent( Event e ) {
		  point_lock.lock();
		  H3D::AutoUpdate<H3D::SFVec3f>::propagateEvent(e);
		  point_lock.unlock();
	  }

	  virtual void update() {
		 value = static_cast<SFVec3f*>(event.ptr)->getValue();
	  }

     virtual void setValue(H3D::Vec3f const & val, int id = 0) {
       point_lock.lock();
		 H3D::SFVec3f::setValue(val, id);
       point_lock.unlock();

     }

   private:
     H3DUtil::MutexLock point_lock;
     H3D::Vec3f ret_value;
   };

protected:
	virtual Attractor::AttractForce * createAttractForce(H3D::H3DTime t, 
														 	 H3D::Vec3f const & _point,
															 bool _repel,
															 H3D::H3DFloat maxForce, 
															 H3D::H3DFloat maxDeltaForce,
															 H3D::H3DFloat _radius,
															 H3D::H3DFloat _scale,
															 H3D::Vec3f const & offset);

	// AttractForce is the same as the one in the parent class, except when the 
	// point of attraction is required during the haptics cycle, it retrieves it 
	// from the relatimePoint field, because it may have been updated at haptics 
	// rates, or at least faster than graphics rates.
   class RTAttractForce : public Attractor::AttractForce {
	public:
		RTAttractForce(H3D::H3DTime t, 
                     H3D::Vec3f const & point,
                     bool repel,
                     H3D::H3DFloat maxForce, 
                     H3D::H3DFloat maxDeltaForce,
                     H3D::H3DFloat radius,
                     H3D::H3DFloat scale,
							H3D::Vec3f const & offset,
							Attractor * attrP) :
		AttractForce(t, point, repel, maxForce, maxDeltaForce, radius, scale, offset, attrP) {}

	protected:
		virtual H3D::Vec3f getLatestAttractionPoint() {
			return static_cast<RealtimeAttractor*>(attrP.get())->realtimePoint->getValue();
		}
	};

   virtual H3D::Vec3f getPoint()  {   
      return realtimePoint->getValue();
   }
   // Overrides the base class's function to get the value from the field
   // This gets called each haptics cycle, so it is accessing a field in
   // the haptics loop, and must have locking protection.

};

}

#endif
