/*
 * AXCTimerUnitFreeRTOS.cpp
 */

#include "AXCStdioFreeRTOS.hpp"
#include "AXCTimerTask.hpp"
#include "AXCTimerTaskFreeRTOS.hpp" // for isInWaitList()
#include "AXCTimerUnitFreeRTOS.hpp"

AXCTimerUnitFreeRTOS::AXCTimerUnitFreeRTOS(AXCTimerTask* task)
    : AXCTimerUnit(task),
      m_task(task),
      m_cycphs(0),
      m_cyctim(0),
      m_expNum(0) {
}

AXCTimerUnitFreeRTOS::~AXCTimerUnitFreeRTOS() {
  if (cancel() != TIMER_UNIT_SUCCESS) {
    //@ToDo: エラーを表示する
  }
}

int AXCTimerUnitFreeRTOS::init() {
  return TIMER_UNIT_SUCCESS;
}

int AXCTimerUnitFreeRTOS::set(CoreTimeSpec* spec, bool isSingle) {
  m_isSingle = isSingle;
	
  // 実行開始までの時間
  m_cycphs = (spec->sec * 1000) + (spec->nsec / 1000000);
  if (m_cycphs == 0) {
    m_cycphs = 1; /* 0は設定不可なので1を設定する */
  }
	
  // 実行間隔
  if (isSingle) {
    m_cyctim = 1;  /* 0は設定不可なので1を設定し、ハンドラ内で停止する */
  } else {
    m_cyctim = m_cycphs;
  }
	
  // タイマーの生成＋開始
	m_cycId = xTimerCreate("", m_cyctim, pdTRUE, (void*)0, AXCTimerUnitFreeRTOS::expireFunc);
	if (m_cycId == NULL) {
    return TIMER_UNIT_ERROR_SET_CRECYC;
	}

  return TIMER_UNIT_SUCCESS;
}

int AXCTimerUnitFreeRTOS::get(CoreTimeSpec* ret_spec) {
  if (m_cycId == NULL) {
    return TIMER_UNIT_ERROR_GET_ID;
  }

  // 残り時間の取得
	TickType_t lfttim = xTimerGetExpiryTime(m_cycId);
	
  // 設定時間 - 残時間 = 経過時間（単位ミリ秒）
	TickType_t pastTime = 0;
	if (m_isSingle) {
		pastTime = m_cycphs - lfttim;
	} else {
		pastTime = m_cyctim - lfttim;
	}
	
  // ミリ秒→秒以上部分を設定
  ret_spec->sec = pastTime / 1000;

  // ミリ秒→秒未満部分を設定
  ret_spec->nsec = (pastTime % 1000) * 1000000;

  return TIMER_UNIT_SUCCESS;
}

int AXCTimerUnitFreeRTOS::cancel() {
  int ret;
  if ((ret = stop()) != TIMER_UNIT_SUCCESS) {
    return ret;
  }

  // タイマーの削除
	BaseType_t err = xTimerDelete(m_cycId, portMAX_DELAY);
	
	m_cycId = NULL;
	m_cycphs = 0;
	m_cyctim = 0;
	m_expNum = 0;
	
	if (err != pdPASS) {
    return TIMER_UNIT_ERROR_CANCEL_DELCYC;
	}
	
  return TIMER_UNIT_SUCCESS;
}

int AXCTimerUnitFreeRTOS::getExpNum() {

  // 満了回数取得で保持してる値はクリアする
  int v = m_expNum;
  m_expNum = 0;

  if (m_isSingle && v > 1) {
    v = 1;
  }
  return v;
}

bool AXCTimerUnitFreeRTOS::isExpired() {
  return m_expNum > 0;
}

int AXCTimerUnitFreeRTOS::stop() {
  if (m_cycId == NULL) {
    return TIMER_UNIT_ERROR_STOP_ID;
  }
	
	BaseType_t err = xTimerStop(m_cycId, portMAX_DELAY);
	if (err != pdPASS) {
    return TIMER_UNIT_ERROR_STOP_STPCYC;
	}
	
  return TIMER_UNIT_SUCCESS;
}

void AXCTimerUnitFreeRTOS::expireFunc(void* exinf) {

  // タイマ満了時に呼び出されるハンドラ

  AXCTimerUnitFreeRTOS* obj = reinterpret_cast<AXCTimerUnitFreeRTOS*>(exinf);
  obj->expire();
}

void AXCTimerUnitFreeRTOS::expire() {

  // タイマ満了時に呼び出されるハンドラから、
  // 本メソッドが呼び出される

  m_expNum++;

  int ret;
  if (m_isSingle) {
    if ((ret = stop()) != TIMER_UNIT_SUCCESS) {
      //@ToDo: エラーを表示する
    }
  }

  if (m_task == NULL) {
    //@ToDo: エラーを表示する
    return;
  }

  if (!((AXCTimerTaskFreeRTOS*)m_task)->isInWaitList(this)) {
    // TimerTaskTKernelの待機リストに登録されてなければ
    // wakeup() は呼び出さない
    // TimeDomain有効時は、Daemonの特定のタイマのみ待機リストに登録される

    return;
  }

  if ((ret = m_task->wakeup(AXCTimerTask::TIMERTASK_WAKEUP_EXPIRE)) != AXCTimerTask::TIMER_TASK_SUCCESS) {
    //@ToDo: エラーを表示する
  }
}
