/*
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

#pragma once

#include "ACubismMotion.hpp"
#include "Model/CubismModel.hpp"
#include "Type/csmVector.hpp"

namespace Live2D { namespace Cubism { namespace Framework {

class CubismMotionQueueEntry;
class CubismMotionQueueManager;

/**
* @brief イベントのコールバック関数定義
*
* イベントのコールバックに登録できる関数の型情報
*
* @param[in]   caller           発火したイベントを再生させたCubismMotionQueueManager
* @param[in]   eventValue       発火したイベントの文字列データ
* @param[in]   customData       コールバックに返される登録時に指定されたデータ
*/
typedef void(*CubismMotionEventFunction)(const CubismMotionQueueManager* caller, const csmString& eventValue, void* customData);

/**
 * @brief モーションの識別番号
 *
 * モーションの識別番号の定義
 */
typedef void* CubismMotionQueueEntryHandle;

extern const CubismMotionQueueEntryHandle InvalidMotionQueueEntryHandleValue;       ///< 無効なモーションの識別番号の定義

/**
 * @brief モーション再生の管理
 *
 * モーション再生の管理用クラス。CubismMotionモーションなどACubismMotionのサブクラスを再生するために使用する。
 *
 * @note 再生中に別のモーションが StartMotion()された場合は、新しいモーションに滑らかに変化し旧モーションは中断する。
 *       表情用モーション、体用モーションなどを分けてモーション化した場合など、
 *       複数のモーションを同時に再生させる場合は、複数のCubismMotionQueueManagerインスタンスを使用する。
 */
class CubismMotionQueueManager
{
public:
    /**
     * @brief コンストラクタ
     *
     * コンストラクタ。
     */
    CubismMotionQueueManager();

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

    /**
     * @brief 指定したモーションの開始
     *
     * 指定したモーションを開始する。同じタイプのモーションが既にある場合は、既存のモーションに終了フラグを立て、フェードアウトを開始させる。
     *
     * @param[in]   motion          開始するモーション
     * @param[in]   autoDelete      再生が終了したモーションのインスタンスを削除するなら true
     * @param[in]   userTimeSeconds デルタ時間の積算値[秒]
     * @return                      開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
     */
    CubismMotionQueueEntryHandle    StartMotion(ACubismMotion* motion, csmBool autoDelete, csmFloat32 userTimeSeconds);

    /**
     * @brief すべてのモーションの終了の確認
     *
     * すべてのモーションが終了しているかどうか。
     *
     * @retval  true    すべて終了している
     * @retval  false   終了していない
     */
    csmBool     IsFinished();

    /**
     * @brief 指定したモーションの終了の確認
     *
     * 指定したモーションが終了しているかどうか。
     *
     * @param[in]   motionQueueEntryNumber  モーションの識別番号
     * @retval  true    指定したモーションは終了している
     * @retval  false   終了していない
     */
    csmBool     IsFinished(CubismMotionQueueEntryHandle motionQueueEntryNumber);

    /**
     * @brief すべてのモーションの停止
     *
     * すべてのモーションを停止する。
     */
    void        StopAllMotions();

    /**
     * @brief 指定したCubismMotionQueueEntryの取得
     *
     * 指定したCubismMotionQueueEntryを取得する。
     *
     * @param[in]   motionQueueEntryNumber  モーションの識別番号
     * @return  指定したCubismMotionQueueEntryへのポインタ
     * @retval  NULL   見つからなかった
     */
    CubismMotionQueueEntry* GetCubismMotionQueueEntry(CubismMotionQueueEntryHandle motionQueueEntryNumber);

    /**
    * @brief イベントを受け取るCallbackの登録
    *
    * イベントを受け取るCallbackの登録をする。
    *
    * @param[in]   callback     コールバック関数
    * @param[in]   customData   コールバックに返されるデータ
    */
    void SetEventCallback(CubismMotionEventFunction callback, void* customData = NULL);

protected:
    /**
    * @brief モーションの更新
    *
    * モーションを更新して、モデルにパラメータ値を反映する。
    *
    * @param[in]   model   対象のモデル
    * @param[in]   userTimeSeconds   デルタ時間の積算値[秒]
    * @retval  true    モデルへパラメータ値の反映あり
    * @retval  false   モデルへパラメータ値の反映なし(モーションの変化なし)
    */
    virtual csmBool     DoUpdateMotion(CubismModel* model, csmFloat32 userTimeSeconds);


    csmFloat32 _userTimeSeconds;        ///< デルタ時間の積算値[秒]

private:
    csmVector<CubismMotionQueueEntry*>      _motions;       ///< モーション

    CubismMotionEventFunction         _eventCallback;     ///< コールバック関数ポインタ
    void*                             _eventCustomData;   ///< コールバックに戻されるデータ
};

}}}
