/*
 * AXFTimeDomainInternal.cpp
 */

#include "AXFTimeDomainInternal.hpp"
#ifndef	UNUSE_CHRONO	// baba Chrono非対応のためAXFTimeDomainInternalは現状非対応
#include "AXFDaemonInternal.hpp"
#include "AXFLog.hpp"
#include "AXFStage.hpp"
#include "AX.hpp"
#include "AXFStdioInternal.hpp"

#include <sstream>
#include <string>
#include <vector>
#include <iostream>
#include <iomanip>

AXFTimeDomainInternal::AXFTimeDomainInternal()
    : m_currentTick(),
      m_isBreakLifeCycle(false),
      m_isBreakStateMachine(false),
      m_isActive(false) {
}

AXFTimeDomainInternal::~AXFTimeDomainInternal() {
}

bool AXFTimeDomainInternal::isActive() {
  return m_isActive;
}

void AXFTimeDomainInternal::enable() {
  if (AXFStage::getInstance()->m_axdaemon) {
    m_currentTick = AXFStage::getInstance()->m_axdaemon->getCurrentTime();
    registBreakDate(m_braekDateList, BreakDateInfo(m_currentTick));
  }
  m_isActive = true;
}

void AXFTimeDomainInternal::disable() {
  m_isActive = false;
}

bool AXFTimeDomainInternal::addBreakTick(INT64_t count) {
  if (false == m_isActive) {
    return false;
  }

  AXFChrono breakTick = m_currentTick;
  breakTick.upCounter(count);
  registBreakDate(m_braekDateList, BreakDateInfo(breakTick));

  resetBreakState();

  return true;
}

bool AXFTimeDomainInternal::setBreakDate(AXFChrono::TimeSpec& date) {
  if (false == m_isActive) {
    return false;
  }

  registBreakDate(m_braekDateList, BreakDateInfo(AXFChrono(date)));

  return true;
}

void AXFTimeDomainInternal::setBreakLifeCycleState(char* actorName, AXFActor::actorState state, AXFLog& log) {
  if (false == m_isActive) {
    return;
  }

  // TimeDomain では、break対象アクタを アクタIDで管理するので
  // アクタ名からアクタIDに変換する
  AXFObjectName name(actorName);
  AXFActor* dummy;
  INT32_t actorId = AXFStage::getInstance()->getActorInfo(name, &dummy);
  if (actorId < 0) {
    log.write(AXFLOG_ERR, "setBreakLifeCycleState: illegal ActorName");
    return;
  }

  registBreakState(m_braekLifeCycleList, BreakStateInfo(state, actorId));

  return;
}

void AXFTimeDomainInternal::setBreakStateMachineState(char* actorName, INT32_t state, AXFLog& log) {
  if (false == m_isActive) {
    return;
  }

  // TimeDomain では、break対象アクタを アクタIDで管理するので
  // アクタ名からアクタIDに変換する
  AXFObjectName name(actorName);
  AXFActor* dummy;
  INT32_t actorId = AXFStage::getInstance()->getActorInfo(name, &dummy);
  if (actorId < 0) {
    log.write(AXFLOG_ERR, "setBreakStateMachineState: illegal ActorName");
    return;
  }
  registBreakState(m_breakStateMachineList, BreakStateInfo(state, actorId));

  return;
}

void AXFTimeDomainInternal::delBreak(INT32_t breakId, AXFLog& log) {
  if (false == m_isActive) {
    return;
  }

  if (breakId <= 0) {
    //@ToDo 一括削除など
    return;
  }

  --breakId;  // 0 origin に調整する
  if (true == unregistBreakDate(m_braekDateList, breakId)) {
    return;
  }

  breakId -= m_braekDateList.size();  // 0 origin に調整する
  if (true == unregistBreakState(m_braekLifeCycleList, breakId)) {
    return;
  }

  breakId -= m_braekLifeCycleList.size();  // 0 origin に調整する
  unregistBreakState(m_breakStateMachineList, breakId);

  return;
}

void AXFTimeDomainInternal::commitState(AXFActor* actorObj) {
  if (false == m_isActive) {
    return;
  }

  BreakStateInfo lifeCycleInfo(actorObj->getLifeCycleState(), actorObj->getActorId());
  std::set<BreakStateInfo>::iterator lp = m_braekLifeCycleList.find(lifeCycleInfo);
  if (lp != m_braekLifeCycleList.end()) {
    m_isBreakLifeCycle = true;
  }



  BreakStateInfo stmInfo(actorObj->getStateMachineState(), actorObj->getActorId());
  std::set<BreakStateInfo>::iterator sp = m_breakStateMachineList.find(stmInfo);
  if (sp != m_breakStateMachineList.end()) {
    m_isBreakStateMachine = true;
  }

  return;
}

bool AXFTimeDomainInternal::funcTokenRing(AXFLog& log) {

  if (m_isBreakLifeCycle ||      // break LifeCycle条件一致 (各アクタのイベント・メソッド完了時に更新される)
      m_isBreakStateMachine) {   // break StateMachine条件一致 (各アクタのイベント・メソッド完了時に更新される)
    return true;
  }

  BreakDateInfo breakTick(m_currentTick);
  std::set<BreakDateInfo>::iterator it;
  it = m_braekDateList.lower_bound(breakTick);
  if ((it != m_braekDateList.end()) && (*it == breakTick)) {
    return true;
  }

#if 1 // for debug kon
  // @@@ToDo: Tickステップ処理を調整する
  // 次のBreak時刻までの間隔が長い場合は、Tickステップ数を大きくする
  INT64_t step = 1000;
  if (it != m_braekDateList.end()) {
    INT64_t rest = (it->m_breakTick - m_currentTick).getTick();
    if (step >= rest) {
      step = 1;
    }
  }
#endif

#ifdef NEVER
  log.write(
      AXFLOG_DEBUG,
      "@@@ funcTokenRing(): m_breakTick=" + (const_cast<AXFChrono*>(&it->m_breakTick))->getString()
          + " m_currentTick=" + m_currentTick.getString());
#endif /* NEVER */

  std::vector<AXFStageActorInfo>* actorInfoList;
  AXFStage::getInstance()->getActorInfo(&actorInfoList);

  for (std::vector<AXFStageActorInfo>::iterator it = actorInfoList->begin();
      it != actorInfoList->end(); it++) {
    if (NULL == it->objRef) {
      log.write(AXFLOG_DEBUG, "@@@ it->objRef is NULL");
      continue;
    }

    if ((0
        == it->objRef->getNameObject().getActorName().compare(axshellActorName))
        || (0
            == it->objRef->getNameObject().getActorName().compare(
                axdaemonActorName))) {
      it->objRef->incrementCurrentTime();
      continue;
    }

    if (0 < it->objRef->npeekMailbox()) {
      log.write(
          AXFLOG_DEBUG,
          "Director signal() actor="
              + it->objRef->getNameObject().getActorName());

#if 1 // for debug kon
      it->objRef->incrementCurrentTime(step);
#else
      it->objRef->incrementCurrentTime();
#endif
      it->objRef->signalAxSimCond();
    }
  }

#if 1 // for debug kon
  m_currentTick.upCounter(step);
#else
  m_currentTick++;
#endif

  return true;
}

bool AXFTimeDomainInternal::funcAllWakeup(AXFLog& log) {

  std::vector<AXFStageActorInfo>* actorInfoList;
  AXFStage::getInstance()->getActorInfo(&actorInfoList);

  for (std::vector<AXFStageActorInfo>::iterator it = actorInfoList->begin();
      it != actorInfoList->end(); it++) {
    if (NULL == it->objRef) {
      log.write(AXFLOG_DEBUG, "@@@ it->objRef is NULL");
      continue;
    }

    if (0 < it->objRef->isAxSimWait()) {
      log.write(
          AXFLOG_DEBUG,
          "Director signal() actor="
              + it->objRef->getNameObject().getActorName());

      it->objRef->signalAxSimCond();
    }
  }

  return true;
}

bool AXFTimeDomainInternal::funcForceBreak(AXFLog& log) {
  registBreakDate(m_braekDateList, BreakDateInfo(m_currentTick));
  return true;
}

void AXFTimeDomainInternal::registBreakDate(std::set<BreakDateInfo>& list, BreakDateInfo info) {
  std::set<BreakDateInfo>::iterator itReg = list.lower_bound(info);
  if ((itReg != list.end()) && (*itReg == info)) {
    // 登録済み
    //@ToDo break 有効・無効設定対応に、無効であれば有効に変更する
    return;
  }
  list.insert(itReg, info);
}

bool AXFTimeDomainInternal::unregistBreakDate(std::set<BreakDateInfo>& list, INT32_t delIdx) {
  std::set<BreakDateInfo>::iterator it = list.begin();
  for (int i = 0; it != list.end(); i++, it++) {
    if (delIdx == i) {
      list.erase(it);
      return true;
    }
  }

  return false;
}

void AXFTimeDomainInternal::registBreakState(std::set<BreakStateInfo>& list, BreakStateInfo info) {
  std::set<BreakStateInfo>::iterator it = list.lower_bound(info);
  if ((it != list.end()) && (*it == info)) {
    // 登録済み
    //@ToDo break 有効・無効設定対応に、無効であれば有効に変更する
    return;
  }
  list.insert(it, info);
}

bool AXFTimeDomainInternal::unregistBreakState(std::set<BreakStateInfo>& list, INT32_t delIdx) {
  std::set<BreakStateInfo>::iterator it = list.begin();
  for (int i = 0; it != list.end(); i++, it++) {
    if (delIdx == i) {
      list.erase(it);
      return true;
    }
  }

  return false;
}

void AXFTimeDomainInternal::resetBreakState() {
  m_isBreakLifeCycle = false;
  m_isBreakStateMachine = false;
}

void AXFTimeDomainInternal::dumpBreakInfo(AXFLog& log) {
/*
@ToDo: 以下は暫定出力。
　　　　最終的には、以下のような出力を行う予定。
(ex)
Num Type          name         break                     Enb
1   Time          ---          2014/08/29/12:10:50.1234  y
2   LifeCycle     .actor.stage SUSPEND                   y
3   StateMachine  .actor.stage 0                         y
*/

  AXFStdioInternal stdio;
  std::cout << axstdio::endl
            << "Current Count = " << m_currentTick.getString() << axstdio::endl
            << "m_isActive = " << m_isActive << axstdio::endl;
  stdio.flush();

  std::cout << axstdio::endl;
  stdio.flush();
  std::cout << std::resetiosflags(std::ios::right) << std::setiosflags(std::ios::left)
            << std::setw(4) << std::setfill(' ') << "Num"
            << std::setw(14) << std::setfill(' ') << "Type"
            << std::setw(24) << std::setfill(' ') << "name"
            << std::setw(26) << std::setfill(' ') << "break"
#ifdef NEVER
            << std::setw(3) << std::setfill(' ') << "Enb"
#endif /* NEVER */
            << axstdio::endl;

  dumpBreakDateInfo(log);
  dumpBreakLifeCycleInfo(log);
  dumpBreakStateMachineInfo(log);
}

void AXFTimeDomainInternal::dumpBreakDateInfo(AXFLog& log) {
  if (m_braekDateList.size() <= 0) {
    return;
  }

  AXFStdioInternal stdio;
  std::vector<AXFStageActorInfo>* actorInfoList;
  AXFStage::getInstance()->getActorInfo(&actorInfoList);
  std::set<BreakDateInfo>::iterator it = m_braekDateList.begin();
  int no = 1;
  while (it != m_braekDateList.end()) {
    std::cout << std::resetiosflags(std::ios::right) << std::setiosflags(std::ios::left)
              << std::setw(4) << std::setfill(' ') << no
              << std::setw(14) << std::setfill(' ') << "Time"
              << std::setw(24) << std::setfill(' ')
              << "---"
              << std::setw(26) << std::setfill(' ') << (const_cast<AXFChrono*>(&it->m_breakTick))->getString()
#ifdef NEVER
              << std::setw(3) << std::setfill(' ') << "y"
#endif /* NEVER */
              << axstdio::endl;
    stdio.flush();
    it++;
    no++;
  };
}

void AXFTimeDomainInternal::dumpBreakLifeCycleInfo(AXFLog& log) {
  if (m_braekLifeCycleList.size() <= 0) {
    return;
  }

  const char* LifeCycleStr[] = {"INIT", "ACTIVE", "SUSPEND", "INACTIVE"};

  AXFStdioInternal stdio;
  std::vector<AXFStageActorInfo>* actorInfoList;
  AXFStage::getInstance()->getActorInfo(&actorInfoList);
  std::set<BreakStateInfo>::iterator it = m_braekLifeCycleList.begin();
  int no = m_braekDateList.size() + 1;
  while (it != m_braekLifeCycleList.end()) {
    std::cout << std::resetiosflags(std::ios::right) << std::setiosflags(std::ios::left)
              << std::setw(4) << std::setfill(' ') << no
              << std::setw(14) << std::setfill(' ') << "LifeCycle"
              << std::setw(24) << std::setfill(' ')
              << (*actorInfoList)[it->m_actorId].objRef->getNameObject().getFullActorName()
              << std::setw(26) << std::setfill(' ') << LifeCycleStr[it->m_state]
#ifdef NEVER
              << std::setw(3) << std::setfill(' ') << "y"
#endif /* NEVER */
              << axstdio::endl;
    stdio.flush();
    it++;
    no++;
  };
}

void AXFTimeDomainInternal::dumpBreakStateMachineInfo(AXFLog& log) {

  if (m_breakStateMachineList.size() <= 0) {
    return;
  }

  AXFStdioInternal stdio;
  std::vector<AXFStageActorInfo>* actorInfoList;
  AXFStage::getInstance()->getActorInfo(&actorInfoList);
  std::set<BreakStateInfo>::iterator it = m_breakStateMachineList.begin();
  int no = m_braekDateList.size() + m_braekLifeCycleList.size() + 1;
  while (it != m_breakStateMachineList.end()) {
    std::cout << std::resetiosflags(std::ios::right) << std::setiosflags(std::ios::left)
              << std::setw(4) << std::setfill(' ') << no
              << std::setw(14) << std::setfill(' ') << "StateMachine"
              << std::setw(24) << std::setfill(' ')
              << (*actorInfoList)[it->m_actorId].objRef->getNameObject().getFullActorName()
              << std::setw(26) << std::setfill(' ') << it->m_state
#ifdef NEVER
              << std::setw(3) << std::setfill(' ') << "y"
#endif /* NEVER */
              << axstdio::endl;
    stdio.flush();
    it++;
    no++;
  };
}

AXFTimeDomainInternal::BreakDateInfo::BreakDateInfo(AXFChrono date)
    : m_breakTick(date),
      m_isEnable(true) {
}

AXFTimeDomainInternal::BreakDateInfo::~BreakDateInfo() {
}

bool AXFTimeDomainInternal::BreakDateInfo::operator <(const AXFTimeDomainInternal::BreakDateInfo& info) const {
  return (m_breakTick < info.m_breakTick);
}

bool AXFTimeDomainInternal::BreakDateInfo::operator ==(const AXFTimeDomainInternal::BreakDateInfo& info) const {
  return (m_breakTick == info.m_breakTick);
}

AXFTimeDomainInternal::BreakStateInfo::BreakStateInfo(INT32_t state, INT32_t actorId)
    : m_state(state),
      m_actorId(actorId),
      m_isEnable(true) {
}

AXFTimeDomainInternal::BreakStateInfo::~BreakStateInfo() {
}

bool AXFTimeDomainInternal::BreakStateInfo::operator <(const AXFTimeDomainInternal::BreakStateInfo& info) const {
  return (m_actorId != info.m_actorId) ? (m_actorId < info.m_actorId) : (m_state < info.m_state);
}

bool AXFTimeDomainInternal::BreakStateInfo::operator ==(const AXFTimeDomainInternal::BreakStateInfo& info) const {
  return ((m_state == info.m_state) && (m_actorId == info.m_actorId));
}
#endif	/* UNUSE_CHRONO */
