/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2002/X/X - 2015/11/13
/// 
/// @brief  イベント管理クラス。
/// 
/// ****************************************************************************

#pragma once

#include "CPL_Core_Basic.h"
#include "../Pointer/CPL_Core_Pointer.h"
#include "../Container/List/CPL_Core_SinglyList.h"


namespace CPL {
namespace Core {


enum eEVENT_THROW_FLAGS
{ kEVENT_THROW_ADD
, kEVENT_THROW_ADD_ONLY_FIRST
, kEVENT_THROW_ADD_ONLY_LAST
};


// _____________________________________________________________________________
/// @brief イベントアイテムクラス
///
// *****************************************************************************
class EventItem
  : public List::DoublyElement<EventItem>
{
private:
  typedef List::DoublyElement<EventItem> type_base;

  EventItem( EventItem const& ); // 使用禁止。

public:
  virtual void operator ()(){}; // オーバーライドして処理を記述すること。
  virtual bool Comp( EventItem const* ){ return false; } // 既存検査？をする場合のチェック関数
  virtual ~EventItem(){} // 

  //
  EventItem(){}
  EventItem( EventItem&& e ) : type_base( std::move(e) )
  {}
};


// _____________________________________________________________________________
/// @brief イベントキャッチャー
///
/// イベントキャッチャーオブジェクトが存命中は、そのスレッドにおけるイベントを
/// 全て保持し、デストラクタでまとめてイベントをコールする。\n
/// これらは、スレッド単位となる。
///
// *****************************************************************************
class EventCatcher
  : public Core::BaseObject
{
protected:
  friend class EventCatcher;

  //CPL_SINGLETON_INIT( EventCatcher*, sm_ptr_evt_store, nullptr )
  CPL_THREAD_SINGLETON( EventCatcher*, stm_ptr_evt_store )

  EventCatcher* m_ptr_event_store_old;
  EventItem*    m_ptr_event_top;
  EventItem*    m_ptr_event_end;

public:
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  EventCatcher();

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ~EventCatcher();

  // _____________________________________________________________________________
  /// @brief イベントを追加。
  ///
  /// @param[in] in_event : 追加対象のイベントクラス。
  ///
  /// @retval true  : リストに追加。
  /// @retval false : リスト追加失敗。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_EVT>
  static bool Add( T_EVT&& in_event )
  {
    T_EVT* ptr_new_event = nullptr;
    if( !stm_ptr_evt_store() )
    {
      return false;
    }
    else
    if( stm_ptr_evt_store()->m_ptr_event_end )
    {
      ptr_new_event = new T_EVT( std::forward<T_EVT>(in_event) );
      stm_ptr_evt_store()->m_ptr_event_end->AddNodeNext( ptr_new_event );
    }
    else
    {
      ptr_new_event = new T_EVT( std::forward<T_EVT>(in_event) );
      stm_ptr_evt_store()->m_ptr_event_top = ptr_new_event;
    }
    stm_ptr_evt_store()->m_ptr_event_end = ptr_new_event;
    return true;
  }
  
  // _____________________________________________________________________________
  /// @brief 限定追加（最初のみ）。
  ///
  /// 同じものがあれば、最初のアイテムのみ保持される（つまり追加されない）
  /// 
  /// @param[in] in_event : 追加対象のイベントクラス。
  ///
  /// @retval true  : リストに追加。
  /// @retval false : リスト追加失敗。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Event>
  static bool AddOnlyFirst( T_Event&& in_event )
  {
    if( !stm_ptr_evt_store() )
    {
      return false;
    }
    if( Check( &in_event ) == nullptr )
    {
      return Add( std::forward(in_event) );
    }
    return false;
  }
  
  // _____________________________________________________________________________
  /// @brief 限定追加（最後のみ）
  ///
  /// 同じものがあれば、最後のアイテムのみ保持される（先のイベントは破棄される）
  ///
  /// @param[in] in_event : 追加対象のイベントクラス。
  ///
  /// @retval true  : リストに追加。
  /// @retval false : リスト追加失敗。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_EVT>
  static bool AddOnlyLast( T_EVT&& in_event )
  {
    if( !stm_ptr_evt_store() )
    {
      return false;
    }
    // 
    EventItem*& ptr_event_top = stm_ptr_evt_store()->m_ptr_event_top;
    EventItem*& ptr_event_end = stm_ptr_evt_store()->m_ptr_event_end;
    EventItem* ptr_check_event = ptr_event_top;
    if( !ptr_check_event ) // 先頭の要素が設定されていなければ、そのまま追加。
    {
      return Add( std::forward(&in_event) );
    }

    // 要素がある場合、一致する要素
    while( ptr_check_event )
    {
      // 比較。
      if( ptr_check_event->Comp( in_event ) ) // 同じと判断された場合。
      {
        if( ptr_check_event == ptr_event_end )
        {
          // 末尾の要素と一致した場合、新たに指定されたもので末尾の要素を上書きして抜ける。
          *ptr_check_event = in_event;
          break;
        }
        if( ptr_check_event == ptr_event_top )
        {
          ptr_event_top = ptr_event_top->GetNodeNext();
        }
        DEL( ptr_check_event, ptr_check_event->GetNodeNext() );
      }
      else
      {
        ptr_check_event = ptr_check_event->GetNodeNext();
      }
    }
    return Add( std::forward(in_event) );
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  static EventItem* Check( EventItem* in_ptr_event );

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  EventItem const* GetEventTop() const { return m_ptr_event_top; }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  EventItem* GetEventTop(){ return m_ptr_event_top; }
};




} // namespace Core
} // namespace CPL
