/*=auto=======================================================================

  Portions (c) Copyright 2005 Brigham and Women's Hospital (BWH) All Rights
  Reserved.

  See Doc/copyright/copyright.txt
  or http://www.slicer.org/copyright/copyright.txt for details.

  Program:   3D Slicer
  Module:    $RCSfile: vtkMRMLEMSTreParametersNode.cxx,v$
  Date:      $Date: 2006/01/06 17:56:51 $
  Version:   $Revision: 1.6 $
  Author:    $Nicolas Rannou (BWH), Sylvain Jaume (MIT)$

=======================================================================auto=*/

#include "vtkMRMLEMSTreeParametersNode.h"

#include <sstream>
#include "vtkMRMLScene.h"

#include <algorithm>
#include <iterator>

#include <vtksys/ios/sstream>

//----------------------------------------------------------------------------
vtkMRMLEMSTreeParametersNode* vtkMRMLEMSTreeParametersNode::New()
{
  // First try to create the object from the vtkObjectFactory
  vtkObject* ret = vtkObjectFactory::CreateInstance(
      "vtkMRMLEMSTreeParametersNode");

  if (ret)
  {
    return (vtkMRMLEMSTreeParametersNode*)ret;
  }

  // If the factory was unable to create the object, then create it here.
  return new vtkMRMLEMSTreeParametersNode;
}

//----------------------------------------------------------------------------
vtkMRMLNode* vtkMRMLEMSTreeParametersNode::CreateNodeInstance()
{
  // First try to create the object from the vtkObjectFactory
  vtkObject* ret = vtkObjectFactory::CreateInstance(
      "vtkMRMLEMSTreeParametersNode");

  if (ret)
  {
    return (vtkMRMLEMSTreeParametersNode*)ret;
  }

  // If the factory was unable to create the object, then create it here.
  return new vtkMRMLEMSTreeParametersNode;
}

//----------------------------------------------------------------------------
vtkMRMLEMSTreeParametersNode::vtkMRMLEMSTreeParametersNode()
{
  this->LeafParametersNodeID        = NULL;
  this->ParentParametersNodeID      = NULL;

  this->ColorRGB[0]                 = 1.0;
  this->ColorRGB[1]                 = 0.0;
  this->ColorRGB[2]                 = 0.0;

  this->SpatialPriorVolumeName      = NULL;
  this->SpatialPriorWeight          = 1.0;
  this->ClassProbability            = 0.0;
  this->ExcludeFromIncompleteEStep  = 0;
  this->PrintWeights                = 0;

  this->NumberOfTargetInputChannels = 0;
}

//----------------------------------------------------------------------------
vtkMRMLEMSTreeParametersNode::~vtkMRMLEMSTreeParametersNode()
{
  this->SetLeafParametersNodeID(NULL);
  this->SetParentParametersNodeID(NULL);
  this->SetSpatialPriorVolumeName(NULL);
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::WriteXML(ostream& of, int nIndent)
{
  Superclass::WriteXML(of, nIndent);

  vtkIndent indent(nIndent);

  of << indent
    << " ParentParametersNodeID=\""
    << (this->ParentParametersNodeID ? this->ParentParametersNodeID : "NULL")
    << "\"";

  of << indent
    << " LeafParametersNodeID=\""
    << (this->LeafParametersNodeID ? this->LeafParametersNodeID : "NULL")
    << "\"";

  {
    vtksys_stl::stringstream ss;

    ss << this->ColorRGB[0] << " "
      << this->ColorRGB[1] << " "
      << this->ColorRGB[2];

    of << indent
      << " ColorRGB=\"" << ss.str() << "\"";
  }

  of << indent
    << " InputChannelWeights=\"";

  vtksys_stl::copy(this->InputChannelWeights.begin(),
                   this->InputChannelWeights.end(),
                   vtksys_stl::ostream_iterator<double>(of, " "));
  of << "\"";

  of << indent
    << " SpatialPriorVolumeName=\""
    << (this->SpatialPriorVolumeName ? this->SpatialPriorVolumeName : "")
    << "\"";

  of << indent
    << " SpatialPriorWeight=\""
    << this->SpatialPriorWeight
    << "\"";

  of << indent
    << " ClassProbability=\""
    << this->ClassProbability
    << "\"";

  of << indent
    << " ExcludeFromIncompleteEStep=\""
    << this->ExcludeFromIncompleteEStep
    << "\"";

  of << indent
    << " PrintWeights=\""
    << this->PrintWeights
    << "\"";
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::UpdateReferenceID(const char* oldID, const
    char* newID)
{
  if (this->LeafParametersNodeID && !strcmp(oldID,this->LeafParametersNodeID))
  {
    this->SetLeafParametersNodeID(newID);
  }

  if (this->ParentParametersNodeID &&
      strcmp(oldID, this->ParentParametersNodeID) == 0)
  {
    this->SetParentParametersNodeID(newID);
  }
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::UpdateReferences()
{
  Superclass::UpdateReferences();

  if (this->LeafParametersNodeID != NULL &&
      this->Scene->GetNodeByID(this->LeafParametersNodeID) == NULL)
    {
    this->SetLeafParametersNodeID(NULL);
    }

  if (this->ParentParametersNodeID != NULL &&
      this->Scene->GetNodeByID(this->ParentParametersNodeID) == NULL)
    {
    this->SetParentParametersNodeID(NULL);
    }
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::ReadXMLAttributes(const char** attrs)
{
  Superclass::ReadXMLAttributes(attrs);

  // we can assume an even number of elements

  const char* key;
  const char* val;

  while (*attrs != NULL)
  {
    key = *attrs++;
    val = *attrs++;

    if (strcmp(key, "LeafParametersNodeID") == 0)
    {
      this->SetLeafParametersNodeID(val);
      //this->Scene->AddReferencedNodeID(this->LeafParametersNodeID, this);
    }
    else if (strcmp(key, "ParentParametersNodeID") == 0)
    {
      this->SetParentParametersNodeID(val);
      //this->Scene->AddReferencedNodeID(this->ParentParametersNodeID, this);
    }
    else if (strcmp(key, "ColorRGB") == 0)
    {
      vtksys_stl::stringstream ss;
      ss << val;

      double d;

      if (ss >> d)
      {
        this->ColorRGB[0] = d;
        this->ColorRGB[1] = d;
        this->ColorRGB[2] = d;
      }
    }
    else if (strcmp(key, "InputChannelWeights") == 0)
    {
      // read data into a temporary vector
      vtksys_stl::stringstream ss;
      ss << val;

      double d;
      vtksys_stl::vector<double> tmpVec;

      while (ss >> d)
      {
        tmpVec.push_back(d);
      }

      // update number of input channels
      if (this->NumberOfTargetInputChannels != tmpVec.size())
      {
      this->SetNumberOfTargetInputChannels((int)tmpVec.size());
      }

      // copy data
      vtksys_stl::copy(tmpVec.begin(), tmpVec.end(),
          this->InputChannelWeights.begin());
    }
    else if (strcmp(key, "SpatialPriorVolumeName") == 0)
    {
      this->SetSpatialPriorVolumeName(val);
    }
    else if (strcmp(key, "SpatialPriorWeight") == 0)
    {
      vtksys_stl::stringstream ss;
      ss << val;
      ss >> this->SpatialPriorWeight;
    }
    else if (strcmp(key, "ClassProbability") == 0)
    {
      vtksys_stl::stringstream ss;
      ss << val;
      ss >> this->ClassProbability;
    }
    else if (strcmp(key, "ExcludeFromIncompleteEStep") == 0)
    {
      vtksys_stl::stringstream ss;
      ss << val;
      ss >> this->ExcludeFromIncompleteEStep;
    }
    else if (strcmp(key, "PrintWeights") == 0)
    {
      vtksys_stl::stringstream ss;
      ss << val;
      ss >> this->PrintWeights;
    }
  }
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::Copy(vtkMRMLNode *rhs)
{
  Superclass::Copy(rhs);

  vtkMRMLEMSTreeParametersNode* node = (vtkMRMLEMSTreeParametersNode*) rhs;

  this->NumberOfTargetInputChannels = node->NumberOfTargetInputChannels;

  this->SetLeafParametersNodeID(node->LeafParametersNodeID);
  this->SetParentParametersNodeID(node->ParentParametersNodeID);
  this->SetColorRGB(node->ColorRGB);

  this->InputChannelWeights = node->InputChannelWeights;

  this->SetSpatialPriorVolumeName(node->SpatialPriorVolumeName);
  this->SetSpatialPriorWeight(node->SpatialPriorWeight);

  this->SetClassProbability(node->ClassProbability);
  this->SetExcludeFromIncompleteEStep(node->ExcludeFromIncompleteEStep);
  this->SetPrintWeights(node->PrintWeights);
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::PrintSelf(ostream& os, vtkIndent indent)
{
  Superclass::PrintSelf(os, indent);

  os << indent
    << "LeafParametersNodeID: "
    << (this->LeafParametersNodeID ? this->LeafParametersNodeID : "(none)")
    << "\n";

  os << indent
    << "ParentParametersNodeID: "
    << (this->ParentParametersNodeID ? this->ParentParametersNodeID :"(none)")
    << "\n";

  os << indent
    << "ColorRGB: "
    << this->ColorRGB[0] << this->ColorRGB[1] << this->ColorRGB[2]
    << "\n";

  os << indent
    << "InputChannelWeights: ";

  vtksys_stl::copy(this->InputChannelWeights.begin(),
                   this->InputChannelWeights.end(),
                   vtksys_stl::ostream_iterator<double>(os, " "));
  os << "\n";

  os << indent
    << "SpatialPriorVolumeName: "
    << (this->SpatialPriorVolumeName ? this->SpatialPriorVolumeName :"(none)")
    << "\n";

  os << indent
    << "SpatialPriorWeight: "
    << this->SpatialPriorWeight
    << "\n";

  os << indent
    << "ClassProbability: "
    << this->ClassProbability
    << "\n";

  os << indent
    << "ExcludeFromIncompleteEStep: "
    << this->ExcludeFromIncompleteEStep
    << "\n";

  os << indent
    << "PrintWeights: "
    << this->PrintWeights
    << "\n";
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::AddChildNode(const char* childNodeID)
{
  // nothing for the parameters in this class, but pass it on to leaf
  // and parent parameter nodes

  if (this->GetLeafParametersNode() != NULL)
  {
    this->GetLeafParametersNode()->AddChildNode(childNodeID);
  }

  if (this->GetParentParametersNode() != NULL)
  {
    this->GetParentParametersNode()->AddChildNode(childNodeID);
  }
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::RemoveNthChildNode(int n)
{
  // nothing for the parameters in this class, but pass it on to leaf
  // and parent parameter nodes

  if (this->GetLeafParametersNode() != NULL)
  {
    this->GetLeafParametersNode()->RemoveNthChildNode(n);
  }

  if (this->GetParentParametersNode() != NULL)
  {
    this->GetParentParametersNode()->RemoveNthChildNode(n);
  }
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::MoveNthChildNode(int fromIndex, int
    toIndex)
{
  // nothing for the parameters in this class, but pass it on to leaf
  // and parent parameter nodes

  if (this->GetLeafParametersNode() != NULL)
    {
    this->GetLeafParametersNode()->MoveNthChildNode(fromIndex, toIndex);
    }

  if (this->GetParentParametersNode() != NULL)
    {
    this->GetParentParametersNode()->MoveNthChildNode(fromIndex, toIndex);
    }
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::SetNumberOfTargetInputChannels(unsigned int
    n)
{
  if (n != this->NumberOfTargetInputChannels)
  {
    this->NumberOfTargetInputChannels = n;

    // resize InputChannelWeights, don't preserve data!
    this->InputChannelWeights.resize(n);
    vtksys_stl::fill(this->InputChannelWeights.begin(),
        this->InputChannelWeights.end(), 1.0);

    if (this->GetLeafParametersNode() != NULL)
    {
      this->GetLeafParametersNode()->SetNumberOfTargetInputChannels(n);
    }

    if (this->GetParentParametersNode() != NULL)
    {
      this->GetParentParametersNode()->SetNumberOfTargetInputChannels(n);
    }
  }
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::AddTargetInputChannel()
{
  this->NumberOfTargetInputChannels++;

  this->InputChannelWeights.push_back(1.0);

  if (this->GetLeafParametersNode() != NULL)
  {
    this->GetLeafParametersNode()->AddTargetInputChannel();
  }

  if (this->GetParentParametersNode() != NULL)
  {
    this->GetParentParametersNode()->AddTargetInputChannel();
  }
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::RemoveNthTargetInputChannel(int index)
{
  this->NumberOfTargetInputChannels--;

  this->InputChannelWeights.erase(InputChannelWeights.begin() + index);

  if (this->GetLeafParametersNode() != NULL)
  {
    this->GetLeafParametersNode()->RemoveNthTargetInputChannel(index);
  }

  if (this->GetParentParametersNode() != NULL)
  {
    this->GetParentParametersNode()->RemoveNthTargetInputChannel(index);
  }
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::MoveNthTargetInputChannel(int fromIndex,
    int toIndex)
{
  double movingParam = this->InputChannelWeights[fromIndex];
  this->InputChannelWeights.erase(this->InputChannelWeights.begin() +
      fromIndex);
  this->InputChannelWeights.insert(this->InputChannelWeights.begin() +
      toIndex, movingParam);

  if (this->GetLeafParametersNode() != NULL)
  {
    this->GetLeafParametersNode()->MoveNthTargetInputChannel(fromIndex,
        toIndex);
  }

  if (this->GetParentParametersNode() != NULL)
  {
    this->GetParentParametersNode()->MoveNthTargetInputChannel(fromIndex,
        toIndex);
  }
}

//----------------------------------------------------------------------------
double vtkMRMLEMSTreeParametersNode::GetInputChannelWeight(int index) const
{
  return this->InputChannelWeights[index];
}

//----------------------------------------------------------------------------
void vtkMRMLEMSTreeParametersNode::SetInputChannelWeight(int index, double
    value)
{
  this->InputChannelWeights[index] = value;
}

//----------------------------------------------------------------------------
vtkMRMLEMSTreeParametersLeafNode* vtkMRMLEMSTreeParametersNode::
GetLeafParametersNode()
{
  vtkMRMLEMSTreeParametersLeafNode* node = NULL;

  if (this->GetScene() && this->GetLeafParametersNodeID() )
  {
    vtkMRMLNode* snode = this->GetScene()->
      GetNodeByID(this->LeafParametersNodeID);

    node = vtkMRMLEMSTreeParametersLeafNode::SafeDownCast(snode);
  }

  return node;
}

//----------------------------------------------------------------------------
vtkMRMLEMSTreeParametersParentNode* vtkMRMLEMSTreeParametersNode::
GetParentParametersNode()
{
  vtkMRMLEMSTreeParametersParentNode* node = NULL;

  if (this->GetScene() && this->GetParentParametersNodeID() )
  {
    vtkMRMLNode* snode = this->GetScene()->
      GetNodeByID(this->ParentParametersNodeID);

    node = vtkMRMLEMSTreeParametersParentNode::SafeDownCast(snode);
  }

  return node;
}

