/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            instrumentparser.cc
 *
 *  Wed Mar  9 13:22:24 CET 2011
 *  Copyright 2011 Bent Bisballe Nyeng
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *  This file is part of DrumGizmo.
 *
 *  DrumGizmo is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  DrumGizmo 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with DrumGizmo; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include "instrumentparser.h"

#include <string.h>
#include <stdio.h>

#include <hugin.hpp>

#include "cpp11fix.h"
#include "path.h"

#include "nolocale.h"

InstrumentParser::InstrumentParser(Instrument& instrument)
	: instrument(instrument)
{

}

int InstrumentParser::parseFile(const std::string& filename)
{
	path = getPath(filename);

	return SAXParser::parseFile(filename);
}

void InstrumentParser::startTag(const std::string& name, const attr_t& attr)
{
	if(name == "instrument")
	{
		if(attr.find("name") != attr.end())
		{
			instrument._name = attr.at("name");
		}

		if(attr.find("description") != attr.end())
		{
			instrument._description = attr.at("description");
		}

		if(attr.find("version") != attr.end())
		{
			try
			{
				instrument.version = VersionStr(attr.at("version"));
			}
			catch(const char* err)
			{
				ERR(instrparser, "Error parsing version number: %s, using 1.0\n", err);
				instrument.version = VersionStr(1,0,0);
			}
		}
		else
		{
			WARN(instrparser, "Missing version number, assuming 1.0\n");
			instrument.version = VersionStr(1,0,0);
		}
	}

	if(name == "samples")
	{
	}

	if(name == "sample")
	{
		if(attr.find("name") == attr.end())
		{
			ERR(instrparser,"Missing required attribute 'name'.\n");
			return;
		}

		float power;
		if(attr.find("power") == attr.end())
		{
			power = -1;
		}
		else
		{
			power = atof_nol(attr.at("power").c_str());
			DEBUG(instrparser, "Instrument power set to %f\n", power);
		}

		// TODO get rid of new or delete it properly
		sample = new Sample(attr.at("name"), power);
	}

	if(name == "audiofile")
	{
		if(sample == nullptr)
		{
			ERR(instrparser,"Missing Sample!\n");
			return;
		}

		if(attr.find("file") == attr.end())
		{
			ERR(instrparser,"Missing required attribute 'file'.\n");
			return;
		}

		if(attr.find("channel") == attr.end())
		{
			ERR(instrparser,"Missing required attribute 'channel'.\n");
			return;
		}

		int filechannel = 1; // default, override with optional attribute
		if(attr.find("filechannel") != attr.end())
		{
			filechannel = std::stoi(attr.at("filechannel"));
			if(filechannel < 1)
			{
				ERR(instrparser,"Invalid value for attribute 'filechannel'.\n");
				filechannel = 1;
			}
		}

		filechannel = filechannel - 1; // 1-based in file but zero-based internally.

		auto audio_file = std::make_unique<AudioFile>(path + "/" + attr.at("file"), filechannel);

		// note: memory leak! the channels are never released
		// once I replaced this using unique_ptr, the channels were
		// destroyed when the InstrumentParser went out of scope
		// (see drumkitparser.cc, where the InstrumentParser lives in
		// local scope).
		// so.. we cannot replace this using smart ptr until we decided
		// the ownership semantics for instances InstrumentChannel
		InstrumentChannel *instrument_channel =
			new InstrumentChannel(attr.at("channel"));

		channellist.push_back(instrument_channel);
		sample->addAudioFile(instrument_channel, audio_file.get());

		// Transfer audio_file ownership to the instrument.
		instrument.audiofiles.push_back(std::move(audio_file));
	}

	if(name == "velocities")
	{
	}

	if(name == "velocity")
	{
		if(attr.find("lower") == attr.end())
		{
			ERR(instrparser,"Missing required attribute 'lower'.\n");
			return;
		}

		if(attr.find("upper") == attr.end())
		{
			ERR(instrparser,"Missing required attribute 'upper'.\n");
			return;
		}

		lower = atof_nol(attr.at("lower").c_str());
		upper = atof_nol(attr.at("upper").c_str());
	}

	if(name == "sampleref")
	{
		if(attr.find("name") == attr.end())
		{
			ERR(instrparser,"Missing required attribute 'name'.\n");
			return;
		}

		Sample* sample_ref = nullptr;
		for(auto& sample : instrument.samplelist)
		{
			if(sample->name == attr.at("name"))
			{
				sample_ref = sample;
				break;
			}
		}

		if(sample_ref == nullptr)
		{
			ERR(instrparser,"Samplref pointed at non-existing sample.\n");
			return;
		}

		if(instrument.version == VersionStr("1.0"))
		{
			// Old "velocity group" algorithm needs this
			instrument.addSample(lower, upper, sample_ref);
		}
	}
}

void InstrumentParser::endTag(const std::string& name)
{
	if(name == "sample")
	{
		if(sample == nullptr)
		{
			ERR(instrparser,"Missing Sample.\n");
			return;
		}

		instrument.samplelist.push_back(sample);

		sample = nullptr;
	}

	if(name == "instrument") {
		instrument.finalise();
	}
}