#include "ifdefs.h"
#include "const.h"
#include "text.h"
#include "menu.h"

// Constructor
Menu::Menu()
{
	init("", 0, 0, 0, nullptr, nullptr, nullptr);
}

Menu::~Menu()
{
	mRenderer = nullptr;
	mText = nullptr;
}

// Inicializador
void Menu::init(std::string name, int x, int y, int backgroundType, LTexture *texture, SDL_Renderer *renderer, Text *text)
{
	// Inicia variables
	mName = name;
	mSelector.index = 0;
	mTotalItems = 0;
	mItemSelected = MENU_NO_OPTION;
	mVerticalPadding = 1;
	mPosX = x;
	mPosY = y;
	mRectBG.rect.x = 0;
	mRectBG.rect.y = 0;
	mRectBG.rect.w = 0;
	mRectBG.rect.h = 0;
	mRectBG.r = 0;
	mRectBG.g = 0;
	mRectBG.b = 0;
	mSelector.rect.x = 0;
	mSelector.rect.y = 0;
	mSelector.rect.w = 0;
	mSelector.rect.h = 0;
	mSelector.r = 0;
	mSelector.g = 0;
	mSelector.b = 0;
	mSelector.a = 255;
	mBackgroundType = backgroundType;
	mText = text;
	mRenderer = renderer;
	mIsCentered = false;
	mAreElementsCentered = false;
	mCenter = x + ((SCREEN_WIDTH - x) / 2);
	mWidestItem = 0;

	// Selector
	mSelector.origin = 0;
	mSelector.target = 0;
	mSelector.y = 0;
	mSelector.numJumps = 4;
	mSelector.despY = 0;
	mSelector.moving = false;

	// Sprite con los graficos del selector
	//mSelectorSprite.setWidth(8);
	//mSelectorSprite.setHeight(8);
	//mSelectorSprite.setPosX(0);
	//mSelectorSprite.setPosY(0);
	//mSelectorSprite.setTexture(texture);
	//mSelectorSprite.setRenderer(renderer);
	//mSelectorSprite.setSpriteClip(0, 0, mSelectorSprite.getWidth(), mSelectorSprite.getHeight());

	// Elementos del menu
	for (int i = 0; i < 10; i++)
	{
		mItem[i].label = "";
		mItem[i].w = 0;
		mItem[i].h = 0;
		mItem[i].x = 0;
		mItem[i].y = 0;
		mItem[i].hPaddingUp = 0;
		mItem[i].hPaddingDown = 0;
	}

	// Mueve el grafico del selector al elemento seleccionado
	//moveSelectorSprite(mSelector.index);
}

// Obtiene el nombre del menu
std::string Menu::getName()
{
	return mName;
}

// Obtiene el valor de la variable
Uint8 Menu::getItemSelected()
{
	return mItemSelected;
}

// Mueve el grafico del selector al elemento seleccionado
/*void Menu::moveSelectorSprite(int pos)
{
	mSelector.rect.y = mItem[pos].y - 1;
}*/

// Actualiza la posicion y el estado del selector
void Menu::updateSelector()
{
	if (mSelector.moving)
	{
		mSelector.y += mSelector.despY;
		if (mSelector.despY > 0) // Va hacia abajo
		{
			if (mSelector.y > mSelector.target) // Ha llegado al destino
			{
				mSelector.origin = mSelector.y = mSelector.target;
				mSelector.moving = false;
			}
		}
		if (mSelector.despY < 0) // Va hacia abajo
		{
			if (mSelector.y < mSelector.target) // Ha llegado al destino
			{
				mSelector.origin = mSelector.y = mSelector.target;
				mSelector.moving = false;
			}
		}
		mSelector.rect.y = int(mSelector.y) - 1;
	}
	else
	{
		mSelector.rect.y = int(mSelector.y) - 1;
	}
}

// Establece el origen del selector
void Menu::setSelectorOrigin(int value)
{
	mSelector.origin = value;
}

// Establece el destino del selector
void Menu::setSelectorTarget(int value)
{
	mSelector.target = value;
}

// Coloca el selector en una posición específica
void Menu::setSelectorPos(Uint8 index)
{
	if (index < mTotalItems)
	{
		mSelector.index = index;
		mSelector.y = mSelector.origin = mSelector.target = mItem[mSelector.index].y;
		mSelector.moving = false;
	}
}

// Obtiene la anchura del elemento más ancho del menu
Uint16 Menu::getWidestItem()
{
	Uint16 result = 0;

	// Obtenemos la anchura del item mas ancho
	for (Uint8 i = 0; i < mTotalItems; i++)
	{
		if (mItem[i].w > result)
		{
			result = mItem[i].w;
		}
	}

	return result;
}

// Deja el menu apuntando al primer elemento
void Menu::reset()
{
	mItemSelected = MENU_NO_OPTION;
	mSelector.index = 0;
	//moveSelectorSprite(mSelector.index);
	mSelector.origin = mSelector.target = mSelector.y = mItem[0].y;
	mSelector.moving = false;
}

// Deja el menu sin elemento seleccionado
void Menu::deselectItem()
{
	mItemSelected = MENU_NO_OPTION;
}

// Actualiza el menu para recolocarlo correctamente y establecer el tamaño
void Menu::reorganize()
{
	setRectSize();
	if (mIsCentered)
		centerMenu(mCenter);
	if (mAreElementsCentered)
		centerMenuElements();
}

// Deja el menu apuntando al siguiente elemento
bool Menu::increaseSelectorIndex()
{
	bool success = false;
	mSelector.y = mSelector.origin = mItem[mSelector.index].y;

	if (mSelector.index < (mTotalItems - 1))
	{
		mSelector.index++;
		success = true;
	}

	mSelector.target = mItem[mSelector.index].y;
	mSelector.despY = (mSelector.target - mSelector.origin) / mSelector.numJumps;
	mSelector.moving = true;
	return success;
}

// Deja el menu apuntando al elemento anterior
bool Menu::decreaseSelectorIndex()
{
	bool success = false;
	mSelector.y = mSelector.origin = mItem[mSelector.index].y;

	if (mSelector.index > 0)
	{
		mSelector.index--;
		success = true;
	}

	mSelector.target = mItem[mSelector.index].y;
	mSelector.despY = (mSelector.target - mSelector.origin) / mSelector.numJumps;
	mSelector.moving = true;
	return success;
}

// Comprueba la entrada (teclado, gamepad) y actua en consecuencia
bool Menu::checkInput(Uint8 input)
{
	bool success = false;
	switch (input)
	{
	case INPUT_UP:
		success = decreaseSelectorIndex();
		//moveSelectorSprite(mSelector.index);
		break;
	case INPUT_DOWN:
		success = increaseSelectorIndex();
		//moveSelectorSprite(mSelector.index);
		break;
	case INPUT_ACCEPT:
		mItemSelected = mSelector.index;
		break;
	case INPUT_CANCEL:
		mItemSelected = mDefaultActionWhenCancel;
		break;
	}
	return success;
}

// Actualiza la logica del menu
void Menu::update()
{
	updateSelector();
}

// Pinta el menu en pantalla
void Menu::render()
{
	// Rendereritza el fondo del menu
	if (mBackgroundType == MENU_BACKGROUND_SOLID)
	{
		SDL_SetRenderDrawColor(mRenderer, mRectBG.r, mRectBG.g, mRectBG.b, mRectBG.a);
		SDL_RenderFillRect(mRenderer, &mRectBG.rect);
	}

	// Renderiza el rectangulo del selector
	SDL_SetRenderDrawColor(mRenderer, mSelector.r, mSelector.g, mSelector.b, mSelector.a);
	SDL_RenderFillRect(mRenderer, &mSelector.rect);

	// Renderiza el sprite del selector
	//mSelectorSprite.render();

	// Renderitza el text
	for (Uint8 i = 0; i < mTotalItems; i++)
	{
		if (i == mSelector.index)
		{
			mText->writeColored(mItem[i].x, mItem[i].y, mItem[i].label, mSelector.itemR, mSelector.itemG, mSelector.itemB);
		}
		else
		{
			mText->write(mItem[i].x, mItem[i].y, mItem[i].label, 0);
		}
	}
}

// Establece el rectangulo de fondo del menu y el selector
void Menu::setRectSize()
{
	Uint8 i = 0;
	mRectBG.rect.w = 0;
	mRectBG.rect.h = 0;
	mSelector.rect.w = 0;
	mSelector.rect.h = 0;

	// Obtenemos la anchura del item mas ancho y la altura de la suma de alturas de los items
	for (i = 0; i < mTotalItems; i++)
	{
		if (mItem[i].w > mRectBG.rect.w)
		{
			mRectBG.rect.w = mItem[i].w;
		}
		mRectBG.rect.h += mItem[i].h + mItem[i].hPaddingDown;
	}

	// La anchura de la cadena más larga, mas un caracter, mas la anchura del sprite del selector
	mRectBG.rect.w += (mText->getSize() * 1); // + mSelectorSprite.getWidth();

	// La altura de la suma de los items mas un caracter y menos un pixel (porque el texto en realidad es de 7 pixeles)
	mRectBG.rect.h += (mText->getSize() * 1) - 1;

	// La posición X es la del menú menos la anchura del sprite del selector y menos medio caracter
	mRectBG.rect.x = mPosX - (mText->getSize() / 2); // - mSelectorSprite.getWidth();

	// La posición Y es la del menu menos la altura de medio caracter i el padding
	mRectBG.rect.y = mPosY - (mText->getSize() / 2) - mVerticalPadding;

	// Establecemos los valores del rectangulo del selector a partir de los valores del rectangulo de fondo
	mSelector.rect.h = (mText->getSize() * 1) + 1;
	mSelector.rect.w = mRectBG.rect.w;
	mSelector.rect.x = mRectBG.rect.x;
}

// Establece el valor de la variable
void Menu::setTotalItems(int num)
{
	mTotalItems = num;
}

// Establece el color del rectangulo de fondo
void Menu::setBackgroundColor(int r, int g, int b, int alpha)
{
	mRectBG.r = r;
	mRectBG.g = g;
	mRectBG.b = b;
	mRectBG.a = alpha;
}

// Establece el color del rectangulo del selector
void Menu::setSelectorColor(int r, int g, int b, int alpha)
{
	mSelector.r = r;
	mSelector.g = g;
	mSelector.b = b;
	mSelector.a = alpha;
}

// Establece el color del texto del selector
void Menu::setSelectorTextColor(int r, int g, int b)
{
	mSelector.itemR = r;
	mSelector.itemG = g;
	mSelector.itemB = b;
}

// Centra el menu respecto un punto
void Menu::centerMenu(int value)
{
	mIsCentered = true;
	mCenter = value;

	// Actualiza el rectangulo de fondo para recalcular las dimensiones
	setRectSize();

	// Obten el acho del menu
	mWidestItem = getWidestItem();

	// Establece la nueva posición centrada en funcion del elemento más ancho
	mPosX = (value) - (mWidestItem / 2);

	// Reposiciona los elementos del menu
	for (Uint8 i = 0; i < 10; i++)
	{
		mItem[i].x = mPosX;
	}

	// Recalcula el rectangulo de fondo
	setRectSize();

	// Recoloca el selector
	mSelector.origin = mSelector.target = mSelector.y = mItem[mSelector.index].y;
	mSelector.moving = false;
	//moveSelectorSprite(mSelector.index);
}

// Centra los elementos del menu
void Menu::centerMenuElements()
{
	mAreElementsCentered = true;

	for (Uint8 i = 0; i < mTotalItems; i++)
	{
		//mItem[i].x = (mCenter - ((mText->lenght(mItem[i].label, 0)) / 2));
		mItem[i].x = (mCenter - (mItem[i].w / 2));
	}

	// Mueve el grafico del selector al elemento seleccionado
	//moveSelectorSprite(mSelector.index);
}

// Añade un item al menu
void Menu::addItem(std::string text, const Uint8 hPaddingUp, const Uint8 hPaddingDown)
{
	// Si es el primer item coge la posición y del propio menu
	if (mTotalItems == 0)
	{
		mItem[mTotalItems].label = text;
		mItem[mTotalItems].w = mText->lenght(mItem[mTotalItems].label, 0);
		mItem[mTotalItems].h = mText->getSize() + (mVerticalPadding * 2);
		mItem[mTotalItems].x = mPosX;
		mItem[mTotalItems].y = mPosY;
		mItem[mTotalItems].hPaddingUp = hPaddingUp;
		mItem[mTotalItems].hPaddingDown = hPaddingDown;
	}
	else
	{
		// En caso contrario, coge la posición y a partir del elemento anterior
		if (mTotalItems < 10)
		{
			mItem[mTotalItems].label = text;
			mItem[mTotalItems].w = mText->lenght(mItem[mTotalItems].label, 0);
			mItem[mTotalItems].h = mText->getSize() + (mVerticalPadding * 2);
			mItem[mTotalItems].x = mPosX;
			mItem[mTotalItems].y = mItem[mTotalItems - 1].y + mItem[mTotalItems - 1].h + mItem[mTotalItems - 1].hPaddingDown;
			mItem[mTotalItems].hPaddingUp = hPaddingUp;
			mItem[mTotalItems].hPaddingDown = hPaddingDown;
		}
	}
	setTotalItems(mTotalItems + 1);
	reorganize();
	setSelectorPos(0);
}

// Cambia el texto de un item
void Menu::setItemCaption(Uint8 index, std::string text)
{
	mItem[index].label = text;
	mItem[index].w = mText->lenght(mItem[index].label, 0);
	reorganize();
}

// Establece el indice del itemm que se usará por defecto al cancelar el menu
void Menu::setDefaultActionWhenCancel(Uint8 item)
{
	mDefaultActionWhenCancel = item;
}
