/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/29
/// 
/// @brief  例外関連
/// 
/// ****************************************************************************

#pragma once

#include "CPL_Core_Define.h"
//#include "CPL_Core_Thread.h"

#include "../../CPL_Setup.h"

namespace CPL {
namespace Core {

// _____________________________________________________________________________
/// @brief CorePLi用例外クラス？
///
// *****************************************************************************
class ExceptionBase
{
#ifdef CPL_DEBUG
  CPL_SINGLETON_INIT( int, sm_flag_break, 0 );
#endif
protected:
  char_cptr   m_file;
  const long  m_line;

public:
  static void SetThrowBreak( bool in_flag_break );

  char_cptr GetFileName() const { return m_file; }
  long      GetFileLine() const { return m_line; }

protected:
  ExceptionBase( char_cptr in_str, char_cptr in_file = (char_cptr)-1, long in_line = -1 );
  ExceptionBase( ExceptionBase const& e );
  virtual ~ExceptionBase(){}

private:
  ExceptionBase& operator = ( ExceptionBase const& e ){ return *this; } // 使用禁止
};


// _____________________________________________________________________________
/// @brief 例外クラス
///
// *****************************************************************************
template<class T_BasicException>
class Exception
  : public T_BasicException
  , public ExceptionBase
{
public:
  Exception( char_cptr in_str, char_cptr in_file = (char_cptr)-1, long in_line = -1 )
    : T_BasicException( in_str )
    , ExceptionBase( in_str, in_file, in_line )
  {
  }

  Exception( Exception const& e )
    : T_BasicException( e )
    , ExceptionBase( e )
  {
  }

  Exception()
  {}

private:
  Exception& operator = ( Exception const& e ){} // 使用禁止
};


// _____________________________________________________________________________
/// @brief 例外クラス（コードあり）
///
// *****************************************************************************
template<class T_BasicException>
class ExceptionCode
  : public Exception< T_BasicException >
{
  SDEF m_code;
public:
  SDEF GetCode() const { return m_code; }

  ExceptionCode
  ( SDEF in_code
  , char_cptr in_str
  , char_cptr in_file = (char_cptr)-1
  , long in_line = -1
  ) : Exception<T_BasicException>( in_str, in_file, in_line ), m_code( in_code ) {}
};


// _____________________________________________________________________________
/// @brief 例外クラス（コード指定付）
///
// *****************************************************************************
template<class T_BasicException, int T_Code>
class ExceptionCodeX
  : public ExceptionCode< T_BasicException >
{
public:
  ExceptionCodeX( SDEF in_code, char_cptr in_str, char_ptr in_file = (char_ptr)-1, long in_line = -1 )
    : ExceptionCode<T_BasicException>( T_Code, in_str, in_file, in_line ){}
};



// CPL 例外メッセージ取得関数
void ExceptionMessage( ExceptionBase* err, str_ptr buffer, int buf_size );


} // namespace Core

#define CPL_WIN_MSG_NOT_RESERVE    "領域の確保に失敗しました"
#define CPL_WIN_MSG_NOT_DELETE     "領域の解放に失敗しました"
#define CPL_WIN_MSG_NOT_RESIZE     "領域の拡張に失敗しました"
#define CPL_END_OF_FILE    "ファイルの終端を越えました？"
#define CPL_OVER_ACCESS    "バッファオーバーランを検出しました"
#define CPL_UNDER_ACCESS   "バッファアンダーランを検出しました"
#define CPL_NULL_ACCESS    "nullポインタを参照しました"
#define CPL_WIN_MSG_NOT_FILE_OPEN  "ファイルオープン失敗"
#define CPL_WIN_MSG_NOT_FILE_READ  "ファイル読み込み失敗"
#define CPL_WIN_MSG_NOT_FILE_WRITE "ファイル書き込み失敗"

// メッセージ例外スロー用
#define CPL_THROW(text)        {throw CPL::Core::Exception<std::exception>( text, __FILE__, __LINE__);}
#define CPL_THROW_T(type,text) {throw CPL::Core::Exception<type>( text, __FILE__, __LINE__);}
#define CPL_THROW_IF(flag,text)        if(flag) CPL_THROW(text)
#define CPL_THROW_IF_T(type,flag,text) if(flag) CPL_THROW(type,text)
#define CPL_THROW_CODE( code,text )        { throw CPL::Core::ExceptionCode<std::exception>( code, text, __FILE__, __LINE__ ); }
#define CPL_THROW_CODE_T( type,code,text ) { throw CPL::Core::ExceptionCode<type>( type, code, text, __FILE__, __LINE__ ); }
#define CPL_THROW_CODE_IF( flag,code,text )        if(flag) CPL_THROW_CODE(code,text)
#define CPL_THROW_CODE_IF_T( type,flag,code,text ) if(flag) CPL_THROW_CODE(type,code,text)

//#ifdef _MSC_VER
//  #define CPL_THROWorRETURN(is_throw,ret_code,text,...)\
//    if(is_throw){ CPL_THROW_X(ret_code,text,__VA_ARGS__) }\
//    else { CPL_DBG_OUTPUT_A(text,__VA_ARGS__) return ret_code; }
//  #define CPL_THROW_X(throw_code,...)\
//    { CPL_DBG_OUTPUT_A(text,__VA_ARGS__) throw throw_code; }
//  #define CPL_THROW_X_IF(flag, throw_code, text,...)\
//    if(flag) CPL_THROW_X(throw_code, text,__VA_ARGS__)
//  #define CPL_THROWorRETURN_IF(flag,is_throw,ret_code,text,...)\
//    if(flag){ CPL_THROWorRETURN(is_throw,ret_code,text,__VA_ARGS__) }
//
//#elif defined(__clang__) || defined(__GNUC__)
  #define CPL_THROWorRETURN(is_throw,ret_code,...)\
    if(is_throw){ CPL_THROW_X(ret_code,__VA_ARGS__) }\
    else { CPL_DBG_OUTPUT_A(__VA_ARGS__) return ret_code; }
  #define CPL_THROW_X(throw_code,...)\
    { CPL_DBG_OUTPUT_A(__VA_ARGS__) throw throw_code; }
  #define CPL_THROW_X_IF(flag, throw_code, ...)\
    if(flag) CPL_THROW_X(throw_code, __VA_ARGS__)
  #define CPL_THROWorRETURN_IF(flag,is_throw,ret_code,...)\
    if(flag){ CPL_THROWorRETURN(is_throw,ret_code,__VA_ARGS__) }
//#endif


// デバッグ時のみメッセージ例外スロー用
#ifdef CPL_DEBUG
#define CPL_THROW2(text)        {throw CPL::Core::Exception<std::exception>( text, __FILE__, __LINE__);}
#define CPL_THROW2_T(type,text) {throw CPL::Core::Exception<type>( text, __FILE__, __LINE__);}
#define CPL_THROW_IF2(flag,text)        if(flag) CPL_THROW2(text)
#define CPL_THROW_IF2_T(type,flag,text) if(flag) CPL_THROW2(type,text)
#define CPL_THROW_CODE2(code,text)        {throw CPL::Core::ExceptionCode<std::exception>(code, text, __FILE__, __LINE__);}
#define CPL_THROW_CODE2_T(type,code,text) {throw CPL::Core::ExceptionCode<type>(code, text, __FILE__, __LINE__);}
#define CPL_THROW_CODE_IF2(flag,code,text)        if(flag) CPL_THROW_CODE2(code,text)
#define CPL_THROW_CODE_IF2_T(type,flag,code,text) if(flag) CPL_THROW_CODE2(type,code,text)
#else
#define CPL_THROW2(text)        {throw CPL::Core::Exception<std::exception>( "", __FILE__, __LINE__);}
#define CPL_THROW2_T(type,text) {throw CPL::Core::Exception<type>( "", __FILE__, __LINE__);}
#define CPL_THROW_IF2(flag,text)        if(flag) CPL_THROW2(text)
#define CPL_THROW_IF2_T(type,flag,text) if(flag) CPL_THROW2(type,text)
#define CPL_THROW_CODE2(code,text)        {throw CPL::Core::ExceptionCode<std::exception>(code, "", __FILE__, __LINE__);}
#define CPL_THROW_CODE2_T(type,code,text) {throw CPL::Core::ExceptionCode<type>(code, "", __FILE__, __LINE__);}
#define CPL_THROW_CODE_IF2(flag,code,text)        if(flag) CPL_THROW_CODE2(code,text)
#define CPL_THROW_CODE_IF2_T(type,flag,code,text) if(flag) CPL_THROW_CODE2(type,code,text)
#endif



} // namespace CPL
