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

#include "Library/Base/StringUtil.h"
#include "Library/Layout/LayoutActionFunction.h"
#include "Library/Layout/LayoutActorUtil.h"
#include "Library/Layout/LayoutInitInfo.h"
#include "Library/Nerve/NerveSetupUtil.h"
#include "Library/Nerve/NerveUtil.h"

namespace {
using namespace al;

NERVE_IMPL(RollParts, Deactive)
NERVE_IMPL(RollParts, Active)
NERVE_IMPL(RollParts, RollIn)
NERVE_IMPL(RollParts, RollOut)

NERVES_MAKE_STRUCT(RollParts, Deactive, Active, RollIn, RollOut)
}  // namespace

namespace al {
RollParts::RollParts(LayoutActor* parent, const LayoutInitInfo& info, const char* archiveName)
    : LayoutActor("ロールパーツ") {
    initLayoutPartsActor(this, parent, info, archiveName, nullptr);
    initNerve(&NrvRollParts.Deactive, 0);
}

void RollParts::startLoopAction(const char* actionName, const char* paneName) {
    startAction(this, actionName, paneName);
}

void RollParts::setData(const char16** messages, s32 messageCount, bool isLoop, s32 selectedIdx,
                        const char* paneName) {
    mIsLoop = isLoop;
    mMessages = messages;
    mMessageCount = messageCount;
    mPaneName = paneName;
    mCurrentActionType = ActionType::Active;
    mNextActionType = ActionType::Active;

    if (selectedIdx >= 0)
        mSelectedIdx = selectedIdx;

    setPaneString(this, mPaneName, mMessages[mSelectedIdx], 0);
    updateHeaderText();

    if (!mHasStatePane)
        return;

    if (messageCount == 1) {
        if (isExistAction(this, "Off", "State"))
            startAction(this, "Off", "State");
    } else {
        if (isExistAction(this, "On", "State"))
            startAction(this, "On", "State");
    }
}

void RollParts::updateHeaderText() {
    if (!mHeaderPaneName)
        return;

    WStringTmp<0x20> text;
    text.clear();

    if (mMessageCount > 1) {
        for (s32 i = 0; i < mMessageCount; i++)
            if (i == mSelectedIdx)
                text.append(u"1");
            else
                text.append(u"0");
    }

    setPaneString(this, mHeaderPaneName, text.cstr(), 0);
}

void RollParts::setSelectedIdx(s32 idx) {
    mSelectedIdx = idx;
    setPaneString(this, mPaneName, mMessages[mSelectedIdx], 0);
}

void RollParts::activate() {
    if (isNerve(this, &NrvRollParts.Active))
        return;

    if (mMessages)
        setPaneString(this, mPaneName, mMessages[mSelectedIdx], 0);

    setNerve(this, &NrvRollParts.Active);
}

void RollParts::activate(s32 selectedIdx) {
    if (isNerve(this, &NrvRollParts.Active))
        return;

    mSelectedIdx = selectedIdx;
    activate();
}

void RollParts::deactivate() {
    if (isNerve(this, &NrvRollParts.Active)) {
        setNerve(this, &NrvRollParts.Deactive);

        return;
    }

    if (isRoll())
        mNextActionType = ActionType::Deactive;
}

void RollParts::rollRight() {
    if ((!mIsLoop && mSelectedIdx + 1 >= mMessageCount) || mMessageCount <= 1)
        return;

    if (isNerve(this, &NrvRollParts.Active)) {
        mCurrentActionType = ActionType::RollRight;
        setNerve(this, &NrvRollParts.RollOut);

        return;
    }

    if (isNerve(this, &NrvRollParts.RollOut) || isNerve(this, &NrvRollParts.RollIn))
        mNextActionType = ActionType::RollRight;
}

void RollParts::rollLeft() {
    if ((!mIsLoop && mSelectedIdx <= 0) || mMessageCount <= 1)
        return;

    if (isNerve(this, &NrvRollParts.Active)) {
        mCurrentActionType = ActionType::RollLeft;
        setNerve(this, &NrvRollParts.RollOut);

        return;
    }

    if (isNerve(this, &NrvRollParts.RollOut) || isNerve(this, &NrvRollParts.RollIn))
        mNextActionType = ActionType::RollLeft;
}

void RollParts::calcCursorTrans(sead::Vector2f* outCursorTrans) const {
    calcPaneTrans(outCursorTrans, this, "Cursor");
}

bool RollParts::isJustChangeRoll() const {
    return isNerve(this, &NrvRollParts.RollIn) && getNerveStep(this) == 1;
}

bool RollParts::isRoll() const {
    return isNerve(this, &NrvRollParts.RollIn) || isNerve(this, &NrvRollParts.RollOut);
}

void RollParts::exeDeactive() {
    if (isFirstStep(this) && mDeactiveAction)
        startAction(this, mDeactiveAction, nullptr);
}

void RollParts::exeActive() {
    if (isFirstStep(this) && mActiveAction)
        startAction(this, mActiveAction, nullptr);
}

void RollParts::exeRollOut() {
    if (isFirstStep(this)) {
        startAction(this,
                    mCurrentActionType == ActionType::RollLeft ? mRollLeftOutAction :
                                                                 mRollRightOutAction,
                    mRollPaneName);

        if (mCurrentActionType == ActionType::RollLeft) {
            startHitReaction(this, "左ロール", nullptr);
            mSelectedIdx--;

            if (mSelectedIdx < 0)
                mSelectedIdx = mIsLoop ? mMessageCount - 1 : 0;
        } else {
            startHitReaction(this, "右ロール", nullptr);
            mSelectedIdx++;

            if (mSelectedIdx >= mMessageCount)
                mSelectedIdx = mIsLoop ? 0 : mMessageCount - 1;
        }

        updateHeaderText();
    }

    if (isActionEnd(this, mRollPaneName))
        setNerve(this, &NrvRollParts.RollIn);
}

void RollParts::exeRollIn() {
    if (isFirstStep(this)) {
        startAction(this,
                    mCurrentActionType == ActionType::RollLeft ? mRollLeftInAction :
                                                                 mRollRightInAction,
                    mRollPaneName);
        setPaneString(this, mPaneName, mMessages[mSelectedIdx], 0);
    }

    if (isActionEnd(this, mRollPaneName)) {
        mCurrentActionType = mNextActionType;
        mNextActionType = ActionType::Active;

        switch (mCurrentActionType) {
        case ActionType::RollRight:
        case ActionType::RollLeft:
            setNerve(this, &NrvRollParts.RollOut);
            break;
        case ActionType::Deactive:
            setNerve(this, &NrvRollParts.Deactive);
            break;
        default:
            setNerve(this, &NrvRollParts.Active);
            break;
        }
    }
}
}  // namespace al
