//	GamePlayState.h - Basic GameState which does everything via Lua.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2013  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/>.
//	----------------------------------------------------------------------------

#ifndef GAMEPLAYSTATE_H_
#define GAMEPLAYSTATE_H_

#include "NiallsSocketLib/UDPSocket.h"
#include "NiallsOSCLib/OSCMessage.h"
#include "CriticalSection.h"
#include "LuaDrawer.h"
#include "GameState.h"
#include "LogFile.h"
#include "Tweener.h"
#include "Thread.h"

extern "C" {
  #include "lua.h"
  #include "lualib.h"
  #include "lauxlib.h"
}

#include <queue>
#include <map>

class EXCOSCSounder;

///	Basic GameState which does everything via Lua.
class GamePlayState : public GameState,
					  public Thread
{
  public:
	///	Constructor.
	GamePlayState(Application& app, shared_ptr<Sounder>& sound);
	///	Destructor.
	~GamePlayState();

	///	Updates the state.
	void update();
	///	Draws the state.
	void draw(shared_ptr<Drawer>& d, float timestepInterp);

	///	Called when a key down event is received.
	void keyDown(const std::wstring& action,
				 const std::wstring& keyPressed,
				 int keyModifier);
	///	Called when a key up event is received.
	void keyUp(const std::wstring& action,
			   const std::wstring& keyPressed,
			   int keyModifier);

	///	Called when a mouse down event is received.
	void mouseDown(int button, const TwoFloats& pos);
	///	Called when a mouse up event is received.
	void mouseUp(int button, const TwoFloats& pos);
	///	Called when a mouse move event is received.
	void mouseMove(const TwoFloats& pos);
	///	Called when a mouse wheel event is received.
	void mouseWheel(bool dir);

	///	Called when a joystick axis changes.
	void joyMove(const std::wstring& action,
				 float value,
				 int joystick,
				 int axis);

	///	Thread which listens for OSC messages.
	/*!
		\return Set this to true if the thread completed successfully, false
		otherwise.
	 */
	bool run();

	///	Starts a sound file playing as a faded-in/out loop.
	void playMusic(const std::string& sound);
	///	Stops the music playing.
	void stopMusic();
	///	Triggers a sound to play as a one-shot.
	void triggerSound(const std::string& sound, float level, float pan);
	///	Triggers a sound to play in a loop.
	int triggerLoop(const std::string& sound, float level, float pan, bool fade);
	///	Stops the indexed sound playing.
	void stopLoop(int index);

	///	Toggles the granulator.
	void toggleGranulator(bool val);
	///	Sets the granulator's parameters.
	void setGranulatorParams(float density,
							 float duration,
							 float pitch,
							 float feedback,
							 float mix,
							 float level);
	///	Turns the delay on and off.
	void toggleDelay(bool val);
	///	Sets the delay's parameters.
	void setDelayParams(float delayTime, float feedback, float mix);
	///	Turns the distortion on and off.
	void toggleDistortion(bool val);
	///	Sets the distortion's gain.
	void setDistortionGain(float val);

	///	Sends an OSC message with a single float value.
	void sendOscMessage(const std::string& address, float value);

	///	Returns the size of the drawing area.
	const TwoFloats getDrawingAreaSize() const;

	///	Returns the name of this state.
	const std::wstring getName() const {return L"GamePlayState";};
	///	Used to construct instances of this state.
	static GameStateCreator<GamePlayState> creator;
  private:
	///	Helper method. Handles lua errors.
	void handleError(const std::string& err);

	///	Helper method. Handles any OSC messages received on the listen socket.
	void handleOscMessage(const OSC::Message& message);

	///	Helper method. Checks if the passed-in action has already been converted, converts it if not.
	inline const std::string& getNarrowAction(const std::wstring& action)
	{
		std::map<std::wstring, std::string>::iterator it;

		it = cachedActions.find(action);
		if(it == cachedActions.end())
		{
			std::string tempstr;

			UTF8File::wstringToChar(action, tempstr);
			cachedActions.insert(std::make_pair(action, tempstr));
			it = cachedActions.find(action);
		}

		return it->second;
	};

	///	Our lua state.
	lua_State *luaState;
	///	The drawer wrapper we pass to the lua script.
	LuaDrawer luaDrawer;

	///	Used to fade in/out on game start/end.
	float fade;
	///	Used to fade in/out on game start/end.
	STweener<float> fadeTween;

	///	Used to log any lua errors.
	LogFile logFile;
	///	True if a lua error occurred.
	bool luaError;
	///	The error message from the last error.
	std::string lastError;

	///	We cache the wide->narrow char actions we receive so we don't have to convert them every time.
	std::map<std::wstring, std::string> cachedActions;

	///	The socket we listen on.
	UDPSocket listenSocket;
	///	The socket we transmit on.
	UDPSocket sendSocket;
	///	Used to stop the OSC listen thread.
	bool timeToStop;
	///	Used to wait until the thread has finished before we quit the app.
	bool threadHasFinished;

	///	Queue holding any pending OSC Messages.
	std::queue<OSC::Message> messageQueue;
	///	Critical section guarding the message queue.
	CriticalSection critSec;

	///	Our copy of the sounder.
	shared_ptr<EXCOSCSounder> sounder;
};

#endif
