/*
 * AXFTimerUnitInternal.cpp
 */

#include <stdio.h>
#include "AXFTimeDomainInternal.hpp"
#include "AXFDaemonInternal.hpp"
#include "AX.hpp"
#include "AXFStage.hpp"
#include "AXFTimerTaskInternal.hpp"
#include "AXFTimerUnitInternal.hpp"

#ifdef	UNUSE_CHRONO	// baba Chrono非対応
AXFTimerUnitInternal::AXFTimerUnitInternal(AXFTimerTaskInternal* task, AXFObjectName& name, AXFActor* toActor, AXFEvent_t event)
    : AXFTIMERUNIT_BASE_CLASS((AXCTimerTask*)task),
      m_id(0),
      m_task(task),
      m_isSingle(true),
      m_name(name),
      m_toActor(toActor),
      m_event(event) {
}
#else	/* UNUSE_CHRONO */
AXFTimerUnitInternal::AXFTimerUnitInternal(AXFTimerTaskInternal* task, AXFObjectName& name, AXFActor* toActor, AXFEvent_t event)
    : AXFTIMERUNIT_BASE_CLASS((AXCTimerTask*)task),
      m_id(0),
      m_task(task),
      m_isSingle(true),
      m_name(name),
      m_toActor(toActor),
      m_event(event),
      m_chronoSpec(),
      m_chronoStart(),
      m_chronoUpdate() {
}
#endif	/* UNUSE_CHRONO */

AXFTimerUnitInternal::~AXFTimerUnitInternal() {
}

int AXFTimerUnitInternal::set(CoreTimeSpec* spec, bool isSingle) {
  if (m_task == NULL) {
    return TIMER_UNIT_ERROR_SET_NOTASK;
  }

  m_isSingle = isSingle;

  // 設定時刻を記録
  if (m_toActor == NULL) {
    return TIMER_UNIT_ERROR_SET_NOACT;
  }
	
#ifndef	UNUSE_CHRONO	// baba Chrono非対応
  m_chronoStart = m_chronoUpdate = m_toActor->getCurrentTime();

  // 設定時間を記録
  m_chronoSpec.set(spec->sec, AXFChrono::convertNsecToTick(spec->nsec));
  if (m_chronoSpec == AXFChrono(0, 0)) {
    // 最短 1 tick とする
    m_chronoSpec.set(0, 1);
  }

  CoreTimeSpec corrSpec = *spec;

  if (isTimeDomainActive()) {
    // タイムドメイン有効時

    AXFChrono now;
    now.getSystemTime();

    AXFChrono corr = m_chronoSpec - (now - m_chronoStart);
    if (corr <= AXFChrono(0, 0)) {
      corr.set(0, 1);
    }
    AXFChrono::TimeSpec ts = corr.getCounter();
    corrSpec.sec = ts.sec;
    corrSpec.nsec = AXFChrono::convertTickToNsec(ts.tick);
  }

  int ret;
  if ((ret = AXFTIMERUNIT_BASE_CLASS::set(&corrSpec, isSingle)) != TIMER_UNIT_SUCCESS) {
    return ret;
  }

  // 既にtaskに追加されていた場合も 0 (SUCCESS) が返る
  if ((ret = m_task->add(this)) != AXFTimerTaskInternal::TIMER_TASK_SUCCESS) {
    return TIMER_UNIT_ERROR_SET_TASKADD;
  }
#endif	/* UNUSE_CHRONO */

  return TIMER_UNIT_SUCCESS;
}

int AXFTimerUnitInternal::getExpNum() {

#ifndef	UNUSE_CHRONO	// baba TimeDomain非対応
  // タイムドメイン有効時
  if (isTimeDomainActive()) {

    int num = 0;
    if (m_toActor == NULL) {
      return TIMER_UNIT_ERROR_GETEXPNUM_NOACT;
    }
    AXFChrono now = m_toActor->getCurrentTime();

    if (m_isSingle) {
      // 単発タイマの場合

      AXFChrono exp = m_chronoStart + m_chronoSpec;
      if (m_chronoUpdate < exp && exp <= now) {
	num = 1;
      }
    } else {
      // 周期タイマの場合

      // 途中からTimeDomainが有効になったときの初回
      if (m_chronoUpdate < m_task->m_simuStartChrono) {
	// TimeDomain有効になった時刻までは、処理済として扱う
	m_chronoUpdate = m_task->m_simuStartChrono;
      }

      // 前回の呼び出しから、現在までの間の満了回数
      num = getExpNumChrono(m_chronoUpdate, now);
    }

    // 呼び出し時刻を更新
    m_chronoUpdate = now;

    return num;
  }
#endif	/* UNUSE_CHRONO */

  // タイムドメイン無効時
  return AXFTIMERUNIT_BASE_CLASS::getExpNum();
}

int AXFTimerUnitInternal::getCorrExpNum(int num) {

#ifndef	UNUSE_CHRONO	// baba TimeDomain非対応
  // かつてTimeDomain処理をしたことがある、周期タイマの場合
  if (m_chronoStart < m_chronoUpdate && !m_isSingle) {

    // 現在時刻取得
    AXFChrono now;
    now.getSystemTime();

    if (num > getExpNumChrono(m_chronoUpdate, now)) {
      // この条件を満たす可能性があるのは、TimeDomain無効になってからの初回のみ

      // TimeDomain有効になった時刻と、タイマ設定時刻と比較し、より新しい方を from に設定
      AXFChrono from = m_chronoStart < m_task->m_simuStartChrono ? m_task->m_simuStartChrono : m_chronoStart;

      // from から 最後のTimeDomain処理時刻までの満了回数
      int overNum = getExpNumChrono(from, m_chronoUpdate);

      // 超過分を補正
      num -= overNum;

      if (num < 0) {
	//@UTIGN always num >= 0
	// 念のために判定しているが、基本的に条件は成立しない
	num = 0;
      }
    }
  }
#endif	/* UNUSE_CHRONO */

  return num;
}

int AXFTimerUnitInternal::delFromTask() {
  if (m_task == NULL) {
    return TIMER_UNIT_ERROR_DELFROMTASK_NOTASK;
  }

  int ret;

  // task側で見つからなかった場合も 0 (SUCCESS) が返る
  if ((ret = m_task->del(this)) != AXFTimerTaskInternal::TIMER_TASK_SUCCESS) {
    return TIMER_UNIT_ERROR_DELFROMTASK_TASKDEL;
  }

  return TIMER_UNIT_SUCCESS;
}

int AXFTimerUnitInternal::sendEvent() {
  if (m_toActor == NULL) {
    return TIMER_UNIT_ERROR_SENDEVENT_NOTOACTOR;
  }

  // m_toActor へ m_event を送信する
  if (m_toActor->send(m_toActor->getNameObject(), m_event, NULL, 0) < 0) {
    return TIMER_UNIT_ERROR_SENDEVENT_SEND;
  }
  return TIMER_UNIT_SUCCESS;
}

bool AXFTimerUnitInternal::isSingle() {
  return m_isSingle;
}

AXFObjectName& AXFTimerUnitInternal::getName() {
  return m_name;
}

void AXFTimerUnitInternal::setId(int id) {
  m_id = id;
}

int AXFTimerUnitInternal::getId() {
  return m_id;
}

AXFActor* AXFTimerUnitInternal::getToActor() {
  return m_toActor;
}

bool AXFTimerUnitInternal::isNeverSimu() {

  // EVENT_INTERVAL_TIMER を Daemonに通知する場合は、シミュレーションしない

  if (m_toActor == NULL) {
    return false;
  }
  return m_toActor->getNameObject().getActorName().compare(axdaemonActorName) == 0
    && m_event == AXFDaemonInternal::EVENT_INTERVAL_TIMER;
}

bool AXFTimerUnitInternal::isTimeDomainActive() {
#ifdef	UNUSE_CHRONO	// baba TimeDomain非対応
	return false;
#else
  return AXFTimerTaskInternal::isTimeDomainActive() && !isNeverSimu();
#endif	/* UNUSE_CHRONO */
}


#ifndef	UNUSE_CHRONO	// baba TimeDomain非対応
// 時刻 m_chronoStart のときに、
// m_chronoSpec の間隔で設定された周期タイマが、
// 時刻 now までに満了した回数を返す

int AXFTimerUnitInternal::getExpNumChrono(AXFChrono now) {
  int num = 0;
  AXFChrono exp = m_chronoStart + m_chronoSpec;
  for ( ; exp <= now ; exp = exp + m_chronoSpec) {
    num++;
  }
  return num;
}


// 時刻 m_chronoStart のときに、
// m_chronoSpec の間隔で設定された周期タイマが、
// 時刻 from から 時刻 to までに満了した回数を返す

int AXFTimerUnitInternal::getExpNumChrono(AXFChrono from, AXFChrono to) {
  return getExpNumChrono(to) - getExpNumChrono(from);
}
#endif	/* UNUSE_CHRONO */
