/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            project.cc
 *
 *  Sun May  6 11:38:11 CEST 2018
 *  Copyright 2018 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 General Public License as published by
 *  the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU 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 "project.h"

#include <QtGlobal>
#include <QFileInfo>
#include <QDir>

#include <iostream>

AudioFile::AudioFile(Instrument& instrument, int id)
	: id(id)
	, instrument(instrument)
{
}

int AudioFile::getId() const
{
	return id;
}

QString AudioFile::getFile() const
{
	return file;
}

void AudioFile::setFile(const QString& file)
{
	if(this->file == file)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(instrument.getProject());
		this->file = file;
	}
}

QString AudioFile::getAbsoluteFile() const
{
	QFileInfo info(file);
	if(info.isRelative())
	{
		// Prepend root path
		return instrument.getProject().getRawFileRoot() + QDir::separator() + file;
	}

	return file;
}

QString AudioFile::getName() const
{
	return name;
}

void AudioFile::setName(const QString& name)
{
	if(this->name == name)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(instrument.getProject());
		this->name = name;
	}
}

int AudioFile::getChannelMapId() const
{
	return channel_map_id;
}

void AudioFile::setChannelMapId(int channel_map_id)
{
	if(this->channel_map_id == channel_map_id)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(instrument.getProject());
		this->channel_map_id = channel_map_id;
	}
}

bool AudioFile::getMainChannel() const
{
	return main_channel;
}

void AudioFile::setMainChannel(bool main_channel)
{
	if(this->main_channel == main_channel)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(instrument.getProject());
		this->main_channel = main_channel;
	}
}

Instrument::Instrument(Project& project, int id)
	: id(id)
	, project(project)
{
}

int Instrument::getId() const
{
	return id;
}

QString Instrument::getInstrumentName() const
{
	return instrument_name;
}

void Instrument::setInstrumentName(const QString& instrument_name)
{
	if(this->instrument_name == instrument_name)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->instrument_name = instrument_name;
		if(prefix.isEmpty())
		{
			setPrefix(instrument_name); // replicate name to prefix
		}
	}
}

QString Instrument::getMasterFile() const
{
	return master_file;
}

void Instrument::setMasterFile(const QString& master_file)
{
	if(this->master_file == master_file)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->master_file = master_file;
	}
}

AudioFile& Instrument::getAudioFile(int id)
{
	for(auto& audio_file : audio_files)
	{
		if(audio_file.getId() == id)
		{
			return audio_file;
		}
	}

	Q_ASSERT(false); // No such audio_file id.
}

int Instrument::createAudioFile()
{
	Project::RAIIBulkUpdate bulkUpdate(project);

	audio_files.emplace_back(*this, project.next_id);
	++project.next_id;

	return audio_files.back().getId();
}

void Instrument::deleteAudioFile(int id)
{
	Project::RAIIBulkUpdate bulkUpdate(project);

	for(auto it = audio_files.begin(); it != audio_files.end(); ++it)
	{
		if((*it).getId() == id)
		{
			audio_files.erase(it);
			return;
		}
	}

	Q_ASSERT(false); // No such audio_file id.
}

QList<int> Instrument::getAudioFileList() const
{
	QList<int> audio_file_list;
	for(auto& audio_file : audio_files)
	{
		audio_file_list.push_back(audio_file.getId());
	}
	return audio_file_list;
}

std::size_t Instrument::getAttackLength() const
{
	return attack_length;
}

void Instrument::setAttackLength(std::size_t attack_length)
{
	if(this->attack_length == attack_length)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->attack_length = attack_length;
	}
}

std::size_t Instrument::getPowerSpread() const
{
	return power_spread;
}

void Instrument::setPowerSpread(std::size_t power_spread)
{
	if(this->power_spread == power_spread)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->power_spread = power_spread;
	}
}

std::size_t Instrument::getMinimumSize() const
{
	return minimum_size;
}

void Instrument::setMinimumSize(std::size_t minimum_size)
{
	if(this->minimum_size == minimum_size)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->minimum_size = minimum_size;
	}
}

std::size_t Instrument::getFalloff() const
{
	return falloff;
}

void Instrument::setFalloff(std::size_t falloff)
{
	if(this->falloff == falloff)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->falloff = falloff;
	}
}

std::size_t Instrument::getFadeLength() const
{
	return fade_length;
}

void Instrument::setFadeLength(std::size_t fade_length)
{
	if(this->fade_length == fade_length)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->fade_length = fade_length;
	}
}

float Instrument::getThreshold() const
{
	return threshold;
}

void Instrument::setThreshold(float threshold)
{
	if(this->threshold == threshold)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->threshold = threshold;
	}
}

Selections Instrument::getSelections() const
{
	return selections;
}

void Instrument::setSelections(const Selections& selections)
{
	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->selections = selections;
	}
}

QString Instrument::getPrefix() const
{
	return prefix;
}

void Instrument::setPrefix(const QString& prefix)
{
	if(this->prefix == prefix)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->prefix = prefix;
	}
}

Project& Instrument::getProject()
{
	return project;
}

Channel::Channel(Project& project, int id)
	: id(id)
	, project(project)
{
}

int Channel::getId() const
{
	return id;
}

QString Channel::getChannelName() const
{
	return channel_name;
}

void Channel::setChannelName(const QString& channel_name)
{
	if(this->channel_name == channel_name)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(project);
		this->channel_name = channel_name;
	}
}

Project& Channel::getProject()
{
	return project;
}


void Project::bulkUpdateBegin()
{
	++update_count;
}

void Project::bulkUpdateEnd()
{
	Q_ASSERT(update_count);
	--update_count;
	if(update_count == 0)
	{
		emit projectChanged();
	}
}


QString Project::getProjectFile() const
{
	return project_file;
}

void Project::setProjectFile(const QString& project_file)
{
	this->project_file = project_file;
}

QString Project::getProjectName() const
{
	return project_name;
}

void Project::setProjectName(const QString& project_name)
{
	if(this->project_name == project_name)
	{
		return;
	}

	{
		RAIIBulkUpdate bulkUpdate(*this);
		this->project_name = project_name;
	}
}

QString Project::getRawFileRoot() const
{
	return raw_file_root;
}

void Project::setRawFileRoot(const QString& raw_file_root)
{
	if(this->raw_file_root == raw_file_root)
	{
		return;
	}

	{
		RAIIBulkUpdate bulkUpdate(*this);
		this->raw_file_root = raw_file_root;
	}
}

QString Project::getExportPath() const
{
	return export_path;
}

void Project::setExportPath(const QString& export_path)
{
	if(this->export_path == export_path)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(*this);
		this->export_path = export_path;
	}
}

QString Project::getProjectDescription() const
{
	return description;
}

void Project::setProjectDescription(const QString& description)
{
	if(this->description == description)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(*this);
		this->description = description;
	}
}

double Project::getProjectSamplerate() const
{
	return samplerate;
}

void Project::setProjectSamplerate(double samplerate)
{
	if(this->samplerate == samplerate)
	{
		return;
	}

	{
		Project::RAIIBulkUpdate bulkUpdate(*this);
		this->samplerate = samplerate;
	}
}

Instrument& Project::getInstrument(int id)
{
	for(auto& instrument : instruments)
	{
		if(instrument.getId() == id)
		{
			return instrument;
		}
	}

	Q_ASSERT(false); // No such instrument id.
}

int Project::createInstrument()
{
	RAIIBulkUpdate bulkUpdate(*this);

	instruments.emplace_back(*this, next_id);
	++next_id;

	return instruments.back().getId();
}

void Project::deleteInstrument(int id)
{
	RAIIBulkUpdate bulkUpdate(*this);

	for(auto it = instruments.begin(); it != instruments.end(); ++it)
	{
		if((*it).getId() == id)
		{
			instruments.erase(it);
			return;
		}
	}

	Q_ASSERT(false); // No such instrument id.
}

QList<int> Project::getInstrumentList() const
{
	QList<int> instrument_list;
	for(auto& instrument : instruments)
	{
		instrument_list.push_back(instrument.getId());
	}
	return instrument_list;
}

Channel& Project::getChannel(int id)
{
	for(auto& channel : channels)
	{
		if(channel.getId() == id)
		{
			return channel;
		}
	}

	Q_ASSERT(false); // No such channel id.
}

int Project::createChannel()
{
	RAIIBulkUpdate bulkUpdate(*this);

	channels.emplace_back(*this, next_id);
	++next_id;

	return channels.back().getId();
}

void Project::deleteChannel(int id)
{
	RAIIBulkUpdate bulkUpdate(*this);

	for(auto it = channels.begin(); it != channels.end(); ++it)
	{
		if((*it).getId() == id)
		{
			channels.erase(it);
			return;
		}
	}

	Q_ASSERT(false); // No such channel id.
}

QList<int> Project::getChannelList() const
{
	QList<int> channel_list;
	for(auto& channel : channels)
	{
		channel_list.push_back(channel.getId());
	}
	return channel_list;
}

void Project::reset()
{
	RAIIBulkUpdate bulkUpdate(*this);
	setProjectName("");
	setProjectFile("");
	setRawFileRoot("");
	setExportPath("");

	instruments.clear();
	channels.clear();
	next_id = 0;
}
