#include "Library/Play/Layout/BalloonMessage.h"

#include "Library/Layout/LayoutActionFunction.h"
#include "Library/Layout/LayoutActorUtil.h"
#include "Library/Layout/LayoutInitInfo.h"
#include "Library/LiveActor/ActorClippingFunction.h"
#include "Library/LiveActor/ActorFlagFunction.h"
#include "Library/LiveActor/ActorInitUtil.h"
#include "Library/LiveActor/ActorMovementFunction.h"
#include "Library/LiveActor/ActorPoseUtil.h"
#include "Library/LiveActor/LiveActor.h"
#include "Library/Nerve/NerveSetupUtil.h"
#include "Library/Nerve/NerveUtil.h"
#include "Library/Play/Layout/TalkMessageVoicePlayer.h"
#include "Library/Player/PlayerUtil.h"
#include "Library/Screen/ScreenFunction.h"
#include "Library/Se/SeFunction.h"

namespace {
using namespace al;

NERVE_IMPL(BalloonMessage, Wait)
NERVE_IMPL(BalloonMessage, Appear)
NERVE_IMPL(BalloonMessage, Hide)
NERVE_IMPL(BalloonMessage, End)

NERVES_MAKE_NOSTRUCT(BalloonMessage, Wait)
NERVES_MAKE_STRUCT(BalloonMessage, Appear, Hide, End)
}  // namespace

namespace al {
BalloonMessage* BalloonMessage::create(const LiveActor* hostActor, const LayoutInitInfo& info,
                                       const BalloonMessageInitParam& initParam,
                                       bool isAutoUpdate) {
    return new BalloonMessage(hostActor, info, initParam, isAutoUpdate);
}

BalloonMessage::BalloonMessage(const LiveActor* hostActor, const LayoutInitInfo& info,
                               const BalloonMessageInitParam& initParam, bool isAutoUpdate)
    : LayoutActor(initParam.name), mHostActor(hostActor), mPaneName(initParam.paneName),
      mAppearDist(initParam.appearDist), mKillDist(initParam.killDist),
      mPlayerIndex(initParam.playerIndex), _158(initParam._38), mIsAutoUpdate(isAutoUpdate),
      _15d(initParam._3c) {
    mPosOffset.y = initParam.yPosOffset;

    initLayoutActor(this, info, initParam.archiveName, initParam.suffix);

    mTalkMessageVoicePlayer = new TalkMessageVoicePlayer();
    mTalkMessageVoicePlayer->set_420(true);

    setText(initParam.message);
    initNerve(&Wait, 0);

    kill();
}

void BalloonMessage::setText(const char* message) {
    setPaneStringFormat(this, mPaneName, message);
}

inline void startAppear(LayoutActor* layout) {
    startAction(layout, "Appear", nullptr);
}

void BalloonMessage::appear() {
    updateTrans();
    hidePushA();

    if (!mIsAutoUpdate) {
        startAppear(this);
        LayoutActor::appear();

        if (!mIsTalkMessageVoicePlayerStarted)
            mTalkMessageVoicePlayer->start(this, mHostActor, getPaneStringBuffer(this, mPaneName),
                                           5);

        setNerve(this, &NrvBalloonMessage.Appear);

        return;
    }

    if (isEnableAppear()) {
        startAppear(this);
        LayoutActor::appear();
        setActionFrameRate(this, 1.0f, nullptr);
        setNerve(this, &NrvBalloonMessage.Appear);

        return;
    }

    LayoutActor::appear();
    startFreezeActionEnd(this, "End", nullptr);
    setNerve(this, &NrvBalloonMessage.Hide);
}

void BalloonMessage::updateTrans() {
    sead::Vector2f layoutPos = sead::Vector2f::zero;

    if (_158 == 0)
        calcLayoutPosFromWorldPos(&layoutPos, mHostActor, getTrans(mHostActor) + mPosOffset);
    else
        calcLayoutPosFromWorldPosSub(&layoutPos, mHostActor, getTrans(mHostActor) + mPosOffset);

    setLocalTrans(this, layoutPos);
}

void BalloonMessage::hidePushA() {
    if (!isExistPane(this, "TxtA"))
        return;

    hidePane(this, "TxtA");
}

bool BalloonMessage::isEnableAppear() const {
    if (!isNerve(this, &NrvBalloonMessage.End) && !isNerve(this, &NrvBalloonMessage.Hide) &&
        isAlive())
        return false;

    return isNearPlayerActor(mAppearDist);
}

void BalloonMessage::appearWithPushA() {
    appear();
    showPushA();
}

void BalloonMessage::showPushA() {
    if (!isExistPane(this, "TxtA"))
        return;

    showPane(this, "TxtA");
}

void BalloonMessage::control() {
    if (mIsAutoUpdate)
        update();

    if (_15d && !isNerve(this, &NrvBalloonMessage.End) && !isNerve(this, &NrvBalloonMessage.Hide) &&
        (isClipped(mHostActor) || isDead(mHostActor))) {
        setNerve(this, &NrvBalloonMessage.End);

        return;
    }

    mTalkMessageVoicePlayer->update();
    updateTrans();
}

void BalloonMessage::update() {
    if (isEnableAppear())
        appear();

    if (isEnableEnd())
        setNerve(this, &NrvBalloonMessage.End);
}

bool BalloonMessage::isWait() const {
    return isNerve(this, &Wait);
}

bool BalloonMessage::isVoicePlayerPlaying() const {
    if (mTalkMessageVoicePlayer->isPlaying())
        return true;

    if (!mSeName.cstr())
        return false;

    return checkIsPlayingSe(mHostActor, mSeName.cstr(), nullptr);
}

bool BalloonMessage::isShowPushA() const {
    return !isHidePane(this, "TxtA");
}

bool BalloonMessage::isEnableEnd() const {
    if (isNerve(this, &NrvBalloonMessage.End) || isNerve(this, &NrvBalloonMessage.Hide) ||
        !isAlive())
        return false;

    return !isNearPlayerActor(mKillDist);
}

void BalloonMessage::end() {
    if (isNerve(this, &NrvBalloonMessage.End) || isNerve(this, &NrvBalloonMessage.Hide))
        return;

    setNerve(this, &NrvBalloonMessage.End);
}

void BalloonMessage::setTextW(const char16* message) {
    setPaneString(this, mPaneName, message, 0);
}

void BalloonMessage::exeAppear() {
    if (isActionEnd(this, nullptr))
        setNerve(this, &Wait);
}

void BalloonMessage::exeWait() {
    if (isFirstStep(this))
        startAction(this, "Wait", nullptr);
}

void BalloonMessage::exeEnd() {
    if (isFirstStep(this))
        startAction(this, "End", nullptr);

    if (isActionEnd(this, nullptr)) {
        if (mIsAutoUpdate)
            setNerve(this, &NrvBalloonMessage.Hide);
        else
            kill();
    }
}

void BalloonMessage::exeHide() {}

bool BalloonMessage::isNearPlayerActor(f32 threshold) const {
    if (mPlayerIndex >= 0)
        return isNear(mHostActor, getPlayerActor(mHostActor, mPlayerIndex), threshold);

    return isNearPlayer(mHostActor, threshold);
}

BalloonMessage* createBalloonMessage(const LiveActor* hostActor, const ActorInitInfo& info,
                                     const BalloonMessageInitParam& initParam) {
    return BalloonMessage::create(hostActor, getLayoutInitInfo(info), initParam, true);
}

BalloonMessage* createBalloonMessageNoAutoUpdate(const LiveActor* hostActor,
                                                 const ActorInitInfo& info,
                                                 const BalloonMessageInitParam& initParam) {
    return BalloonMessage::create(hostActor, getLayoutInitInfo(info), initParam, false);
}

BalloonMessage* createBalloonMessage(const LiveActor* hostActor, const ActorInitInfo& info) {
    BalloonMessageInitParam initParam = {.message = "未設定"};

    return BalloonMessage::create(hostActor, getLayoutInitInfo(info), initParam, true);
}

BalloonMessage* createBalloonMessage(const LiveActor* hostActor, const ActorInitInfo& info,
                                     const char* message) {
    BalloonMessageInitParam initParam = {.message = message};

    return BalloonMessage::create(hostActor, getLayoutInitInfo(info), initParam, true);
}
}  // namespace al
