/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2010/X/X - 2016/09/13
/// 
/// @brief  多次元配列
/// 
/// ****************************************************************************

#pragma once

#include "CPL_Core_ConstantArray.h"


namespace CPL {
namespace Core {
namespace Array {



// _____________________________________________________________________________
/// @brief 多次元配列用プロクシクラス？（終端用）
///
// *****************************************************************************
template<class T_Type>
class MAProxy
  : public Core::BaseObject
{
  template<class T_Type> friend class MAProxy;
  template<class T_Type> friend class MultiArray;

  typedef MAProxy<T_Type> type_this;

protected:
  T_Type* m_ptr;   // 自身が管理する先頭ポインタ
  int     m_slide; // スライド数（要素単位）

  MAProxy(){}

public:
  T_Type const& get() const { return *m_ptr; }
  T_Type&       get()       { return *m_ptr; }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  type_this cpl_cv& operator[]( int idx ) cpl_cv
  {
    type_this* pxy = this;
    if( pxy[1].m_ptr == nullptr )
    {
      return *this;
    }

    pxy[1].m_ptr = pxy[0].m_ptr + pxy[1].m_slide * idx;
    return pxy[1];
  }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  type_this const& operator[]( int idx ) const
  {
    type_this* pxy = this;
    if( pxy[1].m_ptr == nullptr )
    {
      return *this;
    }

    pxy[1].m_ptr = pxy[0].m_ptr + pxy[1].m_slide * idx;
    return pxy[1];
  }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  type_this volatile& operator[]( int idx ) volatile
  {
    type_this* pxy = this;
    if( pxy[1].m_ptr == nullptr )
    {
      return *this;
    }

    pxy[1].m_ptr = pxy[0].m_ptr + pxy[1].m_slide * idx;
    return pxy[1];
  }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  type_this& operator[]( int idx )
  {
    type_this* pxy = this;
    if( pxy[1].m_ptr == nullptr )
    {
      return *this;
    }

    pxy[1].m_ptr = pxy[0].m_ptr + pxy[1].m_slide * idx;
    return pxy[1];
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  operator T_Type cpl_cv&  () cpl_cv   { return *m_ptr; }
  operator T_Type volatile&() volatile { return *m_ptr; }
  operator T_Type const&   () const    { return *m_ptr; }
  operator T_Type&         ()          { return *m_ptr; }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  auto cpl_cv   operator -> () cpl_cv   { return &**m_ptr; }
  auto volatile operator -> () volatile { return &**m_ptr; }
  auto const    operator -> () const    { return &**m_ptr; }
  auto          operator -> ()          { return &**m_ptr; }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  T_Type cpl_cv*   operator & () cpl_cv   { return m_ptr; }
  T_Type volatile* operator & () volatile { return m_ptr; }
  T_Type const*    operator & () const    { return m_ptr; }
  T_Type*          operator & ()          { return m_ptr; }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  type_this& operator = ( T const& in_data ) {
    *m_ptr = in_data;
    return *this;
  }
  template<class T>
  type_this volatile& operator = ( T const& in_data ) volatile {
    *m_ptr = in_data;
    return *this;
  }
  
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  bool   operator == ( T const& in_data ) const {
    return *m_ptr == in_data; }
  template<class T>
  bool   operator == ( T const& in_data ) cpl_cv {
    return *m_ptr == in_data; }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  bool   operator != ( T const& in_data ) const {
    return *m_ptr != in_data; }
  template<class T>
  bool   operator != ( T const& in_data ) cpl_cv {
    return *m_ptr != in_data; }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  bool   operator < ( T const& in_data ) const {
    return *m_ptr < in_data; }
  template<class T>
  bool   operator < ( T const& in_data ) cpl_cv {
    return *m_ptr < in_data; }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  bool   operator <= ( T const& in_data ) const {
    return *m_ptr <= in_data; }
  template<class T>
  bool   operator <= ( T const& in_data ) cpl_cv {
    return *m_ptr <= in_data; }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  bool   operator > ( T const& in_data ) const {
    return *m_ptr > in_data; }
  template<class T>
  bool   operator > ( T const& in_data ) cpl_cv {
    return *m_ptr > in_data; }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  bool   operator >= ( T const& in_data ) const {
    return *m_ptr >= in_data; }
  template<class T>
  bool   operator >= ( T const& in_data ) cpl_cv {
    return *m_ptr >= in_data; }

};




// _____________________________________________________________________________
/// @brief 多次元配列クラス
///
/// ※ 使い方
/// @code
/// MultiArray<int> marr( 3, 4, 5 );
/// marr[0][2][4] = 0;
/// @endcode
///
/// ※ 仕様上の注意
///  ・宣言と同時に初期化はできない
///  ・使い方の例において、marr[0][0]を参照しても
///    int*にはならないし、キャストもできない。
///  ・静的な多次元配列よりオーバーヘッドはある
// *****************************************************************************
template<class T_Type>
class MultiArray
  : public Core::BaseObject
{
  typedef MAProxy<T_Type> type_proxy;

  // 次元数+2のプロクシを保持。
  // [0]には全体の情報を格納。
  // 次元数をNとしたとき、[N]に１要素スライド情報。
  // [N+1]に終端シグネチャ情報を設定する。（これは、要素数１の次元を許容するため）
  // そのために、+2の領域が必要とする。
  type_proxy*    m_proxy_table;
  //
  ThreadMutex<>* m_ptr_mutex;

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  T zzf_caluculateElementNum_( T const& x )
  {
    return x;
  }
  template<class T, class... T_Params>
  T zzf_caluculateElementNum_( T const& x, T const& y, T_Params const&... args )
  {
    return zzf_caluculateElementNum_( x * y, args... );
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class... T_Params>
  size_t zzf_caluculateProxyBufSize_( T_Params const&... args )
  {
    return
      AlignHigh
        ( (GetArgsNum( args... ) + 2) * sizeof(type_proxy)
        , sizeof(void*)
        );
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<int N, class T>
  T zzf_initProxy_( type_proxy* out_ptr_proxy, T const& x )
  {
    out_ptr_proxy->m_slide = x;
    return x;
  }
  template<int N, class T, class... T_Params>
  T zzf_initProxy_( type_proxy* out_ptr_proxy, T x, T_Params const&... args )
  {
    out_ptr_proxy->m_slide = x * zzf_initProxy_<N-1>( out_ptr_proxy+1, args... );
    return out_ptr_proxy->m_slide;
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class... T_Params>
  void zzf_init_( T_Params&&... args )
  {
    auto dim_num      = GetArgsNum( std::forward<T_Params>(args)... );
    auto pxy_buf_size = zzf_caluculateProxyBufSize_( std::forward<T_Params>(args)... );
    auto elm_num      = zzf_caluculateElementNum_( std::forward<T_Params>(args)... );
    auto buf_size     = pxy_buf_size + elm_num * sizeof(T_Type);
    // バッファ確保（プロクシ＋配列）
    char* p = reinterpret_cast<char*>(malloc( buf_size ));
    m_proxy_table = reinterpret_cast<type_proxy*>(p);
    // プロクシ設定
    zzf_initProxy_<sizeof...(args)>
      ( m_proxy_table
      , std::forward<T_Params>(args)...
      , 1
      );
    // 終端
    m_proxy_table[dim_num+1].m_ptr   = nullptr;
    m_proxy_table[dim_num+1].m_slide = 0;
    
    // コンストラクタ呼び出し　※ new(pointer) T_Type[x]と、配列仕様で呼び出してはいけない。
    auto locking = LockMutex( m_ptr_mutex );
    m_proxy_table[0].m_ptr = reinterpret_cast<T_Type*>(p + pxy_buf_size);
    for( int i = 0; i < elm_num; ++i )
    {
      new( m_proxy_table[0].m_ptr + i ) T_Type;
    }
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // エレメント数取得。※ zzf_initProxy_()呼出し後に有効
  size_t zzf_getElementNum_() const { return m_proxy_table[0].m_slide; }

public:
  template<class... T_Params>
  MultiArray( int N0, int N1, T_Params&&... args )
    : m_ptr_mutex( nullptr )
  {
    zzf_init_( N0, N1, std::forward<T_Params>(args)... );
  }

  template<class... T_Params>
  MultiArray
  ( ThreadMutex<>* in_ptr_mutex
  , int N0
  , int N1
  , T_Params&&... args
  ) : m_ptr_mutex( in_ptr_mutex )
  {
    zzf_init_( N0, N1, std::forward<T_Params>(args)... );
  }

  ~MultiArray()
  {
    auto locking = LockMutex( m_ptr_mutex );

    // デストラクタ呼び出し。
    auto num = zzf_getElementNum_();
    CPL_LOOP2( num, i )
    {
      (m_proxy_table->m_ptr+i)->~T_Type();
    }
    // バッファ開放。
    free( m_proxy_table );
  }
  
  type_proxy& operator[]( int idx ) const
  {
    type_proxy* pxy = m_proxy_table;

    pxy[1].m_ptr = pxy[0].m_ptr + pxy[1].m_slide * idx;
    return pxy[1];
  }
};



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