#include "Library/MapObj/SeesawMapParts.h"

#include "Library/LiveActor/ActorAreaFunction.h"
#include "Library/LiveActor/ActorInitFunction.h"
#include "Library/LiveActor/ActorInitUtil.h"
#include "Library/LiveActor/ActorModelFunction.h"
#include "Library/LiveActor/ActorPoseUtil.h"
#include "Library/LiveActor/ActorSensorUtil.h"
#include "Library/MapObj/ChildStep.h"
#include "Library/Math/MathUtil.h"
#include "Library/Nerve/NerveSetupUtil.h"
#include "Library/Placement/PlacementFunction.h"
#include "Library/Se/SeFunction.h"

namespace {
using namespace al;

NERVE_ACTION_IMPL(SeesawMapParts, Wait)

NERVE_ACTIONS_MAKE_STRUCT(SeesawMapParts, Wait)
}  // namespace

namespace al {
SeesawMapParts::SeesawMapParts(const char* name) : LiveActor(name) {}

void SeesawMapParts::init(const ActorInitInfo& info) {
    tryInitSubActorKeeperChildStep(this, info);
    initNerveAction(this, "Wait", &NrvSeesawMapParts.collector, 0);
    initMapPartsActor(this, info, nullptr);
    tryGetQuatPtr(this);

    registerAreaHostMtx(this, info);
    tryGetQuatPtr(this);

    mQuat = getQuat(this);
    calcQuatSide(&mSide, mQuat);
    calcQuatFront(&mFront, mQuat);

    tryGetArg(&mMaxDegree, info, "MaxDegree");
    tryGetArg(&mRotateAccelOn, info, "RotateAccelOn");
    tryGetArg(&mRotateAccelOff, info, "RotateAccelOff");

    initMaterialCode(this, info);
    createChildStep(info, this, true);
    trySyncStageSwitchAppear(this);
}

inline bool isGreaterThanOrEqualToZero(f32 val) {  // sead::Mathf::assertGreaterThanOrEqualToZero_ ?
    return val >= 0.0f;
}

bool SeesawMapParts::receiveMsg(const SensorMsg* message, HitSensor* other, HitSensor* self) {
    if (isMsgFloorTouch(message)) {
        sead::Vector3f pos;
        if (isMySensor(self, this))
            pos.set(getSensorPos(other));
        else
            pos.set(getActorTrans(self));

        f32 weight = isMsgEnemyFloorTouch(message) ? 0.9f : 1.0f;
        if (!isGreaterThanOrEqualToZero((pos - getTrans(this)).dot(mFront)))
            weight = -weight;

        mWeight += weight;

        return true;
    }

    if (isMsgShowModel(message)) {
        showModelIfHide(this);

        return true;
    }

    if (isMsgHideModel(message)) {
        hideModelIfShow(this);

        return true;
    }

    if (isMsgRestart(message)) {
        appearAndSetStart();

        return true;
    }

    return false;
}

void SeesawMapParts::appearAndSetStart() {
    mRotateDegree = 0.0f;
    mRotateSpeed = 0.0f;
    mWeight = 0.0f;
    mRemainingAccelOnFrames = 0;

    setQuat(this, mQuat);

    makeActorAlive();
}

void SeesawMapParts::exeWait() {
    if (mWeight > 0.0f)
        mRemainingAccelOnFrames = mRemainingAccelOnFrames > 59 ? 60 : mRemainingAccelOnFrames + 1;
    else if (mWeight < 0.0f)
        mRemainingAccelOnFrames = mRemainingAccelOnFrames < -59 ? -60 : mRemainingAccelOnFrames - 1;
    else if (mRemainingAccelOnFrames > 0)
        mRemainingAccelOnFrames--;
    else if (mRemainingAccelOnFrames < 0)
        mRemainingAccelOnFrames++;

    mWeight = 0.0f;

    if (mRemainingAccelOnFrames > 0)
        mRotateSpeed += mRotateAccelOn;
    else if (mRemainingAccelOnFrames < 0)
        mRotateSpeed -= mRotateAccelOn;
    else if (mRotateDegree >= 0.0f)
        mRotateSpeed -= mRotateAccelOff;
    else
        mRotateSpeed += mRotateAccelOff;

    mRotateSpeed *= 0.95f;
    mRotateDegree += mRotateSpeed;
    f32 rotateSpeed = sead::Mathf::abs(mRotateSpeed);

    if (sead::Mathf::abs(mRotateDegree) > mMaxDegree) {
        if (isSameSign(mRotateSpeed, mRotateDegree))
            mRotateSpeed *= -0.5f;

        mRotateDegree = sead::Mathf::clamp(mRotateDegree, -mMaxDegree, mMaxDegree);

        if (rotateSpeed > 0.2f)
            tryStartSeWithParam(this, "Stop", rotateSpeed, "");
    }

    if (rotateSpeed > 0.1f)
        tryHoldSeWithParam(this, "Rotate", rotateSpeed, "");

    rotateQuatRadian(getQuatPtr(this), mQuat, mSide, sead::Mathf::deg2rad(mRotateDegree));
}
}  // namespace al
