/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2013/11/27 - 2014/10/30
/// 
/// @brief  スマートポインタリンクツリー。
/// 
/// ****************************************************************************

#pragma once

#include "CPL_Core_SmartLinkList.h"

namespace CPL {
namespace Core {

  
// _____________________________________________________________________________
/// @brief スマートポインタツリー実装クラス
///
/// このクラスは基本クラスとして使用し、派生クラスにスマートポインタリンクツリーの
/// 機能を提供する。\n
/// なお、このクラスはポリモーフィズムとなる。
/// 
/// @tparam T_TypeThisDerived : このクラスを組み込んだ派生クラスを指定する。
///
// *****************************************************************************
template< class T_TypeThisDerived >
class IncludeSmartLinkTree
  : public IncludeSmartLinkList< T_TypeThisDerived >
{
  using td_base = IncludeSmartLinkList< T_TypeThisDerived >;
  using td_self = IncludeSmartLinkTree< T_TypeThisDerived >;
  using type_wp_target = typename td_base::type_wp_target;
  using type_sp_target = typename td_base::type_sp_target;

  friend td_self;

protected:
  type_wp_target m_wp_parent; ///< 親を指すスマポ（所有権は持っちゃいけない）
  type_sp_target m_sp_child;  ///< 子供の先頭を指すスマポ。

public:
  // _____________________________________________________________________________
  /// @brief 親のポインタを取得。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual type_sp_target GetLinkParent() const
  {
    return m_wp_parent.lock();
  }

  // _____________________________________________________________________________
  /// @brief ツリールートのポインタを取得。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual type_sp_target GetLinkRoot() const
  {
    auto sp_node = this->GetLinkMyself();
    while( sp_node  &&  sp_node->GetLinkParent() )
    {
      sp_node = sp_node->GetLinkParent();
    }
    return sp_node->GetLinkTop();
  }

  // _____________________________________________________________________________
  /// @brief 子供（の先頭）のポインタを取得。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual type_sp_target GetLinkChild() const
  {
    return m_sp_child;
  }

  // _____________________________________________________________________________
  /// @brief 全体のツリーリンクでの次の要素を取得。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual type_sp_target GetLinkNextAll() const
  {
    if( auto sp_child = this->GetLinkChild() )
    {
      return sp_child;
    }
    else
    if( auto sp_next = this->GetLinkNext() )
    {
      return sp_next;
    }
    else
    {
      auto sp_node = this->GetLinkMyself();
      while( sp_node )
      {
        auto sub_node_parent = sp_node->GetLinkParent();

        if( sub_node_parent  &&
            sub_node_parent->GetLinkNext() )
        {
          sp_node = sub_node_parent->GetLinkNext();
          break;
        }
        sp_node = sp_node->GetLinkParent();
      }
      return sp_node;
    }
  }

  // _____________________________________________________________________________
  /// @brief １つ前に追加。
  /// @return 追加したスマポを返す。失敗するとnullptrを表すスマポを返す。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual type_sp_target
    AddLinkPrev( _in<type_sp_target> io_sp_target )
  {
    if( io_sp_target == nullptr  ||
        io_sp_target == this->GetLinkMyself() )
    {
      return nullptr;
    }
    // デタッチで誤った解放がされないよう、一時保存。
    auto sp_prev_save   = io_sp_target->m_wp_prev.lock();
    auto sp_next_save   = io_sp_target->m_sp_next;
    auto sp_parent_save = io_sp_target->m_wp_parent.lock();
    //auto sp_child_save  = io_sp_target->m_sp_child; // 子供は関係ない、はず。

    io_sp_target->Detach();
    io_sp_target->m_wp_prev   = this->GetLinkPrev();
    io_sp_target->m_sp_next   = this->GetLinkMyself();
    io_sp_target->m_wp_parent = this->GetLinkParent();

    if( auto sp_prev = this->GetLinkPrev() )
    {
      sp_prev->m_sp_next = io_sp_target;
    }
    else
    if( auto sp_parent = this->GetLinkParent() ) // 前方がなく、親がいる場合、親の子供を変更する必要がある。
    {
      sp_parent->m_sp_child = io_sp_target;
    }
    this->m_wp_prev = io_sp_target;

    return io_sp_target;
  }

  // _____________________________________________________________________________
  /// @brief １つ後ろに追加。
  /// @param[in] io_sp_target : 追加対象のオブジェクトのスマポ。
  /// @return 追加したスマポを返す。失敗するとnullptrを表すスマポを返す。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual type_sp_target
    AddLinkNext( _in<type_sp_target> io_sp_target )
  {
    if( io_sp_target == nullptr
     || io_sp_target == this->GetLinkMyself() )
    {
      return nullptr;
    }
    // デタッチで誤った解放がされないよう、一時保存。
    auto sp_prev_save   = io_sp_target->m_wp_prev.lock();
    auto sp_next_save   = io_sp_target->m_sp_next;
    auto sp_parent_save = io_sp_target->m_wp_parent.lock();
    //auto sp_child_save  = io_sp_target->m_sp_child; // 子供は関係ない、はず。

    io_sp_target->Detach();
    io_sp_target->m_wp_prev   = this->GetLinkMyself();
    io_sp_target->m_sp_next   = this->GetLinkNext();
    io_sp_target->m_wp_parent = this->GetLinkParent();

    if( auto sp_next = this->GetLinkNext() )
    {
      sp_next->m_wp_prev = io_sp_target;
    }
    this->m_sp_next = io_sp_target;

    return io_sp_target;
  }

  // _____________________________________________________________________________
  /// @brief 子供を追加。
  /// 
  /// 新しい子供は最後尾に追加される。
  /// @param[in] io_sp_target : 追加対象のオブジェクトのスマポ。
  /// @return 追加したスマポを返す。失敗するとnullptrを表すスマポを返す。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual type_sp_target
    AddLinkChild( _in<type_sp_target> io_sp_target )
  {
    if( io_sp_target == nullptr  ||
        io_sp_target == this->GetLinkMyself() )
    {
      return nullptr;
    }

    if( auto sp_child = this->GetLinkChild() )
    {
      sp_child->AddLinkEnd( io_sp_target );
    }
    else
    {
      io_sp_target->Detach();
      this->m_sp_child = io_sp_target;
    }
    io_sp_target->m_wp_parent = this->GetLinkMyself();
    return io_sp_target;
  }

  // _____________________________________________________________________________
  /// @brief リンクのデタッチ
  /// 自身がルートとなる独立したツリーとなる。\n
  /// なお、デタッチ前に他のスマートポインタ等に管理を移す等、自身の寿命は別途管理しなければならない。
  /// @param[in] in_flag_next
  ///   : 戻り値決定フラグ。\n
  ///   : false - 前の要素。\n
  ///   : true  - 次の要素。\n
  ///   : 省略すると次(true)となる。
  /// @return 自身の次または前の要素を返す。in_flag_nextを参照。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual type_sp_target Detach( _in<bool> in_flag_next = true )
  {
    auto sp_parent = GetLinkParent();
    if( sp_parent  &&  sp_parent->GetLinkChild() == this->GetLinkMyself() )
    {
      sp_parent->m_sp_child = this->GetLinkNext();
    }
    m_wp_parent.reset();
    return td_base::Detach( in_flag_next );
  }

  // _____________________________________________________________________________
  /// @brief 子供リンクのでタッチ
  /// ※ 他のスマートポインタで保持していない場合、その子供は即時削除されます。
  /// 
  /// @return なし。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual void DetachChild()
  {
    auto sp_child = this->GetLinkChild();
    if( sp_child )
    {
      sp_child = sp_child->GetLinkEnd();
    }
    while( sp_child )
    {
      sp_child = sp_child->Detach( false );
    }
  }


  // _____________________________________________________________________________
  /// @brief 全ての子供のリンクと自身をデタッチする。
  /// @param[in] in_flag_next
  ///   : 戻り値決定フラグ。\n
  ///   : false - 前の要素。\n
  ///   : true  - 次の要素。\n
  ///   : 省略すると次(true)となる。
  /// @return 自身の次または前の要素を返す。in_flag_nextを参照。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual type_sp_target DetachAll( _in<bool> in_flag_next = true )
  {
    auto sp_child = GetLinkChild();
    if( sp_child )
    {
      sp_child = sp_child->GetLinkEnd();
    }
    while( sp_child )
    {
      sp_child = sp_child->DetachAll( false );
    }
    return Detach( in_flag_next );
  }

  // _____________________________________________________________________________
  /// @brief リンクの交換。
  /// 
  /// @param[in] in_sp_link_target : 交換対象のノード。
  /// 
  /// @retval true  : 交換成功。
  /// @retval false : 交換失敗。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual bool SwapLink( _in<type_sp_target> in_sp_link_target )
  {
    if( in_sp_link_target == nullptr )
    {
      return false;
    }

    auto sp_prev0   = this->GetLinkPrev();
    auto sp_parent0 = this->GetLinkParent();
    auto sp_child0  = this->GetLinkChild();
    auto sp_prev1   = in_sp_link_target->GetLinkPrev();
    auto sp_parent1 = in_sp_link_target->GetLinkParent();
    auto sp_child1  = in_sp_link_target->GetLinkChild();
    // 親からのリンクの調整。
    bool const is_childtop_for_self   = (sp_parent0 && !sp_prev0);
    bool const is_childtop_for_target = (sp_parent1 && !sp_prev1);
    if( is_childtop_for_self  &&  is_childtop_for_target )
    {
      std::swap( sp_parent0->m_sp_child, sp_parent1->m_sp_child );
    }
    else if( is_childtop_for_self )
    {
      sp_parent0->m_sp_child = in_sp_link_target;
    }
    else
    {
      sp_parent1->m_sp_child = this->GetLinkMyself();
    }
    // 子供からのリンクの調整。
    while( sp_child0 )
    {
      sp_child0->m_wp_parent = in_sp_link_target;
      sp_child0 = sp_child0->GetLinkNext();
    }
    while( sp_child1 )
    {
      sp_child1->m_wp_parent = this->GetLinkMyself();
      sp_child1 = sp_child1->GetLinkNext();
    }

    // それぞれ保持するリンクの交換。
    std::swap( m_wp_parent, in_sp_link_target->m_wp_parent );
    std::swap( m_sp_child, in_sp_link_target->m_sp_child );

    // 
    return td_base::SwapLink( in_sp_link_target );
  }

  // _____________________________________________________________________________
  /// @brief 子供の数を取得。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual size_t GetChildLinkNum() const
  {
    size_t num = 0;
    for( auto sp_child = GetLinkChild();
         sp_child != nullptr;
         sp_child = sp_child->GetLinkNext() )
    {
      ++num;
    }
    return num;
  }

  // _____________________________________________________________________________
  /// @brief 全ての子供の数を取得。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual size_t GetChildLinkAllNum() const
  {
    size_t num = 0;
    for( auto sp_child = GetLinkChild();
         sp_child != nullptr;
         sp_child = sp_child->GetLinkNext() )
    {
      num += sp_child->GetChildLinkNum() + 1;
    }
    return num;
  }

  // _____________________________________________________________________________
  /// @brief 指定したノードが上位クラスに存在するかどうかチェック。
  /// @param[in] in_sp_target : チェックするオブジェクトのスマポ。
  /// @retval true  : 存在する。
  /// @retval false : 存在しない。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual bool CheckLinkUpClass( _in<type_sp_target> in_sp_target ) const
  {
    auto sp_parent = GetLinkParent();
    while( sp_parent )
    {
      if( sp_parent == in_sp_target )
      {
        return true;
      }
      sp_parent = sp_parent->GetLinkParent();
    }
    return false;
  }

  // _____________________________________________________________________________
  /// @brief 指定したノードが下位クラスに存在するかどうかチェック。
  /// @param[in] in_sp_target : チェックするオブジェクトのスマポ。
  /// @retval true  : 存在する。
  /// @retval false : 存在しない。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual bool CheckLinkDownClass( _in<type_sp_target> in_sp_target ) const
  {
    if( in_sp_target )
    {
      return in_sp_target->CheckLinkUpClass( this->GetLinkMyself() );
    }
    return false;
  }

  // _____________________________________________________________________________
  /// @brief 指定したノードが自身が属するツリー内に存在するかどうかチェック。
  /// @param[in] in_sp_target : チェックするオブジェクトのスマポ。
  /// @retval true  : 存在する。
  /// @retval false : 存在しない。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual bool CheckLinkAllClass( _in<type_sp_target> in_sp_target ) const
  {
    return GetLinkRoot()->CheckLinkDownClass( in_sp_target );
  }

  // _____________________________________________________________________________
  /// @brief リンクのクローンコピー
  ///
  /// @param[in] in_sp_target
  ///   : コピー元のオブジェクトを指定します。\n
  ///   : このオブジェクトはCreateClone関数(引数なし)を実装している必要があります。
  /// 
  /// @return なし。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  void CloneCopyLink( cref<T> in_sp_target )
  {
    auto sp_child = in_sp_target->GetLinkChild();
    while( sp_child )
    {
      AddLinkChild( sp_child->CreateClone() );
      sp_child = sp_child->GetLinkNext();
    }
  }

protected:
  // _____________________________________________________________________________
  /// @brief デフォルトコンストラクタ
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  IncludeSmartLinkTree()
  {}

  // _____________________________________________________________________________
  /// @brief コピーコンストラクタ ※ リンクはコピーしない。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  IncludeSmartLinkTree( cref<td_self> isl )
    : td_base( isl )
  {}

  // _____________________________________________________________________________
  /// @brief ムーブコンストラクタ
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  IncludeSmartLinkTree( rvref<td_self> isl )
    : td_base( isl )
    , m_wp_parent( std::move(isl.m_wp_parent) )
    , m_sp_child( std::move(isl.m_sp_child) )
  {}

  // _____________________________________________________________________________
  /// @brief デストラクタ
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ~IncludeSmartLinkTree()
  {}

public:
  // _____________________________________________________________________________
  /// @brief ムーブ
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  IncludeSmartLinkTree& operator = ( rvref<IncludeSmartLinkTree> islt )
  {
    m_wp_parent = std::move(islt.m_wp_parent);
    m_sp_child  = std::move(islt.m_sp_child);
    *static_cast<td_base*>(this) = static_cast<td_base&&>(islt);
    return *this;
  }

}; // class IncludeSmartLinkTree


} // namespace Core
} // namespace CPL
