#include <Urho3D/Audio/Sound.h>
#include <Urho3D/Audio/SoundSource3D.h>
#include <Urho3D/Core/Context.h>
#include <Urho3D/Physics/RigidBody.h>
#include <Urho3D/Resource/ResourceCache.h>
#include <Urho3D/Scene/Node.h>
#include <Urho3D/Scene/Scene.h>
#include <Urho3D/Scene/ValueAnimation.h>
#include <Urho3D/UI/UI.h>
#include <Urho3D/UI/Text.h>

#include "Common.h"
#include "Grower.h"

namespace {
    constexpr auto E_GROW = "grow";
    constexpr auto E_JUMP = "jump";
    constexpr auto E_SHAKE_LEFT = "shake-left";
    constexpr auto E_SHAKE_RIGHT = "shake-right";
    constexpr auto SHAKE_FORCE = 3.0f;
    constexpr auto SHAKE_LEFT_START = GROWTH_START_TIME  * 0.33;
    constexpr auto SHAKE_RIGHT_START = GROWTH_START_TIME * 0.66;
    constexpr auto MAX_LEVEL = 24;
}

Grower::Grower(Context* context) :
    LogicComponent(context),
    tofuLevel_{0}
{
}

void Grower::RegisterObject(Context* context)
{
    context->RegisterFactory<Grower>();
}


void Grower::Grow(int tofuEaten) {
    auto node = GetNode();
    tofuLevel_ = tofuEaten;



    if (tofuEaten % 3 == 0) {
        auto const scale = node->GetScale();
        SharedPtr<ValueAnimation> scaleAnimation{new ValueAnimation(context_)};

        scaleAnimation->SetKeyFrame(
                0.0f,
                Vector3{scale.x_, scale.y_, scale.z_});


        scaleAnimation->SetKeyFrame(
                0.0f,
                Vector3{scale.x_, scale.y_, scale.z_});
        scaleAnimation->SetEventFrame(SHAKE_LEFT_START, E_SHAKE_LEFT);
        SubscribeToEvent(node, E_SHAKE_LEFT, URHO3D_HANDLER(Grower, HandleShakeLeft));


        scaleAnimation->SetKeyFrame(
                0.0f,
                Vector3{scale.x_, scale.y_, scale.z_});
        scaleAnimation->SetEventFrame(SHAKE_RIGHT_START, E_SHAKE_RIGHT);
        SubscribeToEvent(node, E_SHAKE_RIGHT, URHO3D_HANDLER(Grower, HandleShakeRight));


        scaleAnimation->SetKeyFrame(
                GROWTH_START_TIME,
                Vector3{scale.x_, scale.y_, scale.z_});
        scaleAnimation->SetEventFrame(GROWTH_START_TIME + 0.1, E_GROW);
        SubscribeToEvent(node, E_GROW, URHO3D_HANDLER(Grower, HandleGrowStart));

        auto const growthEndSize = Vector3{
                scale.x_ + PLAYER_SCALE_INC * tofuEaten,
                scale.y_ + PLAYER_SCALE_INC * tofuEaten,
                scale.z_ + PLAYER_SCALE_INC * tofuEaten
        };

        scaleAnimation->SetKeyFrame(GROWTH_END_TIME, growthEndSize);

        auto constexpr JUMP_TIME = GROWTH_END_TIME;

        scaleAnimation->SetKeyFrame(JUMP_TIME, growthEndSize);
        scaleAnimation->SetEventFrame(JUMP_TIME, E_JUMP);
        SubscribeToEvent(node, E_JUMP, URHO3D_HANDLER(Grower, HandleJump));

        node->SetAttributeAnimation("Scale", scaleAnimation, WM_ONCE);

        UI* ui = GetSubsystem<UI>();
        auto textBox = ui->GetRoot()->GetChildDynamicCast<Text>(String{"Instructions"});
        String text;
        if (tofuEaten >= MAX_LEVEL) {
            text = "Fermented freedom.";
        }
        else if (tofuEaten >= 21) {
            text = "Why do they have faces?";
        }
        else if (tofuEaten >= 18) {
            text = "Never stop feeding me.";
        }
        else if (tofuEaten >= 15) {
            text = "Experience subtle flavours.";
        }
        else if (tofuEaten >= 12) {
            text = "Tofu for strong body.";
        }
        else if (tofuEaten >= 9) {
            text = "Keep the soya coming.";
        }
        else if (tofuEaten >= 6) {
            text = "Do not stop feeding.";
        }
        else {
            text = "More tofu.";
        }
        textBox->SetText(text);
        textBox->RemoveAttributeAnimation("Opacity");
        {
            auto constexpr OFFSET = 3.0f;
            ValueAnimation* anim{ new ValueAnimation{ context_ } };
            anim->SetKeyFrame(0.0f, 0.0f);
            anim->SetKeyFrame(OFFSET + 0.4f, 0.0f);
            anim->SetKeyFrame(OFFSET + 0.5f, 1.0f);
            anim->SetKeyFrame(OFFSET + 3.0f, 1.0f);
            anim->SetKeyFrame(OFFSET + 4.0f, 0.0f);
            textBox->SetAttributeAnimation("Opacity", anim, WM_ONCE);
        }

    }
}

void Grower::HandleShakeLeft(StringHash, VariantMap&) {
    auto node = GetNode();
    UnsubscribeFromEvent(node, E_SHAKE_LEFT);

    auto bunionBody = node->GetComponent<RigidBody>();
    bunionBody->ApplyTorque(Vector3::FORWARD * SHAKE_FORCE);
}

void Grower::HandleShakeRight(StringHash, VariantMap&) {
    auto node = GetNode();
    UnsubscribeFromEvent(node, E_SHAKE_RIGHT);

    auto bunionBody = node->GetComponent<RigidBody>();
    bunionBody->ApplyTorque(Vector3::BACK * SHAKE_FORCE);
}

void Grower::HandleGrowStart(StringHash, VariantMap&) {
    auto node = GetNode();
    auto scene = node->GetScene();

    UnsubscribeFromEvent(node, E_GROW);

    // Squeak SFX
    {
        auto const name = String{"fds/squeak.wav"};
        auto const sound = GetSubsystem<ResourceCache>()->GetResource<Sound>(name);
        auto const source = node->CreateComponent<SoundSource3D>();
        source->SetSoundType(SOUND_EFFECT);
        source->SetAutoRemoveMode(REMOVE_COMPONENT);
        source->SetNearDistance(1.0f);
        source->SetFarDistance(50.0f);
        source->SetGain(0.60f);
        source->Play(sound);
    }
}

void Grower::HandleJump(StringHash, VariantMap&) {
    auto node = GetNode();
    UnsubscribeFromEvent(node, E_JUMP);
    auto velocityBoost = Vector3::ZERO;
    velocityBoost.y_ = 1.0f * GetNode()->GetScale().Length();
    if (tofuLevel_ >= MAX_LEVEL) {
        velocityBoost.z_ = 2.0f;
        velocityBoost.y_ = 60.0f;
        UI* ui = GetSubsystem<UI>();
        auto textBox = ui->GetRoot()->GetChildDynamicCast<Text>(String{"Credits"});
        textBox->RemoveAttributeAnimation("Opacity");
        {
            auto constexpr OFFSET = 3.0f;
            ValueAnimation* anim{ new ValueAnimation{ context_ } };
            anim->SetKeyFrame(0.0f, 0.0f);
            anim->SetKeyFrame(OFFSET + 0.4f, 0.0f);
            anim->SetKeyFrame(OFFSET + 0.5f, 1.0f);
            anim->SetKeyFrame(OFFSET + 15.0f, 1.0f);
            anim->SetKeyFrame(OFFSET + 16.0f, 0.0f);
            textBox->SetAttributeAnimation("Opacity", anim, WM_ONCE);
        }
    }
    auto bunionBody = node->GetComponent<RigidBody>();
    bunionBody->SetLinearVelocity(bunionBody->GetLinearVelocity() + velocityBoost);

}
