/**
 * @file   AXFActor.hpp
 * @brief  AXFActorクラスヘッダ
 *
 * @par    Programming Language
 *         C++
 */

#ifndef AXFACTOR_HPP_
#define AXFACTOR_HPP_

#include <signal.h>

#include "AXCStdint.hpp"

#include "AXFStatus.hpp"
#include "AXFEvent.hpp"
#include "AXFObjectName.hpp"
#include "AXFLog.hpp"
#include "AXFTask.hpp"
#include "AXFChrono.hpp"

class AXFMailboxInternal;
class AXFStateMachineInternal;
class AXFTimerInternal;
class AXFConditionVariableInternal;

/**
 * @class AXFActor
 * @brief アクタ(スレッド、タスク)に対し、基底クラスを提供する
 *
 * @n
 * @n     以下の機能をAXM層に提供する。
 * @n       - アクタ間送信
 * @n       - アクタ共通のライフサイクルのイベント受信・状態制御
 * @n       - アクタ個別のイベント受信・状態制御(StateMachine)
 * @n       - タイマ操作
 * @n
 * @n     1. アクタ間送信について
 * @n      宛先にアクタ名を指定し、イベントと任意のサイズのデータを送信する。
 * @n
 * @n      イベントとデータフォーマットは、あらかじめアクタ間で決定しておく必要がある。
 * @n
 * @n      イベント は型に AXFEvent_t を使用し、モデル層が独自に定義するイベントは
 * @n      AXFEVENT_MODELマクロを使用して定義する。
 * @n
 * @n      データ転送に関して、フレームワークでは unsigned char型として、
 * @n      指定されたサイズを memcpy する。
 * @n      データにポインタを置く場合、そのポインタ先のデータのスコープは、
 * @n      要求元で管理する必要がある。
 * @n      また、送信データに C++オブジェクト を置いてはならない。
 * @n
 * @n      イベント受信は 2. と 3. で説明するライフサイクルと StateMachine のハンドラのみで行う。
 * @n
 * @n     2. アクタ・ライフサイクルについて
 * @n      Android Activity ライフサイクルと同じにする。
 * @n      Model は、以下のメソッドをオーバライドして実装する必要がある。
 * @n       - onCreate()
 * @n         AXFActor onCreateメソッドをオーバライドして実装する
 * @n       - onDestroy()
 * @n         AXFActor onDestroyメソッドをオーバライドして実装する
 * @n       - onStart()
 * @n         AXFActor onStartメソッドをオーバライドして実装する
 * @n       - onStop()
 * @n         AXFActor onStopメソッドをオーバライドして実装する
 * @n       - onPause()
 * @n         AXFActor onPauseメソッドをオーバライドして実装する
 * @n       - onResume()
 * @n         AXFActor onResumeメソッドをオーバライドして実装する
 * @n       - onRestart()
 * @n         AXFActor onRestartメソッドをオーバライドして実装する
 * @n
 * @n     3. StateMachine について
 * @n      ライフサイクル以外で、アクタ個別に定義するイベントと状態を使用する場合、
 * @n      StateMachine を使用する。
 * @n      StateMachine にあらかじめイベントに対応したメソッドを登録しておくと、
 * @n      該当するイベントを受信したときに対応するメソッドが呼び出される。
 * @n      登録は状態単位で行う。
 * @n
 * @n     4. タイマ操作
 * @n      AXフレームワークにおいて、tick とは 1/60000秒 のカウンタ のことを示す。
 * @n      タイマ指定は 秒カウンタと tick を使用する。
 * @n
 * @n     AXFActorクラスの 詳細は AXCTaskクラス の説明に記述する。
 *
 */
class AXFActor : AXFTask {
  AXFObjectName& m_name;
  AXFLog m_log;
  AXFTask* m_thread;
  volatile bool m_isActive;
  bool m_isCreated;
  AXFStateMachineInternal* m_stateMachine;
  AXFMailboxInternal* m_mailbox;
  AXFTimerInternal* m_timer;
  AXFConditionVariableInternal* m_axsimCond;
  bool m_isAxSimCondWait;
  INT32_t m_actorId;
  ax::actorFuncStatus m_scriptHandlerStat;

 public:
  typedef ax::actorFuncStatus (AXFActor::*StateFuncPtr)(const void *, int);  ///< StateMachine用のメンバ関数ポインタ定義

  /**
   * @class StateTable
   * @brief ライフサイクル以外で、アクタ個別の状態遷移に必要な情報を格納する構造体
   */
  struct StateTable {
    AXFEvent_t doEvent;                                     ///< イベントID
    ax::actorFuncStatus (AXFActor::*pFunc)(const void *, int);  ///< 実行メンバ関数
  };

  typedef int TimerId_t;  ///< AXFTimerID型

  /**
   * @struct timeSpec
   * @brief タイマ設定に使用する構造体(秒、tick)
   */
  struct timeSpec {
    INT32_t sec;   ///< 秒
    INT32_t tick;  ///< tick
  };

  /** @enum  timerStatus
   *  @brief タイマメソッド実行完了状態
   */
  enum timerStatus {
    TIMER_SUCCESS = 0,      ///< 正常終了
    TIMER_ERROR_PARAMETER,  ///< 異常終了 パラメータエラー
    TIMER_ERROR_INIT,       ///< 異常終了 タイマの初期化エラー
    TIMER_ERROR_CREATE,     ///< 異常終了 タイマの生成エラー
    TIMER_ERROR_SIGACTION,  ///< 異常終了 タイマのシグナルアクション生成エラー
    TIMER_ERROR_SETTIME,    ///< 異常終了 タイマの設定と開始エラー
    TIMER_ERROR_GETTIME,    ///< 異常終了 タイマの情報取得エラー
    TIMER_ERROR_DELTIME,    ///< 異常終了 タイマの削除エラー
    TIMER_ERROR_MUTEXINIT,  ///< 異常終了 タイマのMutex initエラー
    TIMER_ERROR_MUTEXLOCK,  ///< 異常終了 タイマのMutex lockエラー
    TIMER_ERROR_NOCREATE,   ///< 異常終了 タイマインスタンス未生成エラー
    TIMER_ERROR_NOMEM,      ///< 異常終了 タイマメモリ不足エラー
  };

  /** @enum  actorState
   *  @brief AXFActorライフサイクル状態
   */
  enum actorState {
    AXFACTOR_STATE_INITIALIZE = 0,  ///< 初期化状態
    AXFACTOR_STATE_ACTIVE,          ///< 実行状態
    AXFACTOR_STATE_SUSPEND,         ///< 中断状態
    AXFACTOR_STATE_INACTIVE,        ///< 停止状態
    AXFACTOR_STATE_MAX
  };

  /**
   * @brief AXFActorコンストラクタ
   * @n
   * @param [in] name アクタ名
   */
  AXFActor(AXFObjectName& name, AXFConditionVariableInternal* axsimCond = NULL);

  /**
   * @brief AXFActorデストラクタ
   */
  virtual ~AXFActor();

  /**
   * @brief 新しいスレッドを生成する。
   * @n     AXFStage のみ使用する
   * @param [in] priority 生成するタスクの優先度
   * @param [in] stackSize 生成するタスクのスタックサイズ
   */
  taskStatus create(AXFTask::taskPriority priority, size_t stackSize);

  /**
   * @brief アクタの終了を待つ。
   */
  taskStatus join();

  /**
   * @brief アクタにキャンセル要求する。
   * @n     axdaemon のみ使用する
   */
  taskStatus cancel();

  /**
   * @brief アクタに必要なリソースを確保する
   * @n
   * @return 実行完了状態
   */
  virtual ax::actorFuncStatus onCreate();

  /**
   * @brief アクタに必要なリソースを破棄する
   * @n
   * @return 実行完了状態
   */
  virtual ax::actorFuncStatus onDestroy();

  /**
   * @brief アクタ開始処理を行う
   * @n     AXFActorを継承するクラスは必ずオーバライドする必要がある。
   * @n
   * @return 実行完了状態
   */
  virtual ax::actorFuncStatus onStart() = 0;

  /**
   * @brief アクタ終了処理を行う
   * @n     AXFActorを継承するクラスは必ずオーバライドする必要がある。
   * @n     本メソッドを終了すると、onRestart が呼び出され終了するまで
   * @n     イベントを受信することはできない。
   * @n     終了状態で受信したイベントは全て破棄される。
   * @n
   * @return 実行完了状態
   *
   */
  virtual ax::actorFuncStatus onStop() = 0;

  /**
   * @brief アクタ中断処理を行う
   * @n     本メソッドを終了すると、onResume もしくは onRestart が
   * @n     呼び出され終了するまでイベントを受信することはできない。
   * @n     中断状態で受信したイベントは全て破棄される。
   * @n
   * @return 実行完了状態
   */
  virtual ax::actorFuncStatus onPause();

  /**
   * @brief アクタ再開処理を行う
   * @n
   * @return 実行完了状態
   */
  virtual ax::actorFuncStatus onResume();

  /**
   * @brief アクタ再開始処理を行う
   * @n
   * @return 実行完了状態
   *
   */
  virtual ax::actorFuncStatus onRestart();

  /**
   * @brief AXFActorライフサイクル状態を取得する
   * @n
   * @return AXFActorライフサイクル状態
   */
  actorState getLifeCycleState();

  /**
   * @brief StateMachine状態を取得する
   * @n
   * @return StateMachine状態
   */
  int getStateMachineState();

  /**
   * @brief アクタ送信処理を行う
   * @n
   * @n     詳細はAXFActorクラスの記述を参照
   * @n
   * @param [in] name 送信宛アクタ名
   * @param [in] eventId イベント
   * @param [in] data 送信データ
   * @param [in] size 送信データサイズ
   * @return 送信完了データサイズ(送信失敗時は 0 未満の値を返す)
   */
  virtual int send(AXFObjectName& name, AXFEvent_t eventId, void* data,
                   int size);

  /**
   * @brief StateMachine に必要な情報を登録する
   *
   * @param[in] obj          自オブジェクト
   * @param[in] initState    状態遷移の初期状態
   * @param[in] maxState     状態の最大数  (状態の定義は、enumで0オリジンで作成されていること）
   * @return 作成可否
   */
  virtual int setStateInfo(AXFActor* obj, int initState, int maxState);

  /**
   * @brief StateMachine に状態テーブルを登録する
   * @n
   * @n      第1引数で指定する状態で動作する状態テーブルを登録する
   * @n
   * @param[in] registState    登録する状態
   * @param[in] *eventTable    状態テーブルのポインタ
   */
  virtual void setStateTable(int registState, const StateTable* stateTable);

  /**
   * @brief StateMachine に次に遷移する状態を設定する
   *
   * @param[in] nextState    遷移先の状態
   */
  void setNextState(int nextState);

  /**
   * @brief タイマーの生成を行う
   * @n     本メソッドは、タイマーを使用するアクタの onCreate() で呼び出す
   * @n
   * @return 実行完了状態
   */
  virtual timerStatus createTimer();

  /**
   * @brief タイマーの削除を行う
   * @n     本メソッドは、タイマーを使用するアクタの onDestroy() で呼び出す
   * @n
   * @return 実行完了状態
   */
  virtual timerStatus deleteTimer();

  /**
   * @brief 周期タイマーの設定と開始を行う
   * @n
   * @param[in]  timer         周期時間（秒 + tick）
   * @param[in]  timerEventId  イベント（タイマー満了時に、発行されるイベント）
   * @param[out] *TimerId      AXFタイマーID（stopIntervalTimer、getTimer利用時に使用する）
   * @n
   * @return 実行完了状態
   */
  virtual timerStatus startIntervalTimer(timeSpec timer,
                                         AXFEvent_t timerEventId,
                                         TimerId_t* TimerId);

  /**
   * @brief 単発タイマーの設定と開始を行う
   * @n
   * @param[in]  timer         満了時間（秒 + tick）
   * @param[in]  timerEventId  タイマー満了時に、発行されるイベント
   * @param[out] *TimerId      AXFタイマーID（getTimer利用時に使用する）
   * @n
   * @return 実行完了状態
   */
  timerStatus startSingleShotTimer(timeSpec timer, AXFEvent_t timerEventId,
                                   TimerId_t* TimerId);

  /**
   * @brief 周期タイマーの停止を行う
   * @n
   * @param[in] TimerId  AXFタイマーID（startIntervalTimerで取得したTimerIdを設定）
   * @n
   * @return 実行完了状態
   */
  virtual timerStatus stopIntervalTimer(TimerId_t TimerId);

  /**
   * @brief 単発タイマーの停止を行う
   * @n
   * @param[in] TimerId  AXFタイマーID（startSingleShotTimerで取得したTimerIdを設定）
   * @n
   * @return 実行完了状態
   */
  timerStatus stopSingleShotTimer(TimerId_t TimerId);

  /**
   * @brief タイマー情報の取得を行う
   * @n
   * @param[in]  TimerId  タイマーID（startIntervalTimerで取得したTimerIdを設定）
   * @param[out] timerspec  タイマー開始からの経過時間（秒 + tick）
   * @n
   * @return 実行完了状態
   */
  timerStatus getTimer(TimerId_t TimerId, timeSpec* timerspec);

#ifndef UNUSE_CHRONO	// baba Chrono非対応
  /**
   * @brief 現在時刻を取得する
   * @return 現在時刻
   */
  AXFChrono getCurrentTime();
#endif	/* UNUSE_CHRONO */

  /**
   * @brief Mailbox に格納されている先頭メッセージのデータサイズを返す
   * @return データサイズ
   */
  int peekMailbox();

  /**
   * @brief Mailbox に格納されているメッセージの個数を返す
   * @return メッセージ数
   */
  int npeekMailbox();

  /**
   * @brief 自Nameオブジェクト・リファレンスを返す
   * @return 自Nameオブジェクト・リファレンス
   */
  AXFObjectName& getNameObject();

  /**
   * @brief 自アクタIDを返す
   * @return アクタID
   */
  INT32_t getActorId() {
    return m_actorId;
  }

  /**
   * @brief シミュレーション・モード時、アクタを起床させ実行権を与える
   * @n     axdaemon のみ使用する
   */
  bool signalAxSimCond();

#ifndef	UNUSE_CHRONO	// baba Chrono非対応
  /**
   * @brief シミュレーション・モード時、現在時刻を 1 Tick 進める
   * @n     axdaemon のみ使用する
   */
  void incrementCurrentTime();

  /**
   * @brief シミュレーション・モード時、現在時刻を n Tick 進める
   * @n     axdaemon のみ使用する
   */
  void incrementCurrentTime(INT64_t n);
#endif	/* UNUSE_CHRONO */

  /**
   * @brief シミュレーション・モード時、AXSIM Wait状態か確認する
   * @n     axdaemon のみ使用する
   */
  bool isAxSimWait();

  /**
   * @brief スクリプト実行時、ハンドラ結果を返す
   * @n     axdaemon のみ使用する
   */
  ax::actorFuncStatus getScriptHandlerStatus();

 private:
  actorState m_lifeCycleState;
#ifndef	UNUSE_CHRONO	// baba Chrono非対応
  AXFChrono m_currentTime;  ///< AXFActor現在時刻
#endif	/* UNUSE_CHRONO */

  ax::actorFuncStatus doHandler(AXFEvent_t eventId, const void* pParam, int size);
  void *do_worker_sub();
  bool setupRcvMailbox(int& rcvDataSize);
  void actorLoop(UINT8_t* rcvData, int rcvDataSize);

  static void *do_worker(void* obj) {
    AXFActor *fActor = reinterpret_cast<AXFActor *>(obj);
    return fActor->do_worker_sub();
  }

  /**
   * @brief LifeCycle状態遷移を行う
   * @param[in]  state 遷移先LifeCycle状態値
   */
  void transLifeCycleState(actorState state);

  // インスタンスのコピーは想定しない(Klockwork指摘対応:CL.FFM.ASSIGN)
  AXFActor& operator=(const AXFActor&) {
    return *this;
  }

  // インスタンスのコピーは想定しない(Klockwork指摘対応:CL.FFM.COPY)
  AXFActor(const AXFActor& obj)
      : m_name(obj.m_name),
        m_log(obj.m_log),
        m_thread(obj.m_thread),
        m_isActive(obj.m_isActive),
        m_isCreated(obj.m_isCreated),
        m_stateMachine(obj.m_stateMachine),
        m_mailbox(obj.m_mailbox),
        m_timer(obj.m_timer),
        m_axsimCond(obj.m_axsimCond),
        m_isAxSimCondWait(obj.m_isAxSimCondWait),
        m_actorId(obj.m_actorId),
        m_scriptHandlerStat(obj.m_scriptHandlerStat),
        m_lifeCycleState(obj.m_lifeCycleState) {
  }
};
#endif /* AXFACTOR_HPP_ */
