//	PitchSetEditor.cpp - Game state used to edit pitch sets.
//  ----------------------------------------------------------------------------
//	This file is part of 'GameTemplate', template code for a game.
//	Copyright (C) 2011  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 "gui/MouseCursor.h"
#include "drawing/Drawer.h"
#include "PitchSetEditor.h"
#include "TonesSounder.h"
#include "gui/Button.h"
#include "FilePath.h"
#include "UTF8File.h"

using std::wstringstream;
using std::ofstream;
using std::wstring;

//------------------------------------------------------------------------------
const float pitches[36] = {	523.2511306012f,
							554.3652619537f,
							587.3295358348f,
							622.2539674442f,
							659.2551138257f,
							698.4564628660f,
							739.9888454233f,
							783.9908719635f,
							830.6093951599f,
							880.0000000000f,
							932.3275230362f,
							987.7666025122f,
							1046.5022612024f,
							1108.7305239075f,
							1174.6590716696f,
							1244.5079348883f,
							1318.5102276515f,
							1396.9129257320f,
							1479.9776908465f,
							1567.9817439270f,
							1661.2187903198f,
							1760.0000000000f,
							1864.6550460724f,
							1975.5332050245f,
							2093.0045224048f,
							2217.4610478150f,
							2349.3181433393f,
							2489.0158697766f,
							2637.0204553030f,
							2793.8258514640f,
							2959.9553816931f,
							3135.9634878540f,
							3322.4375806396f,
							3520.0000000000f,
							3729.3100921447f,
							3951.0664100490f };

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

//------------------------------------------------------------------------------
PitchSetEditor::PitchSetEditor(Application& app, SounderPtr& sound):
GameState(app, sound),
enteringFilename(false),
sounder(dynamic_pointer_cast<TonesSounder>(sound))
{
	int i, j;
	TwoFloats tempPos;
	const int blacks[15] = {1,3,6,8,10, 13,15,18,20,22, 25,27,30,32,34};
	const int whites[21] = {0,2,4,5,7,9,11, 12,14,16,17,19,21,23, 24,26,28,29,31,33,35};

	buttonContainer.setSize(getDrawingAreaSize());

	for(i=0;i<21;++i)
		notes[whites[i]] = new Button(L"editorNoteWhite", L"editorNoteWhite");

	for(i=0;i<15;++i)
		notes[blacks[i]] = new Button(L"editorNoteBlack", L"editorNoteBlack");

	tempPos.x = 32.0f;
	for(j=0;j<3;++j)
	{
		for(i=0;i<12;++i)
		{
			tempPos.y = 256.0f;
			notes[(j*12)+i]->setPosition(tempPos);
			notes[(j*12)+i]->addTriggerCallback(this);
			buttonContainer.addChild(shared_ptr<Widget>(notes[(j*12)+i]));

			selectedNotes[i] = false;

			tempPos.y += 72.0f;
			hiListen[(j*12)+i] = new Button(L"editorNoteFast", L"editorNoteFast");
			hiListen[(j*12)+i]->setPosition(tempPos);
			hiListen[(j*12)+i]->addTriggerCallback(this);
			buttonContainer.addChild(shared_ptr<Widget>(hiListen[(j*12)+i]));
		
			tempPos.y += 40.0f;
			medListen[(j*12)+i] = new Button(L"editorNoteMed", L"editorNoteMed");
			medListen[(j*12)+i]->setPosition(tempPos);
			medListen[(j*12)+i]->addTriggerCallback(this);
			buttonContainer.addChild(shared_ptr<Widget>(medListen[(j*12)+i]));
		
			tempPos.y += 40.0f;
			loListen[(j*12)+i] = new Button(L"editorNoteSlow", L"editorNoteSlow");
			loListen[(j*12)+i]->setPosition(tempPos);
			loListen[(j*12)+i]->addTriggerCallback(this);
			buttonContainer.addChild(shared_ptr<Widget>(loListen[(j*12)+i]));

			tempPos.x += 32.0f;
		}

		tempPos.x += 32.0f;
	}
}

//------------------------------------------------------------------------------
PitchSetEditor::~PitchSetEditor()
{

}

//------------------------------------------------------------------------------
void PitchSetEditor::draw(DrawerPtr& d, float timestepInterp)
{
	int i;
	const TwoFloats origin;
	const ThreeFloats white(1.0f);

	for(i=(synthVoices.size()-1);i>=0;--i)
	{
		--synthVoices[i].counter;

		if(synthVoices[i].counter <= 0)
		{
			sounder->noteOff(synthVoices[i].index, false);
			synthVoices.erase(synthVoices.begin()+i);
		}
	}

	d->fillRect(origin, d->getDrawingAreaSize(), white);

	if(enteringFilename)
	{
		d->drawText(L"Enter filename:",
					L"Font1",
					TwoFloats(128.0f),
					256.0f,
					TextLayout::Left,
					1.0f,
					ThreeFloats(0.5f));
		d->drawText(filename.str(),
					L"Font1",
					TwoFloats(385.0f),
					256.0f,
					TextLayout::Left,
					1.0f,
					ThreeFloats(0.5f));
	}

	buttonContainer.draw(d);

	for(i=0;i<NumNotes;++i)
	{
		if(selectedNotes[i])
			d->drawImage(L"editorNoteSelector", notes[i]->getPosition());
	}

	MouseCursor::getInstance().draw(d);
}

//------------------------------------------------------------------------------
void PitchSetEditor::keyDown(const std::wstring& action,
							 const std::wstring& keyPressed,
							 int keyModifier)
{
	if(action == L"Escape")
		nextState(L"TitleScreen");
	else if((!enteringFilename) && (keyModifier == 1) && (keyPressed == L"s"))
		enteringFilename = true;
	else if(enteringFilename)
	{
		if(keyPressed == L"Return")
		{
			int i, j;
			wstringstream tempstr;

			tempstr << filename.str() << L".txt";

			FilePath pitchFile = FilePath::getDataFilesDir().getChild(L"pitches").getChild(tempstr.str());
			UTF8File outFile(pitchFile);

			for(i=(NumNotes-1),j=0;i>=0;--i)
			{
				if(selectedNotes[i])
				{
					tempstr.clear();
					tempstr.str(L"");
					tempstr << pitches[i];

					++j;
					if(j >= 8) //So we only store 8 pitches.
					{
						outFile.writeText(tempstr.str());
						break;
					}
					else
					{
						tempstr << std::endl;
						outFile.writeText(tempstr.str());
					}
				}
			}

			enteringFilename = false;
		}
		else if(keyPressed == L"Backspace")
		{
			wstring tempstr = filename.str();

			filename.clear();
			filename.str(tempstr.substr(0, tempstr.length()-1));
		}
		else
			filename << keyPressed;
	}
}

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

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

//------------------------------------------------------------------------------
void PitchSetEditor::mouseMove(const TwoFloats& pos)
{
	buttonContainer.mouseMoveTest(pos);
	MouseCursor::getInstance().mouseMove(pos);
}

//------------------------------------------------------------------------------
void PitchSetEditor::triggered(Widget *w, bool val, const std::wstring& id)
{
	int i;

	if(val)
	{
		for(i=0;i<NumNotes;++i)
		{
			if(w == notes[i])
			{
				selectedNotes[i] = !selectedNotes[i];
				if(selectedNotes[i])
				{
					SynthVoice voice;

					voice.index = sounder->noteOn(pitches[i]*0.5f);
					voice.counter = 160;

					synthVoices.push_back(voice);
				}
				break;
			}
			else if(w == hiListen[i])
			{
				SynthVoice voice;

				voice.index = sounder->noteOn(pitches[i]);
				voice.counter = 160;

				synthVoices.push_back(voice);
			}
			else if(w == medListen[i])
			{
				SynthVoice voice;

				voice.index = sounder->noteOn(pitches[i]*0.5f);
				voice.counter = 160;

				synthVoices.push_back(voice);
			}
			else if(w == loListen[i])
			{
				SynthVoice voice;

				voice.index = sounder->noteOn(pitches[i]*0.25f);
				voice.counter = 160;

				synthVoices.push_back(voice);
			}
		}
	}
}
