/////////////////////////////////////////////////////////////////////////////
// Name:       scoreplayer.h
// Purpose:
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Modified by:
// Created:     26/04/2010 11:05:03
// Copyright:   (C) 2010-11 Cesar Mauri Loba - CREA Software Systems
//
//  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 SCOREPLAYER_H
#define SCOREPLAYER_H

#include "spcore/component.h"
#include "spcore/pinimpl.h"
#include "spcore/basictypes.h"
#include "mod_midi/midi_types.h"

#include <boost/thread/mutex.hpp>

using namespace spcore;
using namespace mod_midi;

namespace mod_score_player {

/** *****************************************************************************
	Component: score_player

	Purpose:
		Given a float value between 0 and 1 selects the corresponding note
		from a selected score and sends to the output

	Input pins:
		pointer (float)
			Value between 0 and 1 which is used to play the note from a score
			Allows to directly play notes from the score.

		progress (float)
			Allows to progress through the notes of the score (and play them) 
			either back or forth. Each time the accumulated progress is 1.0f a 
			new note is played.

		volume (int)
			Notes volume. Range 0..127

		instrument (int)
			Instrument to use. Range 0..127 (standard midi)

		duration (int)
			Note duration in ms

		score (string)
			List of notes which conform the score

		wrap (bool)
			Sets whether the score pointer should wrap once reaches 
			the begining or the end of the score.
	
	Output pins:
		note (midi_message): MIDI message with the note to play

		notes_played (int)
			Number of notes played for the last progress message

	Command line:
		[ -c <num> ]	From 0 to 15 (9 percussion). Default: 0.

	Notes:
		If both "pointer" and "progress" receive messages from different
		threads at the same time the behaviour is undefined.
****************************************************************************** */
class ScorePlayerComponent : public spcore::CComponentAdapter {
public:
	static const char* getTypeName() { return "score_player"; };
	virtual const char* GetTypeName() const { return ScorePlayerComponent::getTypeName(); };

	ScorePlayerComponent(const char * name, int argc, const char * argv[]);

	//
	// Types
	//
	enum { POLYPHONY= 4 };
	struct Chord {
		char notes[POLYPHONY];
		Chord() {
			for (int i= 0; i< POLYPHONY; ++i)
				notes[i]= -1;
		}
	};

private:
	//
	// Data members
	//
	unsigned char m_volume;

	unsigned char m_instrument;
	unsigned char m_newInstrument;

	unsigned char m_channel;
	unsigned char m_newChannel;
	unsigned char m_originalChannel;

	bool m_scoreWrap;

	// Note duration
	unsigned int m_duration;

	// Score
	std::vector<Chord> m_score;
	std::vector<Chord> m_newScore;

	// Running chord -1, none
	int m_runningChord;
	// Last played chord
	int m_lastPlayedChord;
	
	// Timestamp which stores when was started the last note
	unsigned long long m_timeStamp;

	// Chord pointer
	float m_chordPointer;

	// Progress pointer
	float m_progressPointer;
	float m_progressFactor;

	SmartPtr<IOutputPin> m_oPinNote;
	SmartPtr<CTypeMIDIMessage> m_note;

	SmartPtr<IOutputPin> m_oPinNotesPlayed;
	SmartPtr<CTypeInt> m_notesPlayed;

	boost::mutex m_mutex;

	//
	// Private methods
	//	
	void OnPinPointer (const CTypeFloat & msg);
	void OnPinProgress (const CTypeFloat & msg);
	void OnPinVolume (const CTypeInt & msg);
	void OnPinInstrument (const CTypeInt & msg);
	void OnPinDuration (const CTypeInt & msg);
	void OnPinScore (const CTypeString & msg);
	void OnPinWrap (const CTypeBool & msg);

	void ProcessPointerUpdate (float pointer);
	int Pointer2Index (float pointer);
	void StopAllNotes();

	//
	// Input pin classes
	//
	class InputPinPointer : public spcore::CInputPinWriteOnly<CTypeFloat, ScorePlayerComponent> {
	public:
		InputPinPointer (ScorePlayerComponent & component)
		: CInputPinWriteOnly<CTypeFloat, ScorePlayerComponent>("pointer", component) { }

		virtual int DoSend(const CTypeFloat & msg) {
			m_component->OnPinPointer(msg);
			return 0;
		}
	};

	class InputPinProgress : public spcore::CInputPinWriteOnly<CTypeFloat, ScorePlayerComponent> {
	public:
		InputPinProgress (ScorePlayerComponent & component)
		: CInputPinWriteOnly<CTypeFloat, ScorePlayerComponent>("progress", component) { }

		virtual int DoSend(const CTypeFloat & msg) {
			m_component->OnPinProgress(msg);
			return 0;
		}
	};

	class InputPinVolume : public spcore::CInputPinWriteOnly<CTypeInt, ScorePlayerComponent> {
	public:
		InputPinVolume (ScorePlayerComponent & component)
		: CInputPinWriteOnly<CTypeInt, ScorePlayerComponent>("volume", component) { }

		virtual int DoSend(const CTypeInt & msg) {
			m_component->OnPinVolume(msg);
			return 0;
		}
	};

	class InputPinInstrument : public spcore::CInputPinWriteOnly<CTypeInt, ScorePlayerComponent> {
	public:
		InputPinInstrument (ScorePlayerComponent & component)
		: CInputPinWriteOnly<CTypeInt, ScorePlayerComponent>("instrument", component) { }

		virtual int DoSend(const CTypeInt & msg) {
			m_component->OnPinInstrument(msg);
			return 0;
		}
	};

	class InputPinDuration : public spcore::CInputPinWriteOnly<CTypeInt, ScorePlayerComponent> {
	public:
		InputPinDuration (ScorePlayerComponent & component)
		: CInputPinWriteOnly<CTypeInt, ScorePlayerComponent>("duration", component) { }

		virtual int DoSend(const CTypeInt & msg) {
			m_component->OnPinDuration(msg);
			return 0;
		}
	};

	class InputPinScore : public spcore::CInputPinWriteOnly<CTypeString, ScorePlayerComponent> {
	public:
		InputPinScore (ScorePlayerComponent & component)
		: CInputPinWriteOnly<CTypeString, ScorePlayerComponent>("score", component) { }

		virtual int DoSend(const CTypeString & msg) {
			m_component->OnPinScore(msg);
			return 0;
		}
	};

	class InputPinWrap : public spcore::CInputPinWriteOnly<CTypeBool, ScorePlayerComponent> {
	public:
		InputPinWrap (ScorePlayerComponent & component)
		: CInputPinWriteOnly<CTypeBool, ScorePlayerComponent>("wrap", component) { }

		virtual int DoSend(const CTypeBool & msg) {
			m_component->OnPinWrap(msg);
			return 0;
		}
	};
};

typedef ComponentFactory<ScorePlayerComponent> ScorePlayerFactory;

};

#endif
