/*****************************
File:      BufferedSFeild.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 BufferedSField.h
/// \brief Header file for BufferedSField, an SField that can buffer data arriving via it input routes.



#ifndef BufferedSField_H
#define BufferedSField_H

#include "H3DNetworkingUtils/Config.h"
#include <H3D/SField.h>
#include <deque>

namespace H3DNetworkingUtils {
   
/// \class BufferedSField
/// The  BufferedSField class is an H3D::SField, that has two extra capabilities:
/// 1. setValue() can be called from any thread, not just the graphics thread. For example,
///    it can be called from a separate, network-reading thread.
/// 2. It can buffer values that are input in setValue() or update(), and release them into 
///    getValue() in different ways.
/// If bufferring, the field only passes the stored values on to the real field value when checkForChange() is called.
/// The default method for releasing the values, is for no buffering, so the most recent value is released,
/// just like a normal field.
/// If bufferingMethod is SET_ONE_PER_CYCLE, they are buffered and fed into the field value at one per thread cycle.
/// If bufferingMethod is SET_ALL, they are buffered and fed into the field value, one at a time, but all within
/// the next thread cycle.  \n
/// <b>Examples:</b>
///   - <a href="../../examples/BufferedSFieldTestServer.x3d">BufferedSFieldTestServer.x3d</a>
///   - <a href="../../examples/BufferedSFieldTestClient.x3d">BufferedSFieldTestClient.x3d</a>
///

template <class S>
class H3D_NETWORKING_UTILS_DLL_SPEC BufferedSField : public S {
public:
   /// Contstructor
   BufferedSField();

   /// Initialize to a given value
   virtual void initialiseValue( const typename S::value_type empty ) {
      value = empty;
   }

   void setX3DType(H3D::X3DTypes::X3DType t) {x3d_type = t;}
   virtual H3D::X3DTypes::X3DType getX3DType() { return x3d_type; }

   enum BufferReleaseStrategy {NONE, SET_ONE_PER_CYCLE, SET_ALL};

   /// Call this if you want buffering on the values stored in the field.
   /// i.e. if this is NONE, there is effectively no buffering, and only the most recently
   /// set value is set into the field value in the checkForChange() routine.
   /// Setting it to SET_ONE_PER_CYCLE, causes a first-in-first-out (FIFO) method of setting the
   /// field value with one buffered value per thread cycle.
   /// Setting it to SET_ALL is similar to SET_ONE_PER_CYCLE, except that the field
   /// is set repeatedly (using FIFO) until the buffer is empty, in a single checkForChange() routine.
   /// Note that buffering only occurs if we routing into the graphics thread.
   void setBufferStrategy(BufferReleaseStrategy b) {buffer_strategy = b;}

   virtual void checkForChange(int id = 0);

   virtual void setValue(const typename S::value_type &  val, int id = 0 );

   // Should only be called from the graphics thread
   inline const typename S::value_type & getValue(int id = 0) {
     change_lock.lock();
     upToDate();
     temp_val = value;
     change_lock.unlock();
     return temp_val;
   }

   virtual void update();

private:
   H3DUtil::MutexLock change_lock;
   BufferReleaseStrategy buffer_strategy;
   std::deque<typename S::value_type> temp_vals;
   typename S::value_type temp_val;
   H3D::X3DTypes::X3DType x3d_type;
   bool debug;
};

}

#endif

