/*****************************
File:      RemoteField.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 RemoteField.h
/// \brief Header file for RemoteField, a base class for the remote fields.

#ifndef RemoteField_H
#define RemoteField_H


#include "H3DNetworkingUtils/Config.h"
#include "H3DNetworkingUtils/ThreadSafeBool.h"
#include <H3D/X3DChildNode.h>
#include <H3D/SFBool.h>
#include <H3D/SFInt32.h>
#include <H3D/SFString.h>
#include <H3D/SFTime.h>
#include <H3D/TraverseInfo.h>
#include <H3D/SFFloat.h>
#include <H3DUtil/AutoRef.h>
#include <H3DUtil/H3DBasicTypes.h>
#include <iostream>
#include <iomanip>
#include <H3D/PeriodicUpdate.h>
#include <H3D/Scene.h>
#include <HAPI/HAPIForceEffect.h> 

namespace H3DNetworkingUtils {

/// \class RemoteField
/// The RemoteField class is an abstract base class which which writes to and reads from a network connection.  This
/// allows fields to update due to changes to a field on a different machine.
/// This is an abstract class, and is instantiated in the RemoteSF<T> template
/// class.

// forward reference
class RemoteConnection;

class H3D_NETWORKING_UTILS_DLL_SPEC RemoteField : public H3D::X3DChildNode {

public:
   struct H3D_NETWORKING_UTILS_DLL_SPEC Enabled : public H3D::SFBool {
      virtual void setValue(bool const & val, int id = 0) {
         l.lock(); H3D::SFBool::setValue(val, id);  l.unlock();}

      virtual void update() {
         l.lock(); H3D::SFBool::update(); l.unlock();}

      const bool & getValue(int id = 0) {
         l.lock(); g = H3D::SFBool::getValue(id); l.unlock(); return g;
      }
      H3D::MutexLock l;
      bool g;
   };

   RemoteField(H3D::Inst<H3D::SFInt32> _id = 0,
               H3D::Inst<H3D::SFFloat> _printPeriod = 0,
               H3D::Inst<H3D::SFBool> _sendOnceOnConnect = 0,
               H3D::Inst<Enabled> _enabled = 0,
               H3D::Inst<H3D::SFBool> _bufferReceivedVals = 0,
               H3D::Inst<H3D::SFString> _bufferStrategy = 0,
               H3D::Inst<H3D::SFTime> _remoteTimestamp = 0,
               H3D::Inst<H3D::SFBool> _timestamps = 0,
               H3D::Inst<H3D::SFBool> _isHapticField = 0 );

   static H3D::H3DNodeDatabase database;

   virtual void initialize();

   virtual void traverseSG(H3D::TraverseInfo & ti);

   virtual void readField();
   // Read the parts the field into the received field.

   virtual void writeField() = 0;
   // Write the parts of a fields value into the sending packet.
   // Must be supplied for specific instantiations, to send the
   // correct parts of the value.

   // Public fields.

   /// The id of the field, which should have a matching field on the other machine \n
   /// access type: initializeOnly \n
   /// basic type: SFInt32 \n
   /// default value: 0
   auto_ptr< H3D::SFInt32 >    fieldId; // Identifies which remote field this is.

   /// Used for prinitng statistics \n
   /// access type: initializeOnly \n
   /// basic type: SFFloat \n
   /// default value: 0.0
   auto_ptr< H3D::SFFloat >    printPeriod;

   /// If false, (and if periodic send if false), values will only be sent across the network when they change \n
   /// If true, they will also be sent as soon as connection is estanlished \n
   /// access type: inputOutput \n
   /// basic type: SFBool \n
   /// default value: false
   auto_ptr< H3D::SFBool  >    sendOnceOnConnect;

   /// Switches the remote field on \n
   /// access type: inputOutput \n
   /// basic type: SFBool \n
   /// default value: TRUE
   auto_ptr< Enabled      >    enabled;
   ThreadSafeBool              is_enabled; // A threadsafe equivalent of the enabled field 

   /// If TRUE, values received on the network are immediately buffered and released to the 
   /// scenegraph field network according to bufferStrategy \n
   /// access type: inputOutput \n
   /// basic type: SFBool \n
   /// default value: FALSE
   auto_ptr< H3D::SFBool  >    bufferReceivedVals;

   /// bufferStratgey is part of a mechanism to make sure that if two machines run at different frame rates, values
   /// can be assured to be all seen on the slow machine if necessary \n
   /// It can be set to be:                     \n
   /// "NONE"  : no bufferring, only the most recent value is seen on the receiving machine                    \n
   ///           if the receiving machine is slower than the sender, the sender may send several
   ///             values during a reciver's single graphics cycle and only the most recent is used                    \n
   /// "SET_ONE_PER_CYCLE" : the received values are buffered and then released (ie setValue() is called)
   ///                       on the output field once per graphics cycle   \n
   ///                       This ensures that each graphics cycle 'sees' a new received value that was sent on each of the
   ///                       sender's graphics cycles                                                        \n
   /// "SET_ALL" :  the received values are buffered and then released (ie setValue() is called) repeatedly
   ///              until the buffer is empty during a single graphics cycle           \n
   ///              This means that setValue() gets called for each received value, but they are not spaced out, \n
   ///              one per graphics cycle                    \n
   ///              If you are not routing to an AutoUpdate field, these setValue()'s may not be noticed                    \n
   /// access type: inputOutput \n
   /// basic type: SFString \n
   /// default value: "NONE"
   auto_ptr< H3D::SFString>    bufferStrategy;

   /// A received time stamp \n
   /// access type: outputOnly \n
   /// basic type: SFTime \n
   /// default value: 
   auto_ptr< H3D::SFTime  >    remoteTimestamp;

   /// If true, a timestamp value is sent \n
   /// access type: inputOutput \n
   /// basic type: SFBool \n
   /// default value: FALSE
   auto_ptr< H3D::SFBool  >    timestamps;

   /// If you are routing to fields in the scenegraph which are
   /// used in the haptics thread set this to true                        \n
   /// Never route to fields handled in both haptics and graphics threads \n
   /// access type: inputOutput \n
   /// basic type: SFBool \n
   /// default value: FALSE
   auto_ptr< H3D::SFBool  >    isHapticField;

   /// The connection node in which this field exists.
   /// Also the network connection from which it reads and to which it
   /// writes.
   RemoteConnection * conP;

   /// Returns true if a connection is active
   bool isConnected() const {return (is_connected == true);}
   ThreadSafeBool              is_connected; 

   virtual void setSendOnChange(bool val) = 0;

   /// Read the parts of a field's value into the received field.
   /// Must be supplied for specific instantiations, to receive the
   /// correct parts of the value.
   virtual void readValue() = 0;

   virtual string defaultXMLContainerField() {return "remoteFields";}

protected:
   int readInt32();
   // Read an int from the socket.

   void writeInt32(const int & val);
   // Write an int to the socket.

   bool readBool();
   // Read a bool from the socket.

   void writeBool(const bool & val);
   // Write a bool to the socket.

   std::string readString();
   // Read a string from the socket.

   void writeString(const std::string & val);
   // Write a string to the socket.

   void sendPacket();
   // Send the data in the write packet of the remote connection to the socket.

   virtual void checkForChange() = 0;
   // Called from the collider in the graphics loop to check for any changes
   // in the received field and to update the field appropriately.

   //  H3D::H3DTime const & getRemoteSyncClockOffsetValue() const;

   bool receivedData() const {return received_data;}
   // Returns true if some data has been received (since the constructor)

   // <nested>
   class H3D_NETWORKING_UTILS_DLL_SPEC StatsPrinter : public H3D::PeriodicUpdate< H3D::SFTime > {
   public:

      StatsPrinter():
      counter( 0 ) {
         start = H3D::Scene::time->getValue();
      }

      virtual void increment();

      virtual void update();

      std::string msg;
      RemoteField *remote_fld;
      std::vector< H3D::H3DTime > latencies;

   private:
      H3D::H3DTime start;
      int counter;
   };
   friend class StatsPrinter;
   // </nested>

   // Protected fields
   auto_ptr< StatsPrinter > stats_printer;

   struct DummyForce : public HAPI::HAPIForceEffect {
      DummyForce(RemoteField * fld) : remote_fieldP(fld) {}
      HAPI::HAPIForceEffect::EffectOutput virtual calculateForces( const HAPI::HAPIForceEffect::EffectInput &input ) {
         //cout << "using" << endl;
         try {
            remote_fieldP->checkForChange();
         } catch (...) {
         }
         return HAPI::HAPIForceEffect::EffectOutput(H3D::Vec3f(0.0f,0.0f,0.0f));
      }
		H3D::AutoRef<RemoteField> remote_fieldP;
   };

   
private:
   bool collecting_stats;
   bool received_data;
   bool debug;
};

}

#endif

