#include <render/primitives.h>
#include <resources.h>
#include <app.h>
#include <window.h>
#include "noteManager.hpp"

const float speed = 16;
const float hitTime = 0.1;
const float missTime = 0.075;
const float showTime = 1.5;

const vec4f colors[4] = {
    color::uint2rgba(0x639BFF),
    color::uint2rgba(0xE03B0D),
    color::uint2rgba(0xE87BC7),
    color::uint2rgba(0x21B293)
};

namespace game {

Note::Note(NoteManager *manager, const noteInfo *info):manager(manager),info(info)
{
    this->icon = resources::getIcon(info->button, info->type);
    this->placeholderIcon = resources::getPlaceholderIcon(info->button, info->type);

    if (this->info->type == 1) {
        resources::atlas.getIcon("long-icon.png", this->longIcon);
        this->longIcon.size.y = 0.75;

        this->longIcon.angle = info->angle;
        this->longIcon.color = colors[this->info->button];
    }

    resources::atlas.getIcon("timer-arrow.png", this->arrowIcon);
    this->arrowIcon.size = {0.3125, 0.78125};

    sincosf(info->angle + M_PI, &moveVector.y, &moveVector.x);

    if (this->info->type != 1) {

        switch (this->info->button) {
        case 0:
            this->effect = resources::effects.createEffect("trail_blue");
            break;
        case 1:
            this->effect = resources::effects.createEffect("trail_red");
            break;
        case 2:
            this->effect = resources::effects.createEffect("trail_pink");
            break;
        case 3:
            this->effect = resources::effects.createEffect("trail_green");
            break;
        }


        this->effect->start();
    }
}

Note::~Note()
{
    if (this->info->type != 1) {
        this->effect->terminate();
        delete effect;
    }
}

void Note::Update() noexcept
{
    if (this->effectType) {
        if (this->effectTime < 0) {
            this->manager->RemoveNote(this);
            return;
        }

        this->effectTime -= window::deltaTime;
    }

    if (this->info->type != 1) {

        constexpr vec2f renderOffset = {3, 2};
        float position = this->info->time - currentTime;
        this->effect->pos = this->info->position + renderOffset - moveVector * position * speed;

        effect->update();
    }

    this->currentTime = this->manager->GetTime();
}

void Note::Hit()
{
    App::GetSound()->PlayHit();

    this->effectTime = hitTime;
    this->effectType = 1;

    if (this->info->type != 1) {
        this->effect->terminate();
    }
}

void Note::Miss()
{
    this->effectTime = missTime;
    this->effectType = 2;

    if (this->info->type != 1) {
        this->effect->terminate();
    }
}

void Note::RenderEffects() noexcept
{
    if (this->info->type == 1) {
        return;
    }

    this->effect->draw();
}

void Note::RenderPlaceholder() noexcept
{
    constexpr vec2f renderOffset = {3, 2};

    if (this->active || this->effectType) {
        return;
    }

    this->arrowIcon.angle = M_PI * 2 * ((this->info->time - currentTime) / showTime);
    placeholderIcon.draw(this->info->position + renderOffset);
    this->arrowIcon.draw(this->info->position + renderOffset);
}

void Note::Render() noexcept
{
    constexpr vec2f renderOffset = {3, 2};
    float position = this->info->time - currentTime;

    switch (this->effectType) {
        case 0: {

            if (this->info->type == 1) {

                float activeLength = (this->info->time + this->info->duration - currentTime) * speed;

                if (this->active) {
                    this->longIcon.size.x = activeLength;
                    this->longIcon.draw(this->info->position + renderOffset);
                    this->placeholderIcon.draw(this->info->position + renderOffset);
                } else {
                    this->longIcon.size.x = this->info->duration * speed;
                    this->longIcon.draw(this->info->position + renderOffset - moveVector * position * speed);
                    this->icon.draw(this->info->position + renderOffset - moveVector * position * speed);
                }

                icon.draw(this->info->position + renderOffset - moveVector * activeLength);
            } else {
                icon.draw(this->info->position + renderOffset - moveVector * position * speed);
            }
        }
        break;
        case 1: {
            float effectScale = 1 - this->effectTime / hitTime;

            this->icon.scale = vec2f(1 + effectScale * 4);
            this->icon.color.a = 1 - effectScale;
            this->icon.draw(this->info->position + renderOffset);
            break;

        }
        case 2: {
            float effectScale = this->effectTime / missTime;

            this->icon.scale = vec2f(effectScale / 2 + 0.5);
            this->icon.color.a = effectScale;
            this->icon.draw(this->info->position + renderOffset - moveVector * position * speed);
            break;

        }
    }
}

}

