#include "Song.h"
#include "DebugUtil.h"

#include <iostream>
//#include <string>
#include <math.h>

#include "SDL.h"

#include "SoundManager.h"
#include "SineTable.h"

float* SongSection::m_pTrackDegreesOnWave = new float[MAX_TRACKS];

using std::cout;
using std::endl;

SongSection::SongSection()
: m_pTracks(0), m_numTracks(0)
{

}

SongSection::~SongSection()
{
	if(m_pTracks)
		delete[] m_pTracks;
}

bool SongSection::Initialise(const UInt &bpm, const UInt &length)
{
	//srand(timeGetTime());

	for(int i = 0; i < MAX_TRACKS; i++)
		m_pTrackDegreesOnWave[i] = 180.0f;

	//C - Eb - F - Gb - G - Bb - C Blues scale in C
	float scale[NUM_SCALES][SCALE_LENGTH] =
	{
// 		{
// 			16.35f,   //C0
// 			19.45f,   //Eb0
// 			21.83f,   //F0
// 			23.12f,   //Gb0
// 			24.50f,   //G0
// 			29.14f,   //Bb0
// 			32.70f    //C1
// 		},
		{
			32.70f,   //C1
				38.89f,   //Eb1
				43.65f,   //F1
				46.25f,   //Gb1
				49.00f,   //G1
				58.27f,   //Bb1
				65.41f    //C2
		},
		{
			32.70f,   //C1
			38.89f,   //Eb1
			43.65f,   //F1
			46.25f,   //Gb1
			49.00f,   //G1
			58.27f,   //Bb1
			65.41f    //C2
		},
		{
			65.41f,   //C2
			77.78f,   //Eb2
			87.31f,   //F2
			92.50f,   //Gb2
			98.00f,   //G2
			116.54f,  //Bb2
			130.81f   //C3
		},
		{
			65.41f,   //C2
				77.78f,   //Eb2
				87.31f,   //F2
				92.50f,   //Gb2
				98.00f,   //G2
				116.54f,  //Bb2
				130.81f   //C3
		},
		{
			130.81f,  //C3
				155.56f,  //Eb3
				174.61f,  //F3
				185.00f,  //Gb3
				196.00f,  //G3
				233.08f,  //Bb3
				261.63f   //C4
		},
		{
			130.81f,  //C3
					155.56f,  //Eb3
					174.61f,  //F3
					185.00f,  //Gb3
					196.00f,  //G3
					233.08f,  //Bb3
					261.63f   //C4
		},
		{
			261.63f,  //C4
				311.13f,  //Eb4
				349.23f,  //F4
				369.99f,  //Gb4
				392.00f,  //G4
				466.16f,  //Bb4
				523.25f   //C5
		}//,
// 		{
// 			523.25f,  //C5
// 			622.25f,  //Eb5
// 			698.46f,  //F5
// 			739.99f,  //Gb5
// 			783.99f,  //G5
// 			932.33f,  //Bb5
// 			1046.50f  //C6
// 		}/*,*/
// 		{
// 			1046.50f, //C6
// 			1244.51f, //Eb6
// 			1396.91f, //F6
// 			1479.98f, //Gb6
// 			1567.98f, //G6
// 			1864.66f, //Bb6
// 			2093.00f  //C7
// 		}
	};



	UInt barsPerSection = length / static_cast<UInt>((static_cast<double>(bpm) / 60.0f) * 1000.0f);

	//m_numTracks = (rand() % MAX_TRACKS) + 1;
	m_numTracks = MAX_TRACKS;
	//m_pTrackDegreesOnWave = new double[MAX_TRACKS];
	m_pTracks = new STrack[m_numTracks];
	m_timePerSection = length;

	int scaleIndex = rand() % NUM_SCALES;

	for(size_t i = 0; i < m_numTracks; i++)
	{
	//	srand(timeGetTime());
		m_pTrackDegreesOnWave[i] = 0; //Init degrees
		m_pTracks[i].numUniqueBars = ((rand() % MAX_BARS) / 2) + 2; //The number of unique bars
		//m_pTracks[i].numUniqueBars = MAX_BARS;
							  
							  
		int newScaleIndex = rand() % NUM_SCALES;
		while(scaleIndex == newScaleIndex && NUM_SCALES > 1) //Makes the scale be at least different from the previous track's
			newScaleIndex = rand() % NUM_SCALES;			 //but it could be the same as another track in the song section.

		scaleIndex = newScaleIndex;

		m_pTracks[i].pUniqueBars = new SBar[m_pTracks[i].numUniqueBars];
		UInt noteLength = rand() % eNumNotesTypes;

		for(size_t j = 0; j < m_pTracks[i].numUniqueBars; j++)
		{
			SBar* pBar = &(m_pTracks[i].pUniqueBars[j]);
			switch(noteLength)
			{
			default:
			case eOneNote:
				pBar->numFreqs = 1;
				break;
			case eTwoNotes:
				pBar->numFreqs = 2;
				break;
			case eFourNotes:
				pBar->numFreqs = 4;
				break;
			case eEightNotes:
				pBar->numFreqs = 8;
				break;
			}

			pBar->timePerFreq = (UInt)(((double)bpm / 60.0f) * (1000.0f)) / pBar->numFreqs;

			pBar->pFrequencies = new float[pBar->numFreqs];

			for(size_t k = 0; k < pBar->numFreqs; k++)
			{
				int idx = rand() % SCALE_LENGTH;
				pBar->pFrequencies[k] = scale[scaleIndex][idx]; //scale[idx]; //Get note from scale. FIX
			}

			noteLength = rand() % eNumNotesTypes;
		}

		m_pTracks[i].numBars = barsPerSection;
		m_pTracks[i].pBars = new SBar*[barsPerSection];
		m_pTracks[i].timePerBar = length / barsPerSection;

		for(size_t j = 0; j < barsPerSection; j++)
		{
			m_pTracks[i].pBars[j] = &(m_pTracks[i].pUniqueBars[rand() % m_pTracks[i].numUniqueBars]);
		}
	}

	return true;
}

Sint16* SongSection::GetBuffer(const UInt &position, const int &len)
{
	Sint16** pStreams = new Sint16*[m_numTracks];
	Sint16* pMixedStream = 0;

	SDL_AudioSpec* pSpec = SoundManager::getSingleton()->GetAudioSpec();

	float secsPerSampleset = (float)pSpec->samples / (float)pSpec->freq;

	for(size_t i = 0; i < m_numTracks; i++)
	{
		pStreams[i] = new Sint16[len];
		UInt barIndex = position / m_pTracks[i].timePerBar;

		SBar* pBar = m_pTracks[i].pBars[barIndex];
		UInt timeWithinBar = position - (m_pTracks[i].timePerBar * barIndex);

		UInt freqIndex = timeWithinBar / pBar->timePerFreq;
		float freq = pBar->pFrequencies[freqIndex];

		int channelLen = len / pSpec->channels;

		for(int j = 0; j < channelLen; j++)
		{
			m_pTrackDegreesOnWave[i] += secsPerSampleset * freq;

			//DebugUtil::outputDebugText("Track Degrees: %f\n", m_pTrackDegreesOnWave[i]);

			float val = SineTable::getSingleton()->Sin(m_pTrackDegreesOnWave[i]) * VOLUME_AMPLITUDE;

			pStreams[i][j] = static_cast<Sint16>(val);

 			if(m_pTrackDegreesOnWave[i] > 360.0f)
 				m_pTrackDegreesOnWave[i] = 0;
		}

		if(pSpec->channels > 1)
		{
			for(int c = 0; c < pSpec->channels - 1; c++)
			{
				int startPoint = c * channelLen;
				size_t sz = channelLen * sizeof(Sint16);
				memcpy(&(pStreams[i][startPoint + channelLen]), &(pStreams[i][startPoint]), sz);
			}
		}


		if(pMixedStream == 0)
		{
			pMixedStream = new Sint16[len];
			memcpy(pMixedStream, pStreams[i], len * sizeof(Sint16));
		}
		else
		{
			SoundManager::MixBuffers(pMixedStream, pStreams[i], len);
			//SDL_MixAudio((Uint8*)pMixedStream, (Uint8*)(pStreams[i]), len * sizeof(Sint16), SDL_MIX_MAXVOLUME);
		}
		delete[] pStreams[i];
	}

	delete[] pStreams;

	return pMixedStream;
}

void SongSection::Output()
{
	for(size_t i = 0; i < m_numTracks; i++)
	{
		cout << endl;
		cout << "Track " << i << ": " << endl;
		cout << "Number of bars: " << m_pTracks[i].numBars;
		for(size_t j = 0; j < m_pTracks[i].numBars; j++)
		{
			SBar* pBar = m_pTracks[i].pBars[j];

			cout << endl;
			cout << "Bar " << j << ": " << endl;
			cout << "Number of Frequencies: " << pBar->numFreqs << endl;
			cout << "Time per frequency: " << pBar->timePerFreq << endl;

			for(size_t k = 0; k < pBar->numFreqs; k++)
			{
				cout << pBar->pFrequencies[k] << " ";
			}

			cout << endl;
		}
	}
}

Song::Song()
: m_pSongSections(0), m_bpm(0), m_numSections(0), m_pUniqueSections(0), m_length(0),
  m_timePerSection(0), m_lastPointPlayed(0), m_bIsPreCreated(false), m_pSongData(0), m_lastBufferRequested(0)
{
}

Song::~Song()
{
	if(m_pSongSections)
		delete[] m_pSongSections;

	if(m_pSongData)
		delete[] m_pSongData;
}

void Song::Initialise(const bool &bPreCreate, const UInt &bpm, const UInt &numUniqueSections, const UInt &lengthInSections, const UInt &timePerSection)
{
	srand(SDL_GetTicks());
	m_bpm = bpm;
	m_numSections = lengthInSections;
	m_timePerSection = timePerSection;
	m_length = lengthInSections * m_timePerSection;
	m_pSongSections = new SongSection*[lengthInSections];

	m_pUniqueSections = new SongSection[numUniqueSections];

	for(size_t i = 0; i < numUniqueSections; i++)
		m_pUniqueSections[i].Initialise(bpm, timePerSection);

	for(size_t i = 0; i < lengthInSections; i++)
	{
		int idx = rand() % numUniqueSections;
		m_pSongSections[i] = &m_pUniqueSections[idx];
	}

	if(bPreCreate)
	{
		SDL_AudioSpec* pSpec = SoundManager::getSingleton()->GetAudioSpec();
		UInt lengthInWords = static_cast<UInt>((static_cast<float>(m_length) / 1000.0f) * static_cast<float>(SoundManager::getSingleton()->GetAudioSpec()->freq));
		m_numSampleSets = lengthInWords / pSpec->samples;
		m_pSongData = new Sint16*[m_numSampleSets];

		
		UInt msPerSampleSet = static_cast<UInt>(((static_cast<float>(pSpec->samples) / static_cast<float>(pSpec->freq)) * 1000.0f) + 1);
		UInt numSetsPerSection = (timePerSection / msPerSampleSet);

// 		UInt arrIdx = 0;
// 		for(int j = 0; j < lengthInSections; j++)
// 		{
// 			for(int i = 0; i < numSetsPerSection; i++)
// 			{
// 				m_pSongData[arrIdx] = new Sint16[pSpec->samples];
// 				UInt pos = (arrIdx * msPerSampleSet) % m_pSongSections[j]->GetSectionLength();
// 				memcpy(&(m_pSongData[arrIdx][0]), m_pSongSections[j]->GetBuffer(pos, pSpec->samples), pSpec->samples * sizeof(Sint16));
// 				arrIdx++;
// 			}
// 		}


		for(size_t i = 0; i < m_numSampleSets; i++)
		{
// 			UInt section = ((((float)i / (float)m_numSampleSets) * (float)m_length) / m_length);
// 			UInt pos = ( - (idx * m_timePerSection));

			UInt pos = i * msPerSampleSet;
			if(pos >= m_length)
				pos = m_length - 1;
			m_pSongData[i] = GetDynamicBuffer(pos, pSpec->samples);
			///memcpy(&(m_pSongData[i][0]), pBuffer, pSpec->samples * sizeof(Sint16));
		}

		m_bIsPreCreated = true;
	}
}

void Song::Output()
{
	cout << "Music data output: " << endl;
//	cout << "Song: " << m_name << endl;
	cout << "BPM: " << m_bpm << endl;
	cout << "Length in MS: " << m_length << endl;

	for(size_t i = 0; i < m_numSections; i++)
	{
		cout << endl;
		cout << "Song Section " << i << ": " << endl;
		m_pSongSections[i]->Output();
	}
}

Sint16* Song::GetBuffer(const UInt &position, const int &len, bool isLooping)
{
	UInt relTime = 0;
	if(isLooping)
	{
		relTime = position % m_length;//If it's looping
	}
	else
	{
		if(position > m_length)
			return 0;
		else
			relTime = position;
	}

	if(m_bIsPreCreated)
	{
// 		int idx = ((float)relTime / (float)m_length) * (float)m_numSampleSets;
// 		if(idx >= m_numSampleSets)
// 			idx = m_numSampleSets - 1;
// // 		if(m_pSongData[idx] == (Sint16*)0xcdcdcdcd)
// // 			return 0;

		++m_lastBufferRequested;
		if(m_lastBufferRequested >= m_numSampleSets)
			m_lastBufferRequested = 0;
		return m_pSongData[m_lastBufferRequested];
	}
	else
	{
		return GetDynamicBuffer(relTime, len);
	}
}

Sint16* Song::GetDynamicBuffer(const UInt &pos, const int &len)
{
	int idx = pos / m_timePerSection;
	UInt sectionPos = (pos - (idx * m_timePerSection));
	return m_pSongSections[idx]->GetBuffer(sectionPos, len);
}
