#include "item.h"
#include <stdlib.h>			 // para rand
#include "animated_sprite.h" // para SpriteAnimated
#include "param.h"			 // para param
class Texture;

// Constructor
Item::Item(ItemType type, float x, float y, SDL_Rect &play_area, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation)
	: sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
	  type_(type),
	  play_area_(play_area)
{
	switch (type)
	{
	case ItemType::COFFEE_MACHINE:
	{
		width_ = 28;
		height_ = 37;
		pos_x_ = ((static_cast<int>(x) + (play_area.w / 2)) % (play_area.w - width_ - 5)) + 2;
		pos_y_ = -height_;
		vel_x_ = 0.0f;
		vel_y_ = -0.1f;
		accel_y_ = 0.1f;
		collider_.r = 10;
		break;
	}
	default:
	{
		width_ = 20;
		height_ = 20;
		pos_x_ = x;
		pos_y_ = y;
		vel_x_ = -1.0f + ((rand() % 5) * 0.5f);
		vel_y_ = -4.0f;
		accel_y_ = 0.2f;
		collider_.r = width_ / 2;
		break;
	}
	}

	sprite_->setPosX(pos_x_);
	sprite_->setPosY(pos_y_);
	shiftColliders();
}

// Centra el objeto en la posición X
void Item::alignTo(int x)
{
	pos_x_ = static_cast<float>(x - (width_ / 2));

	if (pos_x_ < param.game.play_area.rect.x)
	{
		pos_x_ = param.game.play_area.rect.x + 1;
	}
	else if (pos_x_ + width_ > play_area_.w)
	{
		pos_x_ = static_cast<float>(play_area_.w - width_ - 1);
	}

	// Posición X,Y del sprite
	sprite_->setPosX(pos_x_);
	sprite_->setPosY(pos_y_);

	// Alinea el circulo de colisión con el objeto
	shiftColliders();
}

// Pinta el objeto en la pantalla
void Item::render()
{
	if (enabled_)
	{
		if (time_to_live_ > 200)
		{
			sprite_->render();
		}
		else if (time_to_live_ % 20 > 10)
		{
			sprite_->render();
		}
	}
}

// Actualiza la posición y estados del objeto
void Item::move()
{
	floor_collision_ = false;

	// Calcula la nueva posición
	pos_x_ += vel_x_;
	pos_y_ += vel_y_;

	// Aplica las aceleraciones a la velocidad
	vel_x_ += accel_x_;
	vel_y_ += accel_y_;

	// Si queda fuera de pantalla, corregimos su posición y cambiamos su sentido
	if ((pos_x_ < param.game.play_area.rect.x) || (pos_x_ + width_ > play_area_.w))
	{
		// Corregir posición
		pos_x_ -= vel_x_;

		// Invertir sentido
		vel_x_ = -vel_x_;
	}

	// Si se sale por arriba rebota (excepto la maquina de café)
	if ((pos_y_ < param.game.play_area.rect.y) && !(type_ == ItemType::COFFEE_MACHINE))
	{
		// Corrige
		pos_y_ = param.game.play_area.rect.y;

		// Invierte el sentido
		vel_y_ = -vel_y_;
	}

	// Si el objeto se sale por la parte inferior
	if (pos_y_ + height_ > play_area_.h)
	{
		// Detiene el objeto
		vel_y_ = 0;
		vel_x_ = 0;
		accel_x_ = 0;
		accel_y_ = 0;
		pos_y_ = play_area_.h - height_;
		if (type_ == ItemType::COFFEE_MACHINE)
		{
			floor_collision_ = true;
		}
	}

	// Actualiza la posición del sprite
	sprite_->setPosX(int(pos_x_));
	sprite_->setPosY(int(pos_y_));
	shiftColliders();
}

// Pone a cero todos los valores del objeto
void Item::disable()
{
	enabled_ = false;
}

// Actualiza el objeto a su posicion, animación y controla los contadores
void Item::update()
{
	move();
	sprite_->update();
	updateTimeToLive();
	checkTimeToLive();
}

// Actualiza el contador
void Item::updateTimeToLive()
{
	if (time_to_live_ > 0)
	{
		time_to_live_--;
	}
}

// Comprueba si el objeto sigue vivo
void Item::checkTimeToLive()
{
	if (time_to_live_ == 0)
		disable();
}

// Obtiene del valor de la variable
float Item::getPosX()
{
	return pos_x_;
}

// Obtiene del valor de la variable
float Item::getPosY()
{
	return pos_y_;
}

// Obtiene del valor de la variable
int Item::getWidth()
{
	return width_;
}

// Obtiene del valor de la variable
int Item::getHeight()
{
	return height_;
}

// Obtiene del valor de la variable
ItemType Item::getType()
{
	return type_;
}

// Obtiene el valor de la variable
bool Item::isEnabled()
{
	return enabled_;
}

// Obtiene el circulo de colisión
Circle &Item::getCollider()
{
	return collider_;
}

// Alinea el circulo de colisión con la posición del objeto
void Item::shiftColliders()
{
	collider_.x = int(pos_x_ + (width_ / 2));
	collider_.y = int(pos_y_ + (height_ / 2));
}

// Informa si el objeto ha colisionado con el suelo
bool Item::isOnFloor()
{
	return floor_collision_;
}