/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkTreeNode_hxx
#define itkTreeNode_hxx

#include "itkMacro.h"
#include "itkTreeNode.h"
#include <cstring>

namespace itk
{

/** Destructor */
template< typename TValue >
TreeNode< TValue >
::~TreeNode()
{
  if ( m_Parent )
    {
    m_Parent->Remove(this);
    }

  const auto numberOfChildren = static_cast< ChildIdentifier >( m_Children.size() );

  for ( ChildIdentifier i = numberOfChildren; i > 0; i-- )
    {
    m_Children[i - 1]->SetParent(nullptr);
    }
  m_Children.clear();
  m_Parent = nullptr;
  m_Data = 0;
}

/** Return the parent node */
template< typename TValue >
TreeNode< TValue > *
TreeNode< TValue >
::GetParent() const
{
  return m_Parent;
}

/** Get a child */
template< typename TValue >
TreeNode< TValue > *
TreeNode< TValue >
::GetChild(ChildIdentifier number) const
{
  const auto numberOfChildren = static_cast< ChildIdentifier >( m_Children.size() );

  if ( number < numberOfChildren )
    {
    return m_Children[number];
    }
  else
    {
    return nullptr;
    }
}

/** Set the value of a node */
template< typename TValue >
TValue
TreeNode< TValue >
::Set(const TValue data)
{
  TValue help = m_Data;

  m_Data = data;
  return help;
}

/** Get the data of node */
template< typename TValue >
const TValue &
TreeNode< TValue >
::Get() const
{
  return m_Data;
}

/** Return true if has a parent */
template< typename TValue >
bool
TreeNode< TValue >
::HasParent() const
{
  return ( m_Parent ) ? true : false;
}

/** Set the parent node */
template< typename TValue >
void
TreeNode< TValue >
::SetParent(TreeNode< TValue > *node)
{
  //keep ourself alive just a bit longer
  Pointer ourself = this;

  if ( m_Parent != nullptr )
    {
    m_Parent->Remove(this);
    }
  m_Parent = node;
}

/** Return true if the node has children */
template< typename TValue >
bool
TreeNode< TValue >
::HasChildren() const
{
  return ( !m_Children.empty() ) ? true : false;
}

/** Return the number of children */
template< typename TValue >
typename TreeNode< TValue >::ChildIdentifier
TreeNode< TValue >
::CountChildren() const
{
  return static_cast< ChildIdentifier >( m_Children.size() );
}

/** Remove a child node from the current node */
template< typename TValue >
bool
TreeNode< TValue >
::Remove(Self *n)
{
  typename std::vector< Pointer >::iterator pos;
  pos = std::find(m_Children.begin(), m_Children.end(), n);
  if ( pos != m_Children.end() )
    {
    //keep node alive just a bit longer
    Pointer position = n;
    m_Children.erase(pos);
    n->SetParent(nullptr);
    return true;
    }
  return false;
}

/** Replace a child by a new one */
template< typename TValue >
bool
TreeNode< TValue >
::ReplaceChild(Self *oldChild, Self *newChild)
{
  const auto numberOfChildren = static_cast< ChildIdentifier >( m_Children.size() );

  for ( ChildIdentifier i = 0; i < numberOfChildren; i++ )
    {
    if ( m_Children[i] == oldChild )
      {
      m_Children[i] = newChild;
      return true;
      }
    }
  return false;
}

/** Return the child position given a node */
template< typename TValue >
OffsetValueType
TreeNode< TValue >
::ChildPosition(const Self *node) const
{
  const auto numberOfChildren = static_cast< ChildIdentifier >( m_Children.size() );

  for ( ChildIdentifier i = 0; i < numberOfChildren; i++ )
    {
    if ( m_Children[i] == node )
      {
      return i;
      }
    }
  return -1;
}

/** Return the child position given an element, the first child found. */
template< typename TValue >
typename TreeNode< TValue >::ChildIdentifier
TreeNode< TValue >
::ChildPosition(TValue element) const
{
  const auto numberOfChildren = static_cast< ChildIdentifier >( m_Children.size() );

  for ( ChildIdentifier i = 0; i < numberOfChildren; i++ )
    {
    if ( m_Children[i]->Get() == element )
      {
      return i;
      }
    }
  return -1;
}

/** Add a child node */
template< typename TValue >
void
TreeNode< TValue >
::AddChild(Self *node)
{
  Pointer nodeKeepAlive = node;

  node->SetParent(this);
  m_Children.push_back(node);
}

/** Add a child at a specific position in the children list */
template< typename TValue >
void
TreeNode< TValue >
::AddChild(ChildIdentifier number, Self *node)
{
  const auto numberOfChildren = static_cast< ChildIdentifier >( m_Children.size() );
  auto childId = static_cast<ChildIdentifier>( number );

  if ( childId > numberOfChildren )
    {
    m_Children.resize(childId);
    for ( ChildIdentifier i = numberOfChildren; i <= childId; i++ )
      {
      m_Children[i] = nullptr;
      }
    m_Children[number] = node;
    return;
    }

  m_Children[number] = node;
}

/** Get the number of children given a name and a depth */
template< typename TValue >
typename TreeNode< TValue >::ChildIdentifier
TreeNode< TValue >
::GetNumberOfChildren(unsigned int depth, char *name) const
{
  auto it = m_Children.begin();
  auto itEnd = m_Children.end();

  ChildIdentifier cnt = 0;
  while ( it != itEnd )
    {
    if ( name == nullptr || strstr(typeid( **it ).name(), name) )
      {
      ++cnt;
      }
    ++it;
    }

  it = m_Children.begin();
  itEnd = m_Children.end();
  if ( depth > 0 )
    {
    while ( it != itEnd )
      {
      cnt += ( *it )->GetNumberOfChildren(depth - 1, name);
      ++it;
      }
    }

  return cnt;
}

/** Get children given a name and a depth */
#if !defined( ITK_WRAPPING_PARSER )
template< typename TValue >
typename TreeNode< TValue >::ChildrenListType *
TreeNode< TValue >
::GetChildren(unsigned int depth, char *name) const
{
  auto * children = new ChildrenListType;

  auto childrenListIt = m_Children.begin();
  auto childrenListEnd = m_Children.end();

  while ( childrenListIt != childrenListEnd )
    {
    if ( name == nullptr || strstr(typeid( **childrenListIt ).name(), name) )
      {
      children->push_back(*childrenListIt);
      }
    if ( depth > 0 )
      {
      ChildrenListType *nextchildren = ( **childrenListIt ).GetChildren(depth - 1,
                                                                        name);
      // Add the child to the current list
      typename ChildrenListType::const_iterator nextIt = nextchildren->begin();
      while ( nextIt != nextchildren->end() )
        {
        children->push_back(*nextIt);
        ++nextIt;
        }
      delete nextchildren;
      }
    ++childrenListIt;
    }

  return children;
}

#endif
} // namespace itk

#endif
