/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2002/X/X - 2013/09/13
/// 
/// @brief  動的配列管理
/// 
/// ****************************************************************************

#pragma once

#include "CPL_Core_StaticArray.h"


namespace CPL {
namespace Core {
namespace Array {


// _____________________________________________________________________________
/// @brief 動的配列クラス
///
// *****************************************************************************
template<class T_Type>
class Dynamic
  : public Static<T_Type>
{
  typedef Dynamic<T_Type> type_this;

  template<class T_Array>
  type_this& zzf_copy_typeD_( T_Array const& in_array )
  {
    // 自己代入判定
    if( (void*)this == (void*)&in_array )
    {
      return *this;
    }
    size_t num = in_array.GetNum();
    if( num > 0 )
    {
      this->Clear();
      Resize( in_array.GetNum() );
      this->zzf_CopyBlock_
        ( this->GetTop()
        , in_array.GetTop()
        , in_array.GetNum()
        );
    }
    else
    {
      this->Release();
    }

    return *this;
  }

public:
  // _____________________________________________________________________________
  /// @brief コピー。
  ///
  /// 現在のデータは破棄されます。\n
  /// ※ デストラクタが呼ばれます。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Array>
  type_this& operator = ( T_Array const& in_array )
  {
    return zzf_copy_typeD_( in_array );
  }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // コピー　※ this用
  type_this& operator = ( type_this const& in_array )
  {
    return zzf_copy_typeD_( in_array );
  }

//  long GetBlockSize() const { return m_block; } // ブロックサイズ取得
//  void SetBlockSize( long bsize ){ m_block = bsize; }

  // _____________________________________________________________________________
  /// @brief 領域を予約。
  ///
  /// 領域の確保に失敗氏らたら例外を投げる。
  ///
  /// @param[in] in_num
  ///   : 確保する要素数。
  /// @param[in] in_block_size
  ///   : 自動拡張時に増やすよう素数を指定。\n
  ///   : 0を指定することで倍になる。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void  Reserve( size_t in_num, size_t in_block_size = 0 )
  {
    if( in_num <= this->m_max )
    {
      return; // すでに指定サイズ以上確保していたら何もせずに戻る
    }

    if( in_block_size <= 0 )
    {
      in_num = (this->m_max<<1) > in_num
        ? this->m_max << 1
        : in_num;
    }
    else
    {
      in_num = (this->m_max + in_block_size) > in_num
        ? (this->m_max + in_block_size)
        : in_num;
    }
    const int min_size = 16;
    if( sizeof(T_Type)*in_num < min_size )
    {
      in_num = 16 / sizeof(T_Type);
    }

    try {
  //    sp::Linked<T_Type> new_ptr = zzf_New_( in_num ); // 新しい領域を確保
      T_Type* new_ptr = this->zzf_New_( in_num );
      if( !new_ptr )
      {
        //CPL_THROW_T(std::bad_alloc,"Core::Array::Dynamic::Reserve()\n");
        throw std::bad_alloc();
      }
      if( this->m_ptr )
      {
//        zzf_ConstructorBlock_( new_ptr, new_ptr + m_num - 1 ); // コピーのため、コンストラクタ呼び出し
        this->zzf_ConstructorBlock_( new_ptr, new_ptr + this->m_num - 1, this->m_ptr );
        this->zzf_CopyBlock_( new_ptr, this->m_ptr, this->m_num ); // 以前のデータをコピー
        this->zzf_DestructorBlock_( this->m_ptr, this->m_ptr + this->m_num - 1 );
        this->zzf_Del_( this->m_ptr );
      }
      this->m_ptr = new_ptr;  // 新しいポインタを設定
      this->m_max = in_num;      // 予約サイズ更新
    }
    catch(...)
    {
      this->Release();
      throw;
    }
  }

  // _____________________________________________________________________________
  /// @brief 配列サイズ変更。 ※ std::vector互換用
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void reserve( size_t in_size ){ return Reserve( in_size ); }
  
  // _____________________________________________________________________________
  /// @brief 配列サイズ変更。
  ///
  /// @param[in] in_num
  ///   : 確保する要素数。\n
  ///   : 0以下のサイズを与えると解放される。
  /// @param[in] in_block_size
  ///   : 自動拡張時に増やすよう素数を指定。\n
  ///   : 0を指定することで倍になる。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void  Resize( size_t in_num, size_t in_block_size )
  {
    if( in_num == this->m_num ) // 同じサイズなら処理する必要なし
    {
      return;
    }
    else
    if( (signed)in_num <= 0 )
    {
      this->Release();
    }
    else
    {
      if( in_num > this->m_num ) // 使用域の拡張が必要な場合
      {
        // 予約領域拡張（必要のない場合は何もしない）
        Reserve( in_num, in_block_size );
        // コンストラクタ呼び出し
        this->zzf_ConstructorBlock_( this->m_ptr + this->m_num, this->m_ptr + in_num - 1 );
      }
      else
      if( in_num < this->m_num )
      {
        // デストラクタ呼び出し
        this->zzf_DestructorBlock_
          ( this->m_ptr + in_num
          , this->m_ptr + this->m_num - 1
          );
      }

      this->m_num = in_num; // 新しいサイズを設定
    }
  }

  // _____________________________________________________________________________
  /// @brief 配列サイズ変更。
  ///
  /// @param[in] in_num
  ///   : 確保する要素数。\n
  ///   : ０以下のサイズを与えると解放される。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void  Resize( size_t in_num )
  {
    Resize( in_num, (size_t)0 );
  }

  
  // _____________________________________________________________________________
  /// @brief 初期化付き配列サイズ変更。
  ///
  /// @param[in] in_num
  ///   : 確保する要素数。\n
  ///   : ０以下のサイズを与えると解放される。
  /// @param[in] in_data
  ///   : 初期化データが設定された要素への参照。
  /// @param[in] in_block_size
  ///   : 自動拡張時に増やすよう素数を指定。\n
  ///   : 0を指定することで倍になる。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void
    ResizeInit
      ( size_t        in_num
      , const T_Type& in_data
      , size_t        in_block_size
      )
  {
    if( in_num == this->m_num )
    {
      return;
    }
    else
    if( (signed)in_num <= 0 )
    {
      this->Release();
    }
    else
    {
      if( in_num > this->m_num ) // 使用域の拡張が必要な場合
      {
        // 予約領域拡張（必要のない場合は何もしない）
        Reserve( in_num, in_block_size );

        // コンストラクタ呼び出し
        this->zzf_ConstructorBlock_
          ( this->m_ptr + this->m_num
          , this->m_ptr + in_num - 1
          , in_data
          );
      }
      else
      if( in_num < this->m_num )
      {
        // デストラクタ呼び出し
        this->zzf_DestructorBlock_
          ( this->m_ptr + in_num
          , this->m_ptr + this->m_num - 1
          );
      }

      this->m_num = in_num; // 新しいサイズを設定
    }
    return;
  }

  // _____________________________________________________________________________
  /// @brief 初期化付き配列サイズ変更。
  ///
  /// @param[in] in_num
  ///   : 確保する要素数。\n
  ///   : ０以下のサイズを与えると解放される。
  /// @param[in] in_data
  ///   : 初期化データが設定された要素への参照。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void
    ResizeInit
      ( size_t        in_num
      , const T_Type& in_data
      )
  {
    ResizeInit( in_num, in_data, 0 );
  }

  // STL互換
  void resize( size_t in_size, T_Type const& in_data = T_Type() )
  {
    ResizeInit( in_size, in_data );
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Dynamic(){}
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Dynamic( size_t in_num ){
    Resize( in_num ); }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Dynamic( size_t in_num, const T_Type& in_rInit ){
    Resize( in_num, in_rInit ); }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Dynamic( type_this const& in_arr ){
    *this = in_arr; }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Dynamic( ReserveSize const& rs )
  {
    Reserve( rs.m_size );
  }

  ~Dynamic(){}
};



// 基本型配列定義
typedef Dynamic<char>   D_Char;
typedef Dynamic<short>  D_Short;
typedef Dynamic<int>    D_Int;
typedef Dynamic<float>  D_Float;
typedef Dynamic<double> D_Double;

typedef Dynamic<S8>     D_S8;
typedef Dynamic<U8>     D_U8;
typedef Dynamic<S16>    D_S16;
typedef Dynamic<U16>    D_U16;
typedef Dynamic<S32>    D_S32;
typedef Dynamic<U32>    D_U32;
typedef Dynamic<S64>    D_S64;
typedef Dynamic<U64>    D_U64;

} // namespace Array
} // namespace Core
} // namespace CPL
