/*****************************
File:       RemoteNodeField.CPP
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>
***************************/
#include "H3DNetworkingUtils/RemoteNodeField.h"

using namespace H3D;
using namespace HAPI;
using namespace H3DNetworkingUtils;

#include <H3D/Coordinate.h>
#include <H3D/IndexedTriangleSet.h>
#include <H3D/IndexedFaceSet.h>
#include <H3D/Normal.h>

template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
   const int RemoteNodeField<TheNode, MultiFieldType, fld>::WHOLE_ARRAY = 0;
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
   const int RemoteNodeField<TheNode, MultiFieldType, fld>::SINGLE_VALUE = 1;
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
   const int RemoteNodeField<TheNode, MultiFieldType, fld>::MULTI_VALUE = 2;

// ----------------------------------------------------------------------------
template <class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld>
H3DNodeDatabase RemoteNodeField<TheNode, MultiFieldType, fld>::database(   
   "RemoteNodeField<TheNode, MultiFieldType, fld>", 
   NULL,
   typeid(RemoteNodeField<TheNode, MultiFieldType, fld>),
   &RemoteField::database);

// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
RemoteNodeField<TheNode, MultiFieldType, fld>::RemoteNodeField() :
received_whole_array (false),
theNode (new SFTheNode),
indexToBeSent (new SFSender),
indiciesToBeSent (new MFSender),  
sendAll (new SFSendAll),
data_waiting (false) {

  type_name = "RemoteNodeField";
  database.initFields( this );

  sendAll->setOwner(this);
  indexToBeSent->setOwner(this);
  indiciesToBeSent->setOwner(this);
}


// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
void RemoteNodeField<TheNode, MultiFieldType, fld>::writeSField (unsigned int index) {
  if (theNode.get()) {
    // Write a general header and a code to say we are sending a single
    // index-value pair, followed by the index and value.
    conP->writeFieldHeader(fieldId->getValue(), timestamps->getValue());
    writeInt32(SINGLE_VALUE);
    writeInt32(index);
    writeValue(theField(theNode->getValue())->getValueByIndex(index));
  }
}

// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
void RemoteNodeField<TheNode, MultiFieldType, fld>::writeMField (const std::vector<int> & indicies) {
  if (theNode.get()) {
    // Write a general header and a code to say we are sending a multi
    // index-value pairs, followed by the indexes and values.
    conP->writeFieldHeader(fieldId->getValue(), timestamps->getValue());
    writeInt32(MULTI_VALUE);
    unsigned int num = indicies.size();
    writeInt32(num);
    for (unsigned int i = 0; i < num; i++) {
      int index = indicies[i];
      writeInt32(index);
      typename MultiFieldType::value_type pt = theField(theNode->getValue())->getValueByIndex(index);
      writeValue(pt);
    }
  }
}

// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
void RemoteNodeField<TheNode, MultiFieldType, fld>::writeWholeArray () {
  if (theNode->getValue()) {
    unsigned int num = theField(theNode->getValue())->size();
    if (num > 0) {
      // Write a general header and a code to say we are sending the whole
      // array, followed by the number of pairs and the array of index and value.
      conP->writeFieldHeader(fieldId->getValue(), timestamps->getValue());
      writeInt32(WHOLE_ARRAY);
      writeInt32(num);
      MultiFieldType * the_field = theField(theNode->getValue());  
      for (unsigned int i = 0; i < num; i++) {
        writeValue(the_field->getValueByIndex(i));
      }
    }
  }
}

// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
void RemoteNodeField<TheNode, MultiFieldType, fld>::writeValue (typename MultiFieldType::value_type const & val) {
}

// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
typename MultiFieldType::value_type RemoteNodeField<TheNode, MultiFieldType, fld>::readVal() {
   typename MultiFieldType::value_type dummy;
   return dummy;
}

// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
void RemoteNodeField<TheNode, MultiFieldType, fld>::readValue() {
  
  change_lock.lock();
  int new_flag = readInt32();
  data_waiting = true;
  // If we have no data, or only indexed items, we can receive a new indexed item.
  // If we have received the whole array, we cannot receive any more indexed  data this
  // cycle - throw it away.
  if ((new_flag != SINGLE_VALUE) && 
      (new_flag != MULTI_VALUE) && 
      (new_flag != WHOLE_ARRAY)) {
    throw InvalidNodeFieldFlag(new_flag, 
              "Invalid flag for Remote Node Field");
  }
  if (new_flag == SINGLE_VALUE) {
    if (received_whole_array) {
      // Throw away new data.
      readInt32();
      readVal();
    } else {
      // New single value
      readSField();
    }    
  } else if (new_flag == MULTI_VALUE) {
    if (received_whole_array) {
      // Throw away new data.
      unsigned int num = readInt32();
      for (unsigned int i = 0; i < num; i++) {
        readInt32();
        readVal();
      }
    } else {
      data.val.resize(0);
      data.index.resize(0); 
      readMField();
    }
  }
  else { // WHOLE_ARRAY
    // an array - perhaps replacing one we've already received this cycle - we
    // don't care.
    data.val.resize(0);
    int num = readInt32();
    for (int i = 0; i < num; i++) {
      typename MultiFieldType::value_type pt = readVal();
      if (!conP->closeRequested()) {
  	      data.val.push_back(pt);
      }
    }
    received_whole_array = true;
  } 
  change_lock.unlock();
}

// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
void RemoteNodeField<TheNode, MultiFieldType, fld>::readSField() {
  data.index.push_back(readInt32());
  typename MultiFieldType::value_type pt = readVal();
  if (!conP->closeRequested()) {
    data.val.push_back(pt);
  }
}

// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
void RemoteNodeField<TheNode, MultiFieldType, fld>::readMField() {
  int num = readInt32();
  for (int i = 0; i < num; i++) {
    int ind = readInt32();
    if (!conP->closeRequested()) {
      data.index.push_back(ind);
      typename MultiFieldType::value_type pt = readVal();
      if (!conP->closeRequested()) {
  	      data.val.push_back(pt);
      }
    }
  }
}

// ----------------------------------------------------------------------------
template<class TheNode, class MultiFieldType, auto_ptr<MultiFieldType> TheNode::*fld >
void RemoteNodeField<TheNode, MultiFieldType, fld>::checkForChange() {
  change_lock.lock();
  if (!data_waiting) {
    change_lock.unlock();
    return;
  }
  
  if (received_whole_array) {
    theField(theNode->getValue())->resize(0);
    theField(theNode->getValue())->setValue(data.val);
    received_whole_array = false;
  } else {
    for (unsigned int i = 0; i < data.index.size(); i++) {
      theField(theNode->getValue())->setValue(data.index[i], data.val[i]);
    }
    data_waiting = false;
  }
  // Clear out ready for next data.
  data.index.resize(0);
  data.val.resize(0);
  
  change_lock.unlock();
}
