//	OptionsScreen.cpp - Simple options screen.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#include "sound/GenericFileSounder.h"
#include "gui/HorizontalSlider.h"
#include "TankYouGlobalData.h"
#include "gui/RadioButtons.h"
#include "gui/MouseCursor.h"
#include "drawing/Drawer.h"
#include "OSCDispatcher.h"
#include "OptionsScreen.h"
#include "Application.h"
#include "gui/Button.h"
#include "Random.h"

#include <sstream>

//------------------------------------------------------------------------------
GameStateCreator<OptionsScreen> OptionsScreen::creator;

//------------------------------------------------------------------------------
OptionsScreen::OptionsScreen(Application& app, SounderPtr& sound):
GameState(app, sound),
backgroundColour(0.125f),
backgroundTweener(backgroundColour, 0.125f, 0.2f),
borderColour(0.1875f),
borderTweener(borderColour, 0.1875f, 0.2f),
shake(0.0f),
shakeIndex(0.0f),
lineLeft(1.0f, 1.0f, 0.33f),
lineLeftTweener(lineLeft, ThreeFloats(0.33f, 0.33f, 1.0f), 0.005f),
lineRight(1.0f, 0.33f, 0.33f),
lineRightTweener(lineRight, ThreeFloats(0.33f, 1.0f, 0.33f), 0.005f),
startGame(false),
sounder(dynamic_pointer_cast<GenericFileSounder>(sound))
{
	std::wstring pickupGen;
	std::wstring cmdOSCOutput(L"-1");
	const TwoFloats drawSize(getDrawingAreaSize());

	globalData = dynamic_pointer_cast<TankYouGlobalData>(getGlobalData());

	if(snowflakes.getDecember())
		snowflakes.setNumSnowflakes(globalData->getSnowflakes());

	if(getApplication().getAppSettings().isArgumentPresent(L"oscOutput"))
		cmdOSCOutput = getApplication().getAppSettings().getArgumentValue(L"oscOutput");
	if(cmdOSCOutput != L"-1")
	{
		int tempint;
		std::wstringstream tempstr;
		OSCDispatcher& oscDispatcher = globalData->getOSCDispatcher();

		oscDispatcher.setDestAddress(cmdOSCOutput.substr(0, cmdOSCOutput.find_first_of(L':')));

		tempstr << cmdOSCOutput.substr(cmdOSCOutput.find_first_of(L':')+1);
		tempstr >> tempint;
		oscDispatcher.setDestPort(tempint);

		globalData->setOSCOutput(true);
	}

	//Set up gradient lines.
	linePos[0].push_back(TwoFloats(32.0f+10.0f, 64.0f));
	linePos[0].push_back(TwoFloats(32.0f+10.0f, 64.0f));
	linePos[0].push_back(TwoFloats(32.0f+10.0f, 64.0f));
	linePos[0].push_back(TwoFloats(drawSize.x-(32.0f+12.0f), 64.0f));
	linePos[0].push_back(TwoFloats(drawSize.x-(32.0f+10.0f), 64.0f));
	linePos[0].push_back(TwoFloats(drawSize.x-(32.0f+10.0f), 64.0f));

	linePos[1].push_back(TwoFloats(32.0f+10.0f, drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(32.0f+10.0f, drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(32.0f+10.0f, drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(drawSize.x-(32.0f+12.0f), drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(drawSize.x-(32.0f+10.0f), drawSize.y-32.0f));
	linePos[1].push_back(TwoFloats(drawSize.x-(32.0f+10.0f), drawSize.y-32.0f));

	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	lineSize.push_back(4.0f);
	
	lineCol.push_back(lineLeft);
	lineCol.push_back(lineLeft);
	lineCol.push_back(lineLeft);
	lineCol.push_back(lineRight);
	lineCol.push_back(lineRight);
	lineCol.push_back(lineRight);

	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);
	lineAlpha.push_back(1.0f);

	//Setup widgets.
	screenShakeSlider = new HorizontalSlider(L"OptionsHoriSliderTick");
	screenShakeSlider->setPosition(TwoFloats(65.0f, 101.0f));
	screenShakeSlider->setSize(TwoFloats(254.0f, 32.0f));
	screenShakeSlider->setValue(globalData->getScreenShake());
	screenShakeSlider->addChangeCallback(this);
	widgets.addChild(shared_ptr<Widget>(screenShakeSlider));

	backgroundFlashSlider = new HorizontalSlider(L"OptionsHoriSliderTick");
	backgroundFlashSlider->setPosition(TwoFloats(65.0f, 205.0f));
	backgroundFlashSlider->setSize(TwoFloats(254.0f, 32.0f));
	backgroundFlashSlider->setValue(globalData->getBackgroundFlash());
	backgroundFlashSlider->addChangeCallback(this);
	widgets.addChild(shared_ptr<Widget>(backgroundFlashSlider));

	borderFlashSlider = new HorizontalSlider(L"OptionsHoriSliderTick");
	borderFlashSlider->setPosition(TwoFloats(65.0f, 315.0f));
	borderFlashSlider->setSize(TwoFloats(254.0f, 32.0f));
	borderFlashSlider->setValue(globalData->getBorderFlash());
	borderFlashSlider->addChangeCallback(this);
	widgets.addChild(shared_ptr<Widget>(borderFlashSlider));

	if(snowflakes.getDecember())
	{
		snowflakesSlider = new HorizontalSlider(L"OptionsHoriSliderTick");
		snowflakesSlider->setPosition(TwoFloats(65.0f, 101.0f+316.0f));
		snowflakesSlider->setSize(TwoFloats(254.0f, 32.0f));
		snowflakesSlider->setValue(globalData->getSnowflakes());
		snowflakesSlider->addChangeCallback(this);
		widgets.addChild(shared_ptr<Widget>(snowflakesSlider));
	}

	pickupGenButtons = new RadioButtons(L"OptionsRadioUp",
										L"OptionsRadioDown",
										4);
	pickupGenButtons->setPosition(TwoFloats(404.0f, 128.0f));
	pickupGenButtons->setSize(TwoFloats(44.0f, 176.0f));
	pickupGen = globalData->getPickupGeneratorName();
	if(pickupGen == L"NullGenerator")
		pickupGenButtons->setValue(0);
	else if(pickupGen == L"OriginalGenerator")
		pickupGenButtons->setValue(1);
	else if(pickupGen == L"GradedGenerator")
		pickupGenButtons->setValue(2);
	else if(pickupGen == L"MADGenerator")
		pickupGenButtons->setValue(3);
	pickupGenButtons->addChangeCallback(this);
	widgets.addChild(shared_ptr<Widget>(pickupGenButtons));

	maxPickupsButtons = new RadioButtons(L"OptionsRadioUp",
										 L"OptionsRadioDown",
										 4);
	maxPickupsButtons->setPosition(TwoFloats(404.0f, 372.0f));
	maxPickupsButtons->setSize(TwoFloats(44.0f, 176.0f));
	maxPickupsButtons->setValue(globalData->getMaxPickups()-1);
	maxPickupsButtons->addChangeCallback(this);
	widgets.addChild(shared_ptr<Widget>(maxPickupsButtons));

	numPlayersButtons = new RadioButtons(L"OptionsRadioUp",
										 L"OptionsRadioDown",
										 3);
	numPlayersButtons->setPosition(TwoFloats(814.0f, 128.0f));
	numPlayersButtons->setSize(TwoFloats(44.0f, 132.0f));
	numPlayersButtons->setValue(globalData->getNumPlayers()-2);
	numPlayersButtons->addChangeCallback(this);
	widgets.addChild(shared_ptr<Widget>(numPlayersButtons));

	musicButtons = new RadioButtons(L"OptionsRadioUp",
									L"OptionsRadioDown",
									2);
	musicButtons->setPosition(TwoFloats(814.0f, 320.0f));
	musicButtons->setSize(TwoFloats(44.0f, 88.0f));
	musicButtons->setValue(globalData->getMusic() ? 1 : 0);
	musicButtons->addChangeCallback(this);
	widgets.addChild(shared_ptr<Widget>(musicButtons));

	startButton = new Button(L"OptionsTankYou", L"OptionsTankYou");
	startButton->setPosition(TwoFloats(507.0f, drawSize.y-81.0f-64.0f));
	startButton->setSize(TwoFloats(265.0f, 81.0f));
	startButton->addTriggerCallback(this);
	widgets.addChild(shared_ptr<Widget>(startButton));

	MouseCursor::getInstance().setImage(false);
}

//------------------------------------------------------------------------------
OptionsScreen::~OptionsScreen()
{

}

//------------------------------------------------------------------------------
void OptionsScreen::update()
{
	//Update any tweeners.
	if(!backgroundTweener.getFinished())
		backgroundTweener.tick();
	if(!borderTweener.getFinished())
		borderTweener.tick();
	if(!lineRightTweener.getFinished())
		lineRightTweener.tick();
	else
	{
		ThreeFloats tempCol;

		while((tempCol == 0.0f) || (tempCol == 1.0f) || (tempCol == lineRight))
		{
			tempCol.x = (Random::getGlobal().getRandomFloat() > 0.5f) ? 1.0f : 0.33f;
			tempCol.y = (Random::getGlobal().getRandomFloat() > 0.5f) ? 1.0f : 0.33f;
			tempCol.z = (Random::getGlobal().getRandomFloat() > 0.5f) ? 1.0f : 0.33f;
		}
		lineRightTweener.reset(tempCol);
	}
	if(!lineLeftTweener.getFinished())
		lineLeftTweener.tick();
	else
		lineLeftTweener.reset(lineRight);

	//Update screen shake.
	if(shake > 0.0f)
	{
		shake -= 0.05f;
		shakeIndex += 0.1f;
		if(shakeIndex > 1.0f)
			shakeIndex -= 1.0f;
	}

	if(snowflakes.getDecember())
		snowflakes.update();

	if(startGame)
		nextState(L"GamePlayState");
}

//------------------------------------------------------------------------------
void OptionsScreen::draw(DrawerPtr& d, float timestepInterp)
{
	TwoFloats shakeVal;
	const TwoFloats& drawSize(d->getDrawingAreaSize());

	//--Arena background--------------------------------------------------------
	d->fillRect(0.0f, drawSize, backgroundColour);

	if(shake > 0.0f)
	{
		shakeVal.x = sinf(shakeIndex*2.0f*NIALL_PI) * 16.0f * shake * 0.25f * globalData->getScreenShake();
		d->translate(shakeVal);
		d->rotate(shakeVal.x * 0.001f, TwoFloats(640.0f, 360.0f));
	}

	//--Border background-------------------------------------------------------
	//Top bar.
	d->fillRect(-64.0f, TwoFloats(drawSize.x+128.0f, 64.0f+64.0f), borderColour);
	//Bottom bar.
	d->fillRect(TwoFloats(-64.0f, drawSize.y-32.0f),
				TwoFloats(drawSize.x+128.0f, 32.0f+64.0f),
				borderColour);
	//Left bar.
	d->fillRect(-64.0f, TwoFloats(32.0f+64.0f, drawSize.y+128.0f), borderColour);
	//Right bar.
	d->fillRect(TwoFloats(drawSize.x-32.0f, -64.0f),
				TwoFloats(32.0f+64.0f, drawSize.y+128.0f),
				borderColour);

	//Border line.
	//Top.
	lineCol[0] = lineLeft;
	lineCol[1] = lineLeft;
	lineCol[2] = lineLeft;
	lineCol[3] = lineRight;
	lineCol[4] = lineRight;
	lineCol[5] = lineRight;
	d->drawConnectedLine(L"",
						 linePos[0],
						 lineSize,
						 lineCol,
						 lineAlpha);
	//Bottom.
	d->drawConnectedLine(L"",
						 linePos[1],
						 lineSize,
						 lineCol,
						 lineAlpha);
	//Left.
	d->fillRect(TwoFloats(30.0f, 64.0f+10.0f),
				TwoFloats(4.0f, drawSize.y-(64.0f+32.0f+20.0f)),
				lineLeft);
	//Right.
	d->fillRect(TwoFloats(drawSize.x-34.0f, 64.0f+10.0f),
				TwoFloats(4.0f, drawSize.y-(64.0f+32.0f+20.0f)),
				lineRight);

	//Top-left Corner.
	d->drawImage(L"ArenaCornerBack",
				 TwoFloats(32.0f-4.0f, 64.0f-4.0f),
				 1.0f,
				 borderColour);
	//Top-right Corner.
	d->drawImage(L"ArenaCornerBack",
				 TwoFloats(drawSize.x-(32.0f-4.0f), 64.0f-4.0f),
				 TwoFloats(-1.0f, 1.0f),
				 borderColour);
	//Bottom-left Corner.
	d->drawImage(L"ArenaCornerBack",
				 TwoFloats(32.0f-4.0f, drawSize.y-(32.0f-4.0f)),
				 TwoFloats(1.0f, -1.0f),
				 borderColour);
	//Bottom-right Corner.
	d->drawImage(L"ArenaCornerBack",
				 TwoFloats(drawSize.x-(32.0f-4.0f), drawSize.y-(32.0f-4.0f)),
				 TwoFloats(-1.0f, -1.0f),
				 borderColour);

	//Top-left Corner.
	d->drawImage(L"ArenaCornerLine",
				 TwoFloats(32.0f-2.0f, 64.0f-2.0f),
				 1.0f,
				 lineLeft);
	//Top-right Corner.
	d->drawImage(L"ArenaCornerLine",
				 TwoFloats(drawSize.x-(32.0f-2.0f), 64.0f-2.0f),
				 TwoFloats(-1.0f, 1.0f),
				 lineRight);
	//Bottom-left Corner.
	d->drawImage(L"ArenaCornerLine",
				 TwoFloats(32.0f-2.0f, drawSize.y-(32.0f-2.0f)),
				 TwoFloats(1.0f, -1.0f),
				 lineLeft);
	//Bottom-right Corner.
	d->drawImage(L"ArenaCornerLine",
				 TwoFloats(drawSize.x-(32.0f-2.0f), drawSize.y-(32.0f-2.0f)),
				 TwoFloats(-1.0f, -1.0f),
				 lineRight);

	//--Draw background images--------------------------------------------------
	d->drawImage(L"OptionsTitle",
				 TwoFloats(36.0f, 10.0f),
				 1.0f,
				 lineRight);
	d->drawImage(L"OptionsSlidersBack", TwoFloats(64.0f, 112.0f));
	if(snowflakes.getDecember())
		d->drawImage(L"OptionsSnowflakesBack", TwoFloats(64.0f, 112.0f+316.0f));
	d->drawImage(L"OptionsPickupGenBack", TwoFloats(400.0f, 88.0f));
	d->drawImage(L"OptionsMaxPickupsBack", TwoFloats(400.0f, 332.0f));
	d->drawImage(L"OptionsNumPlayersBack", TwoFloats(810.0f, 88.0f));
	d->drawImage(L"OptionsMusicBack", TwoFloats(810.0f, 280.0f));

	//--Draw widgets------------------------------------------------------------
	widgets.draw(d);
	MouseCursor::getInstance().draw(d);

	//--Reset screen shake------------------------------------------------------
	if(shake > 0.0f)
	{
		shakeVal.x = -shakeVal.x;
		d->translate(shakeVal);
		d->rotate(-shakeVal.x * 0.001f, TwoFloats(640.0f, 360.0f));
	}

	if(snowflakes.getDecember())
		snowflakes.draw(d);
}

//------------------------------------------------------------------------------
void OptionsScreen::keyDown(const std::wstring& action,
							const std::wstring& keyPressed,
							int keyModifier)
{
	if(action == L"Escape")
		quit();
}

//------------------------------------------------------------------------------
void OptionsScreen::mouseDown(int button, const TwoFloats& pos)
{
	MouseCursor::getInstance().mouseButton(true, pos);
	widgets.mouseButton(true, pos);
}

//------------------------------------------------------------------------------
void OptionsScreen::mouseUp(int button, const TwoFloats& pos)
{
	MouseCursor::getInstance().mouseButton(false, pos);
	widgets.mouseButton(false, pos);
}

//------------------------------------------------------------------------------
void OptionsScreen::mouseMove(const TwoFloats& pos)
{
	MouseCursor::getInstance().mouseMove(pos);
	widgets.mouseMove(pos);
}

//------------------------------------------------------------------------------
void OptionsScreen::valueChanged(Widget *w, float val, const std::wstring& id)
{
	if(w == screenShakeSlider)
		globalData->setScreenShake(val);
	else if(w == backgroundFlashSlider)
		globalData->setBackgroundFlash(val);
	else if(w == borderFlashSlider)
		globalData->setBorderFlash(val);
	else if(w == snowflakesSlider)
	{
		snowflakes.setNumSnowflakes(val);
		globalData->setSnowflakes(val);
	}
	else if(w == pickupGenButtons)
	{
		switch(pickupGenButtons->getValue())
		{
			case 0:
				globalData->setPickupGenerator(L"NullGenerator");
				break;
			case 1:
				globalData->setPickupGenerator(L"OriginalGenerator");
				break;
			case 2:
				globalData->setPickupGenerator(L"GradedGenerator");
				break;
			case 3:
				globalData->setPickupGenerator(L"MADGenerator");
				break;
		}
	}
	else if(w == maxPickupsButtons)
		globalData->setMaxPickups(maxPickupsButtons->getValue()+1);
	else if(w == numPlayersButtons)
		globalData->setNumPlayers(numPlayersButtons->getValue()+2);
	else if(w == musicButtons)
	{
		globalData->setMusic(musicButtons->getValue() > 0);
		if(!musicButtons->getValue() && sounder->getMusicIsPlaying())
			sounder->stopMusic();
	}
}

//------------------------------------------------------------------------------
void OptionsScreen::finishedChanging(Widget *w)
{
	int colourIndex;
	const float colInc = 1.0f/4.0f;
	const ThreeFloats lastBack(backgroundColour);
	const ThreeFloats lastBorder(borderColour);

	if(w == snowflakesSlider)
		return;

	colourIndex = Random::getGlobal().getRandomInt(3);
	switch(colourIndex)
	{
		case 0:
			backgroundColour.x += colInc;
			borderColour.y += colInc;
			borderColour.z += colInc;
			break;
		case 1:
			backgroundColour.y += colInc;
			borderColour.x += colInc;
			borderColour.z += colInc;
			break;
		case 2:
			backgroundColour.z += colInc;
			borderColour.x += colInc;
			borderColour.y += colInc;
			break;
		default:
			backgroundColour.x += colInc;
			borderColour.y += colInc;
			borderColour.z += colInc;
	}
	backgroundColour = lastBack + (globalData->getBackgroundFlash() * (backgroundColour-lastBack));
	borderColour = lastBorder + (globalData->getBorderFlash() * (borderColour-lastBorder));

	backgroundTweener.reset(ThreeFloats(0.125f));
	borderTweener.reset(ThreeFloats(0.1875f));

	shake += 1.0f;
	if(shake > 2.0f)
		shake = 2.0f;
}

//------------------------------------------------------------------------------
void OptionsScreen::triggered(Widget *w, bool val, const std::wstring& id)
{
	startGame = true;
	MouseCursor::getInstance().mouseButton(false,
										   MouseCursor::getInstance().getPosition());
}
