/*
 * AXFActor.cpp
 */

#include "AXFActor.hpp"
#include "AXFStage.hpp"
#include "AXFMailboxInternal.hpp"
#include "AXFTimerInternal.hpp"
#include "AXFStateMachineInternal.hpp"
#include "AXFCommSendInternal.hpp"
#include "AX.hpp"
#include "AXFConditionVariableInternal.hpp"
#include "AXFTimeDomainInternal.hpp"
#include "AXFWatchdogInternal.hpp"
#include "AXFDaemonInternal.hpp"

#include <sstream>
#include <string>

using namespace ax;

AXFActor::AXFActor(AXFObjectName& name, AXFConditionVariableInternal* axsimCond)
    : m_name(name),
      m_log(name),
      m_thread(NULL),
      m_isActive(true),
      m_isCreated(false),
      m_stateMachine(NULL),
      m_mailbox(NULL),
      m_timer(NULL),
      m_axsimCond(axsimCond),
      m_isAxSimCondWait(false),
      m_actorId(-1),
      m_scriptHandlerStat(AXFACTOR_ERROR),
      m_lifeCycleState(AXFACTOR_STATE_INITIALIZE) {
  m_log.write(AXFLOG_DEBUG,
              "AXFActor create name= " + m_name.getFullActorName());
}

AXFActor::~AXFActor() {
  m_log.write(AXFLOG_DEBUG,
              "AXFActor destroy name= " + m_name.getFullActorName());
  delete m_thread;
  m_thread = NULL;
  if (NULL != m_stateMachine) {
    delete m_stateMachine;
    m_stateMachine = NULL;
  }

  if (NULL != m_timer) {
    delete m_timer;
    m_timer = NULL;
  }

  delete m_axsimCond;
  m_axsimCond = NULL;
}

AXFActor::taskStatus AXFActor::create(AXFTask::taskPriority priority,
                                      size_t stackSize) {
  if (m_isCreated) return TASK_ERROR;
  if (NULL == m_axsimCond) {
    m_axsimCond = new (std::nothrow) AXFConditionVariableInternal();
  }
  //@UTIGN new
  if (NULL == m_axsimCond) {
    m_log.write(
        AXFLOG_ERR,
        "AXFActor fail new axmsimCond name= " + m_name.getFullActorName());
    return TASK_ERROR;
  }
  if (AXFConditionVariableInternal::COND_SUCCESS != m_axsimCond->init()) {
    m_log.write(
        AXFLOG_ERR,
        "AXFActor fail init axmsimCond name= " + m_name.getFullActorName());
    return TASK_ERROR;
  }

  AXFStageActorInfo* actorInfo;
  m_actorId = AXFStage::getInstance()->getActorInfo(m_name, &actorInfo);
  if (0 > m_actorId) {
    m_log.write(AXFLOG_ERR, "fail get ActorInfo " + m_name.getFullActorName());
    return TASK_ERROR;
  }

  m_thread = new (std::nothrow) AXFTask();
  //@UTIGN new
  if (NULL == m_thread) {
    return TASK_ERROR;
  }
  taskStatus ret = m_thread->create(m_name.getActorName(), priority, stackSize,
                                    &AXFActor::do_worker, this);
  m_isCreated = (ret == TASK_SUCCESS);
  return ret;
}

AXFActor::taskStatus AXFActor::join() {
  if (NULL == m_thread) {
    return TASK_ERROR_NOCREATE;
  }

  return m_thread->join();
}

AXFActor::taskStatus AXFActor::cancel() {
  if (NULL == m_thread) {
    return TASK_ERROR_NOCREATE;
  }

  m_isActive = false;
  return TASK_SUCCESS;
}

actorFuncStatus AXFActor::onCreate() {
  m_log.write(AXFLOG_DEBUG, "AXFActor onCreate()");
  return AXFACTOR_SUCCESS;
}

actorFuncStatus AXFActor::onDestroy() {
  m_log.write(AXFLOG_DEBUG, "AXFActor onDestroy()");
  return AXFACTOR_SUCCESS;
}

actorFuncStatus AXFActor::onResume() {
  m_log.write(AXFLOG_DEBUG, "AXFActor onResume()");
  return AXFACTOR_SUCCESS;
}

actorFuncStatus AXFActor::onPause() {
  m_log.write(AXFLOG_DEBUG, "AXFActor onPause()");
  return AXFACTOR_SUCCESS;
}

actorFuncStatus AXFActor::onRestart() {
  m_log.write(AXFLOG_DEBUG, "AXFActor onRestart()");
  return AXFACTOR_SUCCESS;
}

AXFActor::actorState AXFActor::getLifeCycleState() {
  return m_lifeCycleState;
}

int AXFActor::getStateMachineState() {
  if (NULL == m_stateMachine) {
    return -1;
  } else {
    return m_stateMachine->m_state;
  }
}

int AXFActor::send(AXFObjectName& name, AXFEvent_t eventId, void* data,
                   int size) {

  if ((NULL != AXFStage::m_axdaemon) &&
      (AXFStage::m_axdaemon->isScriptMode())) {
    // @ToDo: 宛先,イベントID,パラメータを退避し、AXScriptハンドラ実行結果の期待値検証で使用する
    return size;
  }

  if (name.isCurrentStage()) {
    AXFStageActorInfo* actorInfo;
    AXFMailboxInternal* mailbox;
    if (0 > AXFStage::getInstance()->getActorInfo(name, &actorInfo)) {
      m_log.write(AXFLOG_ERR, "fail send mailRef " + name.getFullActorName());
      return -1;
    }
    mailbox = actorInfo->mailRef;
    return mailbox->send(eventId, data, size);
  }

  if (NULL == AXFStage::m_commSend) {
    m_log.write(AXFLOG_ERR, "@@@@ AXFStage::m_commSend is NULL!!");
    return -1;
  }
  return AXFStage::m_commSend->send(name, eventId, data, size);
}

ax::actorFuncStatus AXFActor::doHandler(AXFEvent_t eventId, const void* pParam, int size) {
  AXFEvent_t eventCategory;
  actorFuncStatus ret = ax::AXFACTOR_ERROR;

#if 0 // bbtest
		std::string strFullActNm = m_name.getFullActorName();
#endif
	
  // AXFイベントカテゴリの取得
  eventCategory = AXFEVENT_CATEGORY_MASK& eventId;

  switch (eventCategory) {
    // アプリケーションメッセージ
    case AXFEVENT_CATEGORY_DEVICE:
    case AXFEVENT_CATEGORY_MODEL:
    case AXFEVENT_CATEGORY_SYS:
    if (NULL != m_stateMachine) {
      ret = m_stateMachine->doHandler(eventId, pParam, size);
      if (ax::AXFACTOR_SUCCESS != ret) {
        std::ostringstream handleStatusStr;
        handleStatusStr << ret;
        std::ostringstream eventIdStr;
        eventIdStr << std::hex << eventId;
        m_log.write(AXFLOG_INFO, "doHandler() stat=" + handleStatusStr.str() + " eventId=" + eventIdStr.str());
      }
    }
    break;

    default:
    {
      std::ostringstream eventIdStr;
      eventIdStr << std::hex << eventId;
      m_log.write(AXFLOG_DEBUG, "unknown eventId=" + eventIdStr.str());
    }
    break;
  }

  return ret;
}

int AXFActor::setStateInfo(AXFActor* obj, int initState, int maxState) {
  m_stateMachine = new (std::nothrow) AXFStateMachineInternal();
  //@UTIGN new
  if (NULL == m_stateMachine) {
    return -1;
  }
  int ret = m_stateMachine->setStateInfo(obj, initState, maxState);
  return ret;
}

void AXFActor::setStateTable(int registState, const StateTable* stateTable) {
  m_stateMachine->setStateTable(registState, stateTable);
}

void AXFActor::setNextState(int nextState) {
  m_stateMachine->setNextState(nextState);
}

AXFActor::timerStatus AXFActor::startIntervalTimer(timeSpec timer,
                                                   AXFEvent_t timerEventId,
                                                   TimerId_t* TimerId) {
  if (NULL == m_timer) {
    m_log.write(AXFLOG_ERR, "timer error: no create timer instance");
    return TIMER_ERROR_NOCREATE;
  }
  return m_timer->addTimer(this, timer, AXFTimerInternal::INTERVAL_TIMER,
                           timerEventId, TimerId);
}

AXFActor::timerStatus AXFActor::startSingleShotTimer(timeSpec timer,
                                                     AXFEvent_t timerEventId,
                                                     TimerId_t* TimerId) {
  if (NULL == m_timer) {
    m_log.write(AXFLOG_ERR, "timer error: no create timer instance");
    return TIMER_ERROR_NOCREATE;
  }
  return m_timer->addTimer(this, timer, AXFTimerInternal::SINGLE_SHOT_TIMER,
                           timerEventId, TimerId);
}

AXFActor::timerStatus AXFActor::stopIntervalTimer(TimerId_t TimerId) {
  if (NULL == m_timer) {
    m_log.write(AXFLOG_ERR, "timer error: no create timer instance");
    return TIMER_ERROR_NOCREATE;
  }
  return m_timer->delTimer(TimerId);
}

AXFActor::timerStatus AXFActor::stopSingleShotTimer(TimerId_t TimerId) {
  if (NULL == m_timer) {
    m_log.write(AXFLOG_ERR, "timer error: no create timer instance");
    return TIMER_ERROR_NOCREATE;
  }
  return m_timer->delTimer(TimerId);
}

AXFActor::timerStatus AXFActor::getTimer(TimerId_t TimerId,
                                         timeSpec* timerspec) {
  if (NULL == m_timer) {
    m_log.write(AXFLOG_ERR, "timer error: no create timer instance");
    return TIMER_ERROR_NOCREATE;
  }
  return m_timer->getTimer(TimerId, timerspec);
}

AXFActor::timerStatus AXFActor::createTimer() {
  if (NULL != m_timer) {
    return TIMER_ERROR_INIT;
  }
  m_timer = new (std::nothrow) AXFTimerInternal(m_name);
  //@UTIGN new
  if (NULL == m_timer) {
    return TIMER_ERROR_NOMEM;
  }
  return m_timer->initTimer();
}

AXFActor::timerStatus AXFActor::deleteTimer() {
  if (NULL == m_timer) {
    m_log.write(AXFLOG_ERR, "timer error: no create timer instance");
    return TIMER_ERROR_NOCREATE;
  }
  delete m_timer;
  m_timer = NULL;
  return TIMER_SUCCESS;
}

#ifndef	UNUSE_CHRONO	// baba Chrono非対応
AXFChrono AXFActor::getCurrentTime() {
  if (false == AXFStage::getInstance()->m_timeDomain->isActive()) {
    // システムから現在時刻を取得しカウンタに設定する
    m_currentTime.setCounter();
  }
  return m_currentTime;
}
#endif	/* UNUSE_CHRONO */

int AXFActor::peekMailbox() {
  if (NULL == m_mailbox) {
    return 0;
  }
  return m_mailbox->peek();
}

int AXFActor::npeekMailbox() {
  if (NULL == m_mailbox) {
    return 0;
  }
  return m_mailbox->npeek();
}

AXFObjectName& AXFActor::getNameObject() {
  return m_name;
}

bool AXFActor::signalAxSimCond() {
  if (AXFConditionVariableInternal::COND_SUCCESS != m_axsimCond->signal()) {
    return false;
  }
  return true;
}

#ifndef	UNUSE_CHRONO	// baba Chrono非対応
void AXFActor::incrementCurrentTime() {
  m_currentTime.upCounter(1);
  return;
}

void AXFActor::incrementCurrentTime(INT64_t n) {
  m_currentTime.upCounter(n);
}
#endif	/* UNUSE_CHRONO */

bool AXFActor::isAxSimWait() {
  return m_isAxSimCondWait;
}

ax::actorFuncStatus AXFActor::getScriptHandlerStatus() {
  return m_scriptHandlerStat;
}

void AXFActor::transLifeCycleState(actorState state) {
  m_lifeCycleState = state;
#ifndef	UNUSE_CHRONO	// baba Chrono非対応のためAXFTimeDomainInternalは現状非対応
  AXFStage::m_timeDomain->commitState(this);
#endif	/* UNUSE_CHRONO */
}

void* AXFActor::do_worker_sub() {
#ifndef	UNUSE_CHRONO	// baba Chrono非対応
  m_currentTime.setCounter();
#endif	/* UNUSE_CHRONO */

  m_log.write(AXFLOG_DEBUG, "call do_worker()");

  UINT8_t* rcvData;
  int rcvDataSize;

  // 受信Mailboxを設定する
  if (false == setupRcvMailbox(rcvDataSize)) {
    //@UTIGN exitSelf
    return NULL;
  }

  // 受信データ格納用バッファを確保する
  rcvData = new (std::nothrow) UINT8_t[rcvDataSize];
  //@UTIGN new
  if (NULL == rcvData) {
    m_log.write(AXFLOG_ERR, "fail new rcvData " + m_name.getFullActorName());
    if (m_thread != NULL) {
      m_thread->exitSelf();
    }
    //@UTIGN exitSelf
    return NULL;
  }

  // アクタに onCreate イベントを通知する
  onCreate();

  // アクタ・ループ処理
  actorLoop(rcvData, rcvDataSize);

  // 受信データ格納用バッファを破棄する
  delete[] rcvData;

  if (AXFACTOR_STATE_INACTIVE == m_lifeCycleState) {
    goto end;
  }

  if (AXFACTOR_STATE_SUSPEND != m_lifeCycleState) {
    // アクタに onPause イベントを通知する
    onPause();
    transLifeCycleState(AXFACTOR_STATE_SUSPEND);
  }
  // アクタに onStop イベントを通知する
  onStop();
  transLifeCycleState(AXFACTOR_STATE_INACTIVE);

  end:

  // アクタに onDestroy イベントを通知する
  onDestroy();

  if (m_thread != NULL) {
    m_thread->exitSelf();
  }
  //@UTIGN exitSelf
  return NULL;
}

inline bool AXFActor::setupRcvMailbox(int& rcvDataSize) {
  AXFStageActorInfo* actorInfo;

  //@UTIGN (!) too narrow timing to test
  if (0 > AXFStage::getInstance()->getActorInfo(m_name, &actorInfo)) {
    m_log.write(AXFLOG_ERR,
                "fail recieve mailRef " + m_name.getFullActorName());
    if (m_thread != NULL) {
      m_thread->exitSelf();
    }
    //@UTIGN exitSelf
    return false;
  }

  m_mailbox = actorInfo->mailRef;
  rcvDataSize = actorInfo->mailMaxSize;

  return true;
}

inline void AXFActor::actorLoop(UINT8_t* rcvData, int rcvDataSize) {
  while (m_isActive) {

#ifndef	UNUSE_CHRONO	// baba Chrono非対応のためAXFTimeDomainInternalは現状非対応
    if ((true == AXFStage::getInstance()->m_timeDomain->isActive())
        && (0 != m_name.getActorName().compare(axshellActorName))
        && (0 != m_name.getActorName().compare(axdaemonActorName))) {
      m_log.write(AXFLOG_INFO, "wait axsimCond " + m_name.getFullActorName());

      m_isAxSimCondWait = true;
      if (AXFConditionVariableInternal::COND_SUCCESS != m_axsimCond->wait()) {
        m_log.write(AXFLOG_ERR,
                    "fail wait axsimCond " + m_name.getFullActorName());
      }
      m_isAxSimCondWait = false;

    }
#endif	/* UNUSE_CHRONO */
#if 0 // bbtest
		std::string strFullActNm = m_name.getFullActorName();
#endif
    AXFEvent_t eventId;
    int receivedSize;
    receivedSize = m_mailbox->receive(&eventId, rcvData, rcvDataSize);

#ifdef USE_DEBUG_DETAIL_LOG
    std::ostringstream eventIdStr;
    std::ostringstream receivedSizeStr;
    eventIdStr << std::hex << eventId;
    receivedSizeStr << receivedSize;

    m_log.write(
        AXFLOG_DEBUG,
        "AXFActor receive eventId=" + eventIdStr.str() + " receivedSize="
            + receivedSizeStr.str());
#endif /* USE_DEBUG_DETAIL_LOG */

    if (AXFEVENT_SYS_DOG_REQUEST== eventId) {
      AXFWatchdogInternal::ack(m_log, *this);
      continue;
    }

    if (AXFEVENT_SYS_TIMER_SIMU == eventId) {
      if (AXFStage::m_timerTask) {
	AXFStage::m_timerTask->simu(this);
      }
      continue;
    }

    if (AXFEVENT_LIFECYCLE_EXIT== eventId) {
      break;
    }
    if (AXFEVENT_LIFECYCLE_START== eventId) {
      if (AXFACTOR_STATE_INITIALIZE == m_lifeCycleState) {
        onStart();
        onResume();
      } else {
        onRestart();
      }
      transLifeCycleState(AXFACTOR_STATE_ACTIVE);
      continue;
    }
    if (AXFACTOR_STATE_INACTIVE == m_lifeCycleState) {
      continue;
    }
    if (AXFEVENT_LIFECYCLE_STOP== eventId) {
      if (m_lifeCycleState == AXFACTOR_STATE_ACTIVE) {
        onPause();
        transLifeCycleState(AXFACTOR_STATE_SUSPEND);
      }
      onStop();
      transLifeCycleState(AXFACTOR_STATE_INACTIVE);
      continue;
    }
    if ((AXFEVENT_LIFECYCLE_RESUME== eventId) &&
    (AXFACTOR_STATE_SUSPEND == m_lifeCycleState)) {
      onResume();
      transLifeCycleState(AXFACTOR_STATE_ACTIVE);
      continue;
    }
    if (m_lifeCycleState == AXFACTOR_STATE_SUSPEND) {
      continue;
    }
    if (AXFEVENT_LIFECYCLE_PAUSE== eventId) {
      onPause();
      transLifeCycleState(AXFACTOR_STATE_SUSPEND);
      continue;
    }

    m_scriptHandlerStat = doHandler(eventId, rcvData, receivedSize);
#ifndef	UNUSE_CHRONO	// baba Chrono非対応のためAXFTimeDomainInternalは現状非対応
    AXFStage::m_timeDomain->commitState(this);
#endif	/* UNUSE_CHRONO */
    if (NULL != AXFStage::m_axdaemon) {
      AXFStage::m_axdaemon->scriptHandlerSignal();
    }
  }
}
