/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            powerlist.cc
 *
 *  Sun Jul 28 19:45:48 CEST 2013
 *  Copyright 2013 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 "powerlist.h"

#include <stdlib.h>

#include <string.h>

#include <hugin.hpp>

// M_PI is not defined in math.h if __STRICT_ANSI__ is defined.
#ifdef __STRICT_ANSI__
#undef __STRICT_ANSI__
#endif
#include <math.h>

/**
 * Minimum sample set size.
 * Smaller means wider 'velocity groups'.
 * Limited by sample set size, ie. only kicks in if sample set size is smaller
 * than this number.
 */
#define MIN_SAMPLE_SET_SIZE 26

// Enable to calculate power on old samples without power attribute
//#define AUTO_CALCULATE_POWER

#define SIZE 500

PowerList::PowerList()
{
	power_max = 0;
	power_min = 100000000;
	lastsample = nullptr;
}

#define THRES 1.0

void PowerList::add(Sample* sample)
{
	PowerListItem item;
	item.power = -1;
	item.sample = sample;

	samples.push_back(item);
}

Channel* PowerList::getMasterChannel()
{
	std::map<Channel*, int> count;

	std::vector<PowerListItem>::iterator si = samples.begin();
	while(si != samples.end())
	{
		PowerListItem& item = *si;
		Sample* sample = item.sample;

		Channel* max_channel = nullptr;
		sample_t max_val = 0;

		// DEBUG(rand, "Sample: %s\n", sample->name.c_str());

		size_t ci = 0;
		AudioFiles::iterator ai = sample->audiofiles.begin();
		while(ai != sample->audiofiles.end())
		{
			Channel* c = ai->first;
			AudioFile* af = ai->second;

			af->load(SIZE);

			float silence = 0;
			size_t silence_length = 4;
			for(size_t s = af->size; s > 0 && s > af->size - silence_length;
			    s--)
			{
				silence += af->data[s];
			}
			silence /= silence_length;

			size_t s = 0;
			for(; s < af->size; s++)
			{
				float val = af->data[s] * af->data[s] * (1.0 / (float)(s + 1));
				if(val > max_val)
				{
					max_val = val;
					max_channel = c;
					break;
				}
			}

			af->unload();

			ai++;
			ci++;
		}

		if(max_channel)
		{
			if(count.find(max_channel) == count.end())
			{
				count[max_channel] = 0;
			}
			count[max_channel]++;
		}

		si++;
	}

	Channel* master = nullptr;
	int max_count = -1;

	std::map<Channel*, int>::iterator ci = count.begin();
	while(ci != count.end())
	{
		if(ci->second > max_count &&
		    strstr(ci->first->name.c_str(), "Alesis") == 0)
		{
			master = ci->first;
			max_count = ci->second;
		}
		ci++;
	}

	return master;
}

void PowerList::finalise()
{
#ifdef AUTO_CALCULATE_POWER
	Channel* master_channel = getMasterChannel();

	if(master_channel == nullptr)
	{
		ERR(rand, "No master channel found!\n");
		return; // This should not happen...
	}

	DEBUG(rand, "Master channel: %s\n", master_channel->name.c_str());
#endif /*AUTO_CALCULATE_POWER*/

	std::vector<PowerListItem>::iterator si = samples.begin();
	while(si != samples.end())
	{
		PowerListItem& item = *si;
		Sample* sample = item.sample;

#ifdef AUTO_CALCULATE_POWER
		DEBUG(rand, "Sample: %s\n", sample->name.c_str());

		AudioFile* master = nullptr;

		AudioFiles::iterator afi = sample->audiofiles.begin();
		while(afi != sample->audiofiles.end())
		{
			if(afi->first->name == master_channel->name)
			{
				master = afi->second;
				break;
			}
			afi++;
		}

		if(master == nullptr)
		{
			si++;
			continue;
		}

		master->load();
#endif /*AUTO_CALCULATE_POWER*/

#ifdef AUTO_CALCULATE_POWER
		if(sample->power == -1)
		{ // Power not defined. Calculate it!
			DEBUG(powerlist, "Calculating power\n");

			float power = 0;
			size_t s = 0;
			for(; s < SIZE && s < master->size; s++)
			{
				power += master->data[s] * master->data[s];
			}

			power = sqrt(power);

			sample->power = power;
		}
#endif /*AUTO_CALCULATE_POWER*/

		item.power = sample->power;

		if(item.power > power_max)
			power_max = item.power;
		if(item.power < power_min)
			power_min = item.power;

		DEBUG(rand, " - power: %f\n", item.power);

		si++;
	}
}

Sample* PowerList::get(level_t level)
{
	int retry = 3; // TODO: This must be user controllable via the UI.

	Sample* sample = nullptr;

	if(!samples.size())
	{
		return nullptr; // No samples to choose from.
	}

	float power_span = power_max - power_min;

	// Width is limited to at least 10. Fioxes problem with instrument with a
	//  sample set smaller than MIN_SAMPLE_SET_SIZE.
	float width = fmax(samples.size(), MIN_SAMPLE_SET_SIZE);

	// Spread out at most ~2 samples away from center if all samples have a
	// uniform distribution over the power spectrum (which they probably don't).
	float stddev = power_span / width;

	// Cut off mean value with stddev/2 in both ends in order to make room for
	//  downwards expansion on velocity 0 and upwards expansion on velocity 1.
	float mean = level * (power_span - stddev) + (stddev / 2.0);

again:
	// Select normal distributed value between
	//  (stddev/2) and (power_span-stddev/2)
	float lvl = rand.normalDistribution(mean, stddev);

	// Adjust this value to be in range
	//  (power_min+stddev/2) and (power_max-stddev/2)
	lvl += power_min;

	DEBUG(rand, "level: %f, lvl: %f (mean: %.2f, stddev: %.2f)\n", level, lvl,
	    mean, stddev);

	float power = 0;
	std::vector<PowerListItem>::iterator i = samples.begin();
	while(i != samples.end())
	{
		if(sample == nullptr)
		{
			sample = i->sample;
			power = i->power;
		}

		if(fabs(i->power - lvl) < fabs(power - lvl))
		{
			sample = i->sample;
			power = i->power;
		}

		i++;
	}

	if(lastsample == sample && retry--)
	{
		DEBUG(rand, "Retry [%d retries left]", retry);
		goto again;
	}

	DEBUG(rand, "Found sample with power %f\n", power);

	lastsample = sample;

	return sample;
}