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

#ifndef AXFCHRONO_HPP_
#define AXFCHRONO_HPP_

#include <sstream>

#include "AXCStdint.hpp"
#include "AXCChronoFreeRTOS.hpp"

/**
 * @class AXFChrono
 * @brief AX独自の時間形式を操作する機能を提供する
 *
 * @n     本クラスでは2つのカウンタでAX独自の時間を表す
 * @n     秒カウンタ：協定世界時(UTC)での1985年1月1日0時0分0秒を起源とした経過秒数をカウントする
 * @n     tick    ：0起源で 1/60000秒 精度でカウントし、60000tickで秒カウンタを1カウントアップする
 * @n
 * @n     AXM層は、AXFActorクラスのgetCurrentTime()で取得した時間オブジェクト(AXFChrono)から、
 * @n     指定したカウンタ(秒、またはtick)や文字列を取得することができる
 * @n
 * @n     AXFChronoクラスの 詳細は AXCChronoクラス の説明に記述する
 */
class AXFChrono :
	public AXCChronoFreeRTOS
{
 public:
#ifdef	UNUSE_CHRONO	// baba Chronoは時間・Tick変換関数のみ使用
  /**
   * @brief AXFChronoコンストラクタ
   */
  AXFChrono();

  /**
   * @brief AXFChronoデストラクタ
   */
  virtual ~AXFChrono();
 
  /**
   * @brief  ナノ秒(0〜999999999)をtickに変換し、値を返却する
   *
   * - 引数に0未満の値が指定されている場合、変換失敗とする
   * - 999999999 を超える値を指定されている場合、変換失敗とする
   * - 変換時の誤差を少なくするため、小数点第一位で四捨五入する
	 * - 精度はTICKの周波数により変化する
   * @n
   * @param  [in] nsec  ナノ秒
   * @return tick（エラー時は -1）
   */
  static ax::INT32_t convertNsecToTick(ax::INT32_t nsec);

  /**
   * @brief  tickをナノ秒に変換し値を返却する
   *
   * - 引数に0未満の値が指定されている場合、変換失敗とする
   * - TICKの周波数を超える値を指定されている場合、変換失敗とする
	 * - 精度はTICKの周波数により変化する
   * @n
   * @param  [in] tick
   * @return ナノ秒（エラー時は -1）
   */
  static ax::INT32_t convertTickToNsec(ax::INT32_t tick);
#else
  // @Todo エラーの全体方針により修正必要
  /**
   * @enum  ChronoStatus
   * @brief 処理結果
   */
  enum ChronoStatus {
    CHRONO_SUCCESS = 0,  ///< 正常系：処理成功
    CHRONO_ERROR,        ///< 異常系：処理失敗
  };

  /**
   * @struct TimeSpec
   * @brief 時間構造体(秒、tick)
   */
  struct TimeSpec {
    INT64_t sec;   ///< 秒
    INT32_t tick;  ///< tick
  };

  /**
   * @brief AXFChronoコンストラクタ
   */
  AXFChrono();

  /**
   * @brief AXFChronoコンストラクタ
   * @param [in] sec   秒
   * @param [in] tick  tickカウンタ値
   */
  AXFChrono(INT64_t sec, INT32_t tick);

  /**
   * @brief AXFChronoコンストラクタ
   * @param [in] time  秒、tickカウンタ値
   */
  AXFChrono(TimeSpec time);

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

  // @Todo Chronoとは機能が異なるため、別クラスに分けることを検討する
  /**
   * @brief  指定年月日時分秒から1985年1月1日0時0分0秒からの通算秒を設定する
   *
   * @n      [注意]当関数の引数について、以下に当てはまらないものはフォーマットエラーとする
   * @n      ・年月日時分秒tickが半角記号で区切られていること(※区切り文字の指定は無いが必ず必要)
   * @n        (例) 2014/01/02/03:04:05.06789  2014-01-02/03:04:05.06789  2014/01/02/03/04/05/06789
   * @n      ・年は4桁(西暦)で、1985年以降を指定すること。
   * @n      ・月は2桁で、01〜12までを指定すること。
   * @n      ・日は2桁で、01〜31までを指定すること。
   * @n      ・時は2桁で、00〜23までを指定すること。
   * @n      ・分は2桁で、00〜59までを指定すること。
   * @n      ・秒は2桁で、00〜59までを指定すること。
   * @n      ・tickは5桁で、00000〜59999までを指定すること
   * @n
   * @param  [in] date
   * @return 処理結果
   */
  ChronoStatus set(std::string& date);

  // @Todo Chronoとは機能が異なるため、別クラスに分けることを検討する
  /**
   * @brief 1985年1月1日0時0分0秒からの通算秒をシステムに設定する
   *
   * @return 処理結果
   */
  ChronoStatus setSystemTime();

  // @Todo Chronoとは機能が異なるため、別クラスに分けることを検討する
  /**
   * @brief システムから時刻(1985年1月1日0時0分0秒からの経過秒)を取得する
   *
   * @return 処理結果
   */
  ChronoStatus getSystemTime();

  /**
   * @brief システムから取得した時間で、内部で管理しているカウンタを設定する
   * @n
   * @n     getSystemTime() と同じ。ただしエラーを返さない。
   */
  void setCounter();

  /**
   * @brief 指定された時間で、内部で管理しているカウンタを設定する
   *
   * @n     引数の構造体のメンバtickに範囲外の値が指定されている場合、補正される
   * @n
   * @param [in] time  設定する時間(秒、tick)
   */
  void setCounter(TimeSpec time);

  /**
   * @brief 指定された時間で、内部で管理しているカウンタを設定する
   *
   * @n     setCounter(TimeSpec time) と同じ。
   */
  void set(TimeSpec time);

  /**
   * @brief 指定された時間で、内部で管理しているカウンタを設定する
   *
   * @n     引数tickに範囲外の値が指定されている場合、補正される
   * @n
   * @param [in] sec   秒
   * @param [in] tick  tickカウンタ値
   */
  void set(INT64_t sec, INT32_t tick);

  /**
   * @brief 指定された時間で、内部で管理しているカウンタを設定する
   *
   * @n     引数tickに範囲外の値が指定されている場合、補正される
   * @n
   * @param [in] tick  tickカウンタ値 (64ビット整数)
   */
  void set(INT64_t tick);

  /**
   * @brief 指定されたtick分、内部で管理しているカウンタをカウントアップする
   *
   * @n     カウントアップした結果カウンタが飽和したときは、最大値が設定される
   * @n     引数に負の値を指定してカウンタが飽和したときは、最小値が設定される
   *
   * @param  [in] tick  加算するtick
   */
  void upCounter(ax::INT64_t tick);

  /**
   * @brief 指定されたtick分、内部で管理しているカウンタをカウントダウンする
   *
   * @n     カウントダウンした結果カウンタが飽和したときは、最小値が設定される
   * @n     引数に負の値を指定してカウンタが飽和したときは、最大値が設定される
   * @n
   * @param  [in] tick  減算するtick
   */
  void downCounter(ax::INT64_t tick);

  /**
   * @brief  内部で管理しているカウンタを返却する (引数のポインタの先に返却)
   *
   * @param [out] ret_sec   秒
   * @param [out] ret_tick  tickカウンタ値
   */
  void get(INT64_t* ret_sec, INT32_t* ret_tick);

  /**
   * @brief  内部で管理しているカウンタを返却する
   *
   * @return カウンタ値(秒、tick)
   */
  TimeSpec getCounter();

  /**
   * @brief  内部で管理しているカウンタを tick 単位で返却する
   *
   * @return カウンタ値(秒 * TICK_PER_SEC + tick)
   */
  INT64_t getTick();

  /**
   * @brief  内部で管理しているカウンタより日時文字列を返却する
   *
   * @return 日時文字列 (yyyy/MM/dd hh:mm:ss.aaaaaa)
   */
  std::string getString();

  /**
   * @brief  指定された引数(AXFChronoオブジェクト)と 「==」 か判定を行う
   *
   * @param [in] AXFChrono 比較する時間
   * @return 判定結果
   */
  bool operator ==(const AXFChrono& chrono) const;

  /**
   * @brief  指定された引数(AXFChronoオブジェクト)と 「!=」 か判定を行う
   *
   * @param [in] AXFChrono 比較する時間
   * @return 判定結果
   */
  bool operator !=(const AXFChrono& chrono) const;

  /**
   * @brief  指定された引数(AXFChronoオブジェクト)と 「<」 か判定を行う
   *
   * @param [in] AXFChrono 比較する時間
   * @return 判定結果
   */
  bool operator <(const AXFChrono& chrono) const;

  /**
   * @brief  指定された引数(AXFChronoオブジェクト)と 「<=」 か判定を行う
   *
   * @param [in] AXFChrono 比較する時間
   * @return 判定結果
   */
  bool operator <=(const AXFChrono& chrono) const;

  /**
   * @brief  指定された引数(AXFChronoオブジェクト)と 「>」 か判定を行う
   *
   * @param [in] AXFChrono 比較する時間
   * @return 判定結果
   */
  bool operator >(const AXFChrono& chrono) const;

  /**
   * @brief  指定された引数(AXFChronoオブジェクト)と 「>=」 か判定を行う
   *
   * @param [in] AXFChrono 比較する時間
   * @return 判定結果
   */
  bool operator >=(const AXFChrono& chrono) const;

  /**
   * @brief  指定された引数(AXFChronoオブジェクト)と引き算を行う
   *
   * @param [in] &AXFChrono 引く時間
   * @return AXFChrono 差分の時間
   */
  AXFChrono operator -(const AXFChrono& chrono) const;

  /**
   * @brief  指定された引数(AXFChronoオブジェクト)と足し算を行う
   *
   * @param [in] &AXFChrono 足す時間
   * @return AXFChrono 合計の時間
   */
  AXFChrono operator +(const AXFChrono& chrono) const;

  /**
   * @brief  ナノ秒(0〜999999999)をtick(0〜59999)に変換し、値を返却する
   *
   * - 引数に0未満の値が指定されている場合、変換失敗とする
   * - 999999999 を超える値を指定されている場合、変換失敗とする
   * - 変換時の誤差を少なくするため、小数点第一位で四捨五入する
   * - 1 tick は 16666ナノ秒となるため、999983333 を超える値を指定し、
   *    四捨五入して 60000 tick(1 sec)になる場合のみ、59999 tick へ変換する
   * @n
   * @param  [in] nsec  ナノ秒
   * @return tick（エラー時は -1）
   */
  static ax::INT32_t convertNsecToTick(ax::INT32_t nsec);

  /**
   * @brief  tick(0〜59999)をナノ秒(0〜999999999)に変換し値を返却する
   *
   * - 引数に0未満の値が指定されている場合、変換失敗とする
   * - 59999 を超える値を指定されている場合、変換失敗とする
   * - 1 tick は 16666ナノ秒として変換を行い、四捨五入しない
   * - 1 tick は 16666ナノ秒として変換を行うため、実際の変換最大値は(99998333ナノ秒)となる
   * @n
   * @param  [in] tick
   * @return ナノ秒（エラー時は -1）
   */
  static ax::INT32_t convertTickToNsec(ax::INT32_t tick);
	
 private:

  INT64_t m_sec;   ///< 秒
  INT32_t m_tick;  ///< tick

  /**
   * @brief  秒カウンタから年、月、日を算出する
   *
   * @n      秒カウンタが0の場合、年、月、日は初期値(年=1985、月=1、日=1)とする
   * @n
   * @param  [out] ret_year  年
   * @param  [out] ret_mon   月
   * @param  [out] ret_day   日
   */
  void calcDate(ax::INT32_t* ret_year, ax::INT32_t* ret_mon, ax::INT32_t* ret_day);

  /**
   * @brief  秒カウンタから時、分、秒を算出する
   *
   * @n      秒カウンタが0の場合、時、分、秒は初期値(全て0)とする
   * @n
   * @param  [out] ret_hour  時
   * @param  [out] ret_min   分
   * @param  [out] ret_sec   秒
   */
  void calcTime(ax::INT32_t* ret_hour, ax::INT32_t* ret_min, ax::INT32_t* ret_sec);

  /**
   * @brief  指定年月から最大日を返却する。
   *
   * @n      指定された年月を元に閏年などを考慮した月の最大日数(最小28、最大31)を返却する
   * @n
   * @param  [in] year  年
   * @param  [in] month 月
   * @return 最大日 (2月:28 ※閏年 29、4,9,11月:30、それ以外の月:31)
   */
  ax::INT32_t getMaxDayOfMonth(ax::INT32_t year, ax::INT32_t month);

  /**
   * @brief  日付文字列を指定された文字数分整数（数値）に変換し、値を返却する
   *
   * @param  [in] date 日時文字列
   * @return 変換した値 （変換失敗の場合は -1）
   */
  ax::INT32_t convertStringToInt(std::string date);

  /**
   * @brief  指定された文字列が0以上の整数（数値）かどうか判定する
   *
   * @param  [in] date 日時文字列
   * @return 判定結果 （0〜9の数値の組み合わせならtrue）
   */
  bool isInteger(std::string& date);
#endif	/* UNUSE_CHRONO */
};
#endif /* AXFCHRONO_HPP_ */
