/////////////////////////////////////////////////////////////////////////////
// Name:
// Purpose:		instrselect.cpp
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Modified by:
// Created:
// 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/>.
/////////////////////////////////////////////////////////////////////////////

#include "instrselect.h"
#include "spcore/language.h"
#include "spcore/conversion.h"

#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>

using namespace spcore;
using namespace boost;
using namespace std;

namespace mod_score_player {

#define GETTEXT_DOMAIN "sitplus-mod_score_player"

static const char* g_instruments[]= {
	N__("Acoustic Grand Piano"),
	N__("Bright Acoustic Piano"),
	N__("Electric Grand Piano"),
	N__("Honky-Tonk"),
	N__("Electric Piano 1"),
	N__("Electric Piano 2"),
	N__("Harpsichord"),
	N__("Clavinet"),
	N__("Celesta"),
	N__("Glockenspiel"),
	N__("Music Box"),
	N__("Vibraphone"),
	N__("Marimba"),
	N__("Xylophone"),
	N__("Tubular Bells"),
	N__("Dulcimer"),
	N__("Drawbar Organ"),
	N__("Percussive Organ"),
	N__("Rock Organ"),
	N__("Church Organ"),
	N__("Reed Organ"),
	N__("Accordion"),
	N__("Harmonica"),
	N__("Tango Accordion"),
	N__("Nylon String Guitar"),
	N__("Steel String Guitar"),
	N__("Electric Jazz Guitar"),
	N__("Electric Clean Guitar"),
	N__("Electric Muted Guitar"),
	N__("Overdriven Guitar"),
	N__("Distortion Guitar"),
	N__("Guitar Harmonics"),
	N__("Acoustic Bass"),
	N__("Electric Bass(finger)"),
	N__("Electric Bass(pick)"),
	N__("Fretless Bass"),
	N__("Slap Bass 1"),
	N__("Slap Bass 2"),
	N__("Synth Bass 1"),
	N__("Synth Bass 2"),
	N__("Violin"),
	N__("Viola"),
	N__("Cello"),
	N__("Contrabass"),
	N__("Tremolo Strings"),
	N__("Pizzicato Strings"),
	N__("Orchestral Strings"),
	N__("Timpani"),
	N__("String Ensemble 1"),
	N__("String Ensemble 2"),
	N__("SynthStrings 1"),
	N__("SynthStrings 2"),
	N__("Choir Aahs"),
	N__("Voice Oohs"),
	N__("Synth Voice"),
	N__("Orchestra Hit"),
	N__("Trumpet"),
	N__("Trombone"),
	N__("Tuba"),
	N__("Muted Trumpet"),
	N__("French Horn"),
	N__("Brass Section"),
	N__("SynthBrass 1"),
	N__("SynthBrass 2"),
	N__("Soprano Sax"),
	N__("Alto Sax"),
	N__("Tenor Sax"),
	N__("Baritone Sax"),
	N__("Oboe"),
	N__("English Horn"),
	N__("Bassoon"),
	N__("Clarinet"),
	N__("Piccolo"),
	N__("Flute"),
	N__("Recorder"),
	N__("Pan Flute"),
	N__("Blown Bottle"),
	N__("Shakuhachi"),
	N__("Whistle"),
	N__("Ocarina"),
	N__("Square Wave"),
	N__("Saw Wave"),
	N__("Syn. Calliope"),
	N__("Chiffer Lead"),
	N__("Charang"),
	N__("Solo Vox"),
	N__("5th Saw Wave"),
	N__("Bass& Lead"),
	N__("Fantasia"),
	N__("Warm Pad"),
	N__("Polysynth"),
	N__("Space Voice"),
	N__("Bowed Glass"),
	N__("Metal Pad"),
	N__("Halo Pad"),
	N__("Sweep Pad"),
	N__("Ice Rain"),
	N__("Soundtrack"),
	N__("Crystal"),
	N__("Atmosphere"),
	N__("Brightness"),
	N__("Goblin"),
	N__("Echo Drops"),
	N__("Star Theme"),
	N__("Sitar"),
	N__("Banjo"),
	N__("Shamisen"),
	N__("Koto"),
	N__("Kalimba"),
	N__("Bagpipe"),
	N__("Fiddle"),
	N__("Shanai"),
	N__("Tinkle Bell"),
	N__("Agogo"),
	N__("Steel Drums"),
	N__("Woodblock"),
	N__("Taiko Drum"),
	N__("Melodic Tom"),
	N__("Synth Drum"),
	N__("Reverse Cymbal"),
	N__("Guitar Fret Noise"),
	N__("Breath Noise"),
	N__("Seashore"),
	N__("Bird Tweet"),
	N__("Telephone Ring"),
	N__("Helicopter"),
	N__("Applause"),
	N__("Gunshot")
};

static const unsigned int g_instruments_size= sizeof(g_instruments) / sizeof(g_instruments[0]);

InstrumentSelectorComponent::InstrumentSelectorComponent(const char * name, int argc, const char * argv[])
: CComponentAdapter(name, argc, argv)
, m_instrIdx(0)	
{
	//
	// Pins
	//
	RegisterInputPin (*SmartPtr<IInputPin>(new InputPinInstrument(*this), false));

	m_oPinInstruments= CTypeComposite::CreateOutputPin("instruments");
	m_oPinName= CTypeString::CreateOutputPin("name");
	m_oPinMIDINumber= CTypeInt::CreateOutputPin("midi_number");

	RegisterOutputPin (*m_oPinInstruments);
	RegisterOutputPin (*m_oPinName);
	RegisterOutputPin (*m_oPinMIDINumber);

	// 
	// Parse arguments
	//
	bool appendNumber= false;
	if (argc) {
		// Seach -n option
		for (int i= 0; i< argc; ++i) {
			if (argv[i] && strcmp ("-n", argv[i])== 0) {
				// Append numbers
				appendNumber= true;
				break;
			}
		}

		// Process remaining options
		for (int i= 0; i< argc; ++i) {
			if (argv[i] && strcmp ("-s", argv[i])== 0) {
				// Instrument set
				m_instrumentSet.clear();

				bool err= false;
				
				++i;
				if (i< argc && argv[i]) {
					// Parse instrument list
					char_separator<char> sep(", ");
					string str(argv[i]);
					tokenizer<char_separator<char> > tokens(str, sep);
					BOOST_FOREACH(string t, tokens)	{
						unsigned int instrNum;
						if (!StrToUint(t.c_str(), &instrNum) || instrNum< 1 || instrNum> g_instruments_size) {
							err= true;
							break;
						}
						else
							AddInstrumentToSet(instrNum - 1, appendNumber);
					}
				}
				else err= true;
				if (err) throw std::runtime_error("instrument_selector. Wrong value for option -s");
			}
			else if (argv[i] && strcmp ("-n", argv[i])== 0) {
				// Append numbers

				// ... already done
			}
			else if (argv[i] && strlen(argv[i])) {
				string error_msg("instrument_selector. Unknown option:");
				error_msg+= argv[i];
				throw std::runtime_error(error_msg);
			}
		}
	}

	// Empty instrument set?
	if (m_instrumentSet.empty())
		// Add the full set
		for (unsigned int i= 0; i< g_instruments_size; ++i)
			AddInstrumentToSet(i, appendNumber);
}

void InstrumentSelectorComponent::AddInstrumentToSet(unsigned int num, bool appendNum)
{
	assert (num< 128);

	struct Name_MIDINum nn;
	nn.number= (unsigned char) num;
	if (appendNum) {
		nn.name+= boost::lexical_cast<std::string>(num+1);
		nn.name+= "-";
	}
	nn.name+= __(g_instruments[num]);
	m_instrumentSet.push_back(nn);
}

int InstrumentSelectorComponent::DoInitialize()
{
	assert (m_instrIdx< m_instrumentSet.size());

	//
	// Instruments
	//
	SmartPtr<CTypeComposite> instruments= CTypeComposite::CreateInstance();
	vector<Name_MIDINum>::const_iterator it= m_instrumentSet.begin();
	for (; it!= m_instrumentSet.end(); ++it) {
		SmartPtr<CTypeString> instrument_name= CTypeString::CreateInstance();
		instrument_name->set(it->name.c_str());
		instruments->AddChild(instrument_name);
	}
	m_oPinInstruments->Send(instruments);

	//
	// Selected instrument
	//
	SendNameAndMIDINumber();

	return 0;
}

void InstrumentSelectorComponent::SendNameAndMIDINumber()
{
	// Name
	SmartPtr<CTypeString> name= CTypeString::CreateInstance();
	name->set(m_instrumentSet[m_instrIdx].name.c_str());
	m_oPinName->Send(name);

	// MIDI number (0..127)
	SmartPtr<CTypeInt> number= CTypeInt::CreateInstance();
	number->setValue(m_instrumentSet[m_instrIdx].number);
	m_oPinMIDINumber->Send(number);
}

void InstrumentSelectorComponent::OnPinInstrument (const CTypeInt & msg)
{
	unsigned char newInstr= (unsigned char) msg.getValue();

	if (newInstr<= m_instrumentSet.size() && newInstr!= m_instrIdx) {
		m_instrIdx= newInstr;

		SendNameAndMIDINumber();
	}
}


};	// namespace end
