/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2002?/X/X - 2015/03/25
/// 
/// @brief  ブロックプール
/// 
/// ****************************************************************************

#pragma once

#include "../Base/CPL_Core_Basic.h"



namespace CPL {
namespace Core {
namespace Memory {


// _____________________________________________________________________________
/// @brief ブロックプールクラス
///
/// T_Type型のメモリ領域単位での割り当てを行う。
/// このクラスでは排他制御は行っていないので、必要な場合は外部で行うこと。
// *****************************************************************************
template<class T_Type>
class BlockPool
  : public Core::BaseObject
{
private:
  friend class BlockPool;
  typedef BlockPool<T_Type> type_this;
protected:
  T_Type*   m_block_table;        // データテーブル
  T_Type**  m_element_addr_table; // 参照用ポインタテーブル
  size_t    m_max;  // 予約サイズ
  size_t    m_num;  // 使用サイズ
  
  
public:
  T_Type& operator[]( size_t in_idx )       { return *m_element_addr_table[ in_idx ]; }
  T_Type& operator[]( size_t in_idx ) const { return *m_element_addr_table[ in_idx ]; }
//  void    Init( T_Type* in_ptr_data, long in_idx = -1, long in_num = -1 );
  T_Type* New( size_t* out_ptr_index = nullptr );
  T_Type* Delete( pio<T_Type> in_ptr );
  T_Type* DeleteIndex( size_t in_del_idx );
  void    Release();
  size_t  FindIndex( pin<T_Type> in_ptr ) const;
//  void    Delete( long in_delete_idx, T_Type* in_ptr );
  void    Refresh();
  size_t  GetNum() const { return m_num; }
  bool    Resize( size_t in_new_reserve_size );

  BlockPool( size_t in_num );
  ~BlockPool();
};


// _____________________________________________________________________________
/// @brief コンストラクタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
BlockPool<T_Type>::BlockPool( size_t num )
  : m_block_table( (T_Type*)malloc( sizeof(T_Type) * num ) )
  , m_element_addr_table( (T_Type**)malloc( sizeof(T_Type*) * num ) )
  , m_max( num )
  , m_num( 0 )
{
  Refresh();
}

/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 初期化付きコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
BlockPool<T_Type>::BlockPool( long num, T_Type* pData, long in_init_idx, long in_init_num )
{
  new( this ) T_Type( num );
  Init( pData, in_init_idx, in_init_num );
}*/


// _____________________________________________________________________________
/// @brief デストラクタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
BlockPool<T_Type>::~BlockPool()
{
  Release();
}

// _____________________________________________________________________________
/// @brief 解放。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
void BlockPool<T_Type>::Release()
{
  // 領域解放
  free( m_block_table );
  free( m_element_addr_table );
}

/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 初期化
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
void BlockPool<T_Type>::Init( T_Type* in_ptr_data, long in_idx = -1, long in_num = -1 )
{
  // インデックスチェック
  if( in_idx < 0 )
  {
    in_idx = 0;
  }
  if( in_num < 0 )
  {
    in_num = m_num - in_idx;
  }
  // 設定
  T_Type* pPtr  = m_block_table + in_idx;
  T_Type* pLast = pPtr + in_num;
  while( pPtr < pLast )
  {
    *pPtr = *in_ptr_data;
    ++pPtr;
    ++in_ptr_data;
  }
}
*/


// _____________________________________________________________________________
/// @brief 全ての予約エリアの解放。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
void BlockPool<T_Type>::Refresh()
{
  ::memset( m_block_table, 0, sizeof(T_Type)*m_max );
  // ポインタテーブルの初期化
  for( size_t i = 0; i < m_max; ++i )
  {
    m_element_addr_table[ i ] = (T_Type*)&m_block_table[ i ];
  }
  // 個数リセット
  m_num = 0;
}


// _____________________________________________________________________________
/// @brief 予約バッファのサイズ変更。
///
/// @param[in] in_new_reserve_size : 新しく予約するサイズ。
///
/// @retval true  : 成功。
/// @retval false : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
bool BlockPool<T_Type>::Resize( size_t in_new_reserve_size )
{
  if( in_new_reserve_size > m_max )
  {
    // 以前のポインタを保存
    T_Type*  tmp_block_table        = m_block_table;
    T_Type** tmp_element_addr_table = m_element_addr_table;

    // new
    m_block_table        = (T_Type*)realloc( m_block_table, sizeof(T_Type) * in_new_reserve_size );
    m_element_addr_table = (T_Type**)realloc( m_block_table, sizeof(T_Type*) * in_new_reserve_size );
    if( !m_block_table  ||  !m_element_addr_table )
    {
      // 新たな領域を解放
      free( m_block_table );
      free( m_element_addr_table );
      // 以前のポインタを再設定
      m_block_table        = tmp_block_table;
      m_element_addr_table = tmp_element_addr_table;

      return false;
    }

    //// data copy
    //::memcpy(
    //    m_block_table,
    //    tmp_block_table,
    //    sizeof(T_Type) * m_num );
    //// address copy
    //CPL_LOOP2( m_num, i )
    //{
    //  size_t idx = tmp_element_addr_table[ i ] - tmp_block_table;
    //  m_element_addr_table[ i ] = m_block_table + idx;
    //}

    //// old release
    //free( tmp_block_table );
    //free( tmp_element_addr_table );
    //
    m_max = in_new_reserve_size;

    // 拡張部分のアドレステーブルを設定
    for( size_t i = m_num; i < m_max; ++i )
    {
      m_element_addr_table[ i ] = (T_Type*)&m_block_table[ i ];
    }
  }

  return true;
}


// _____________________________________________________________________________
/// @brief 新規取得。
///
/// @param[out] out_ptr_index
///   : 取得された要素のインデックスを格納するためのポインタ。\n
///   : nullptrを指定可能。
///
/// @return 新しく取得された要素のポインタ。失敗するとnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
T_Type* BlockPool<T_Type>::New( size_t* out_ptr_index )
{
  if( m_num >= m_max )
  {
    if( !Resize( m_max * 2 ) )
    {
      return nullptr;
    }
  }
  if( out_ptr_index )
  {
    *out_ptr_index = m_num;
  }

  return m_element_addr_table[ m_num++ ];
}


// _____________________________________________________________________________
/// @brief 削除。
///
/// @param[in] in_ptr : 削除対象のポインタ。
///
/// @return 削除された位置に再配置された要素のポインタ。
///
/// @note 
/// 削除により他のデータの参照ポインタが移動された場合、その参照ポインタを返す。
/// 戻り値が有効ならば、移動された参照ポインタのインデックスはin_del_idxとなる。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
T_Type* BlockPool<T_Type>::Delete( pio<T_Type> in_ptr )
{
  return DeleteIndex( FindIndex( in_ptr ) );
}

/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 削除
//  ※ インデックス指定バージョン
//     ポインタはチェックのために使う
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
void BlockPool<T_Type>::Delete( long in_del_idx, T_Type* in_ptr )
{
  if( m_element_addr_table[ in_del_idx ] == in_ptr )
  {
    DeleteIndex( in_del_idx );
  }
}*/


// _____________________________________________________________________________
/// @brief 削除。
///
/// @param[in] in_del_idx : 削除するインデックス。
///
/// @return 削除された位置に再配置された要素のポインタ。
///
/// @note 
/// 削除により他のデータの参照ポインタが移動された場合、その参照ポインタを返す。
/// 戻り値が有効ならば、移動された参照ポインタのインデックスはin_del_idxとなる。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
T_Type* BlockPool<T_Type>::DeleteIndex( size_t in_del_idx )
{
  if( !FAILED_INDEX(in_del_idx, m_num-1) )
  {
    m_num--;
    if( m_num != in_del_idx )
    {
      Swap( m_element_addr_table[ m_num ],
            m_element_addr_table[ in_del_idx ] );
      return m_element_addr_table[ in_del_idx ];
    }
  }

  return nullptr;
}


// _____________________________________________________________________________
/// @brief ポインタからインデックスを検索。
///
/// @param[in] in_ptr : 検索対象の要素を指すポインタを指定。
///
/// @return 一致したインデックスを返す。該当なしで-1(全ビットON)を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Type>
size_t BlockPool<T_Type>::FindIndex( pin<T_Type> in_ptr ) const
{
  // LIFOの場合の方が多いと思われるので、最後から検索する
  auto pp = m_element_addr_table + m_num;
  while( --pp >= m_element_addr_table )
  {
    if( *pp == in_ptr )
    {
      return pp - m_element_addr_table;
    }
  }

  return -1;
}



} // namespace Memory
} // namespace Core
} // namespace CPL

