#include "munga.h"
#pragma hdrstop

#include "audseq.h"
#include "objstrm.h"
#include "namelist.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioControlEvent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioControlEvent::AudioControlEvent(
	AudioTick start_tick,
	AudioControlID audio_control_ID,
	AudioControlValue audio_control_value
):
	Plug(AudioControlEventClassID)
{
	startTick = start_tick;
	audioControlID = audio_control_ID;
	audioControlValue = audio_control_value;
}

//
//#############################################################################
//#############################################################################
//
AudioControlEvent::AudioControlEvent():
	Plug(AudioControlEventClassID)
{
	startTick = 0;
	audioControlID = NullAudioControlID;
	audioControlValue = 0.0f;
}

//
//#############################################################################
//#############################################################################
//
AudioControlEvent::~AudioControlEvent()
{
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioControlEvent::TestInstance() const
{
	Plug::TestInstance();
	Verify(
		(Enumeration)audioControlID >= (Enumeration)0 &&
		(Enumeration)audioControlID < (Enumeration)AudioControlIDCount
	);
	return True;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlEvent::Send(AudioComponent *audio_component)
{
	Check(this);
	Check(audio_component);
	audio_component->ReceiveControl(audioControlID, audioControlValue);
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioControlEvent::Chase(AudioComponent *audio_component)
{
	Check(this);
	Check(audio_component);

	switch (audioControlID)
	{
	case StartAudioControlID:
		return False;

	case StopAudioControlID:
		Send(audio_component);
		return False;

	default:
		break;
	}
	return True;
}

//
//#############################################################################
//#############################################################################
//
MemoryStream&
	MemoryStream_Read(
		MemoryStream *stream,
		AudioControlEvent *control_event
	)
{
	Check(stream);
	Check_Signature(control_event);

	MemoryStream_Read(stream, &control_event->startTick);
	MemoryStream_Read(stream, &control_event->audioControlID);
	MemoryStream_Read(stream, &control_event->audioControlValue);

	Check(control_event);
	return *stream;
}

//
//#############################################################################
//#############################################################################
//
MemoryStream&
   MemoryStream_Write(
		MemoryStream *stream,
		const AudioControlEvent *control_event
	)
{
	Check(stream);
	Check(control_event);

	MemoryStream_Write(stream, &control_event->startTick);
	MemoryStream_Write(stream, &control_event->audioControlID);
	MemoryStream_Write(stream, &control_event->audioControlValue);
	return *stream;
}

//
//#############################################################################
//#############################################################################
//
std::ostream& operator << (std::ostream &strm, const AudioControlEvent &control_event)
{
	Check(&control_event);

	strm << "[" ;
	strm << control_event.startTick << ",";
	strm << control_event.audioControlID << ",";
	strm << control_event.audioControlValue;
	strm << "]";

	return strm;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioControlSequence ~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioControlSequence::AudioControlSequence(
	PlugStream *stream,
	Entity *entity
):
	AudioComponent(stream),
   audioComponentSocket(NULL),
	audioControlEventSocket(NULL)
{
	Logical dump_value;
	Logical is_looped;
	AudioComponent *audio_component;
	AudioControlEvent *audio_control_event;
	CollectionSize i, number_of_control_events;
	AudioDivisionsPerBeat divisions_per_beat;
	AudioTempo tempo;

	MemoryStream_Read(stream, &dump_value);
	MemoryStream_Read(stream, &is_looped);
	PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);
	MemoryStream_Read(stream, &divisions_per_beat);
	MemoryStream_Read(stream, &tempo);
	MemoryStream_Read(stream, &number_of_control_events);

	for (i = 0; i < number_of_control_events; i++)
	{
		audio_control_event = new AudioControlEvent;
		Register_Object(audio_control_event);
		MemoryStream_Read(stream, audio_control_event);
		audioControlEventSocket.Add(audio_control_event);
	}

	AudioControlSequenceX(
		audio_component,
		entity,
		is_looped,
		divisions_per_beat,
      tempo,
		dump_value
	);
}

//
//#############################################################################
//#############################################################################
//
#if DEBUG_LEVEL>0
void
	AudioControlSequence::AudioControlSequenceX(
		AudioComponent *audio_component,
		Entity *entity,
		Logical is_looped,
		AudioDivisionsPerBeat divisions_per_beat,
		AudioTempo the_tempo,
		Logical dump_value
	)
#else
void
	AudioControlSequence::AudioControlSequenceX(
		AudioComponent *audio_component,
		Entity *entity,
		Logical is_looped,
		AudioDivisionsPerBeat divisions_per_beat,
		AudioTempo the_tempo,
		Logical
	)
#endif
{
	Check(audio_component);
	audioComponentSocket.Add(audio_component);

	isLooped = is_looped;
	isRunning = False;
	startTime = AudioTime::Null;
	audioControlEventIterator = NULL;
	divisionsPerBeat = divisions_per_beat;
	tempo = the_tempo;

	#if DEBUG_LEVEL>0
		dumpValue = dump_value;
	#endif

	audio_component->AddWatcher(this);
	Check(entity);
	entity->AddAudioComponent(this);
}

//
//#############################################################################
//#############################################################################
//
AudioControlSequence::~AudioControlSequence()
{
	StopSequence();
	Verify(!isRunning);
	Verify(audioControlEventIterator == NULL);

	AudioControlEventIterator iterator(&audioControlEventSocket);
	Check(&iterator);
	iterator.DeletePlugs();
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlSequence::BuildFromPage(
		PlugStream *stream,
		NameList *name_list,
		ClassID class_ID,
		ObjectID object_ID
	)
{
	AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

	MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, dump_value);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, is_looped);

	//
	// Write audio component
	//
	CString audio_component_name("audio_component");
	PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);

	//
	// Read tempo
	//
	AudioTempo tempo = 100;

	if (name_list->FindData("tempo") != NULL)
	{
		Check_Pointer(name_list->FindData("tempo"));
		Convert_From_Ascii((const char *)name_list->FindData("tempo"), &tempo);
	}

	//
	// Read midi file
	//
	DynamicMemoryStream midi_file_stream;

	{
		CString midi_file_name;

		Check_Pointer(name_list->FindData("midi_file"));
		midi_file_name = (const char*)name_list->FindData("midi_file");

		std::ifstream input_midi_file(midi_file_name, std::ios::in | std::ios::binary);
		#if DEBUG_LEVEL>0
			if (!input_midi_file)
			{
				Dump(midi_file_name);
			}
		#endif

		MemoryStream_Write(&midi_file_stream, &input_midi_file);
		midi_file_stream.Rewind();
	}

	//
	// Parse midi stream
	//
	DynamicMemoryStream control_event_stream;
	CreateAudioControlEventStream midi_parser;

	Check(&midi_file_stream);
	Check(&control_event_stream);
	Check(&midi_parser);
	midi_parser.Parse(&midi_file_stream, &control_event_stream, tempo);
	control_event_stream.Rewind();

	//
	// Write the divisions per beat and tempo
	//
	AudioDivisionsPerBeat divisions_per_beat;

	divisions_per_beat = midi_parser.GetDivisionsPerBeat();
	tempo = midi_parser.GetTempo();
	MemoryStream_Write(stream, &divisions_per_beat);
	MemoryStream_Write(stream, &tempo);

	//
	// Write control event stream
	//
	CollectionSize number_of_control_events;

	number_of_control_events = midi_parser.GetNumberOfEvents();
	MemoryStream_Write(stream, &number_of_control_events);
	MemoryStream_Write(stream, &control_event_stream);
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioControlSequence::TestInstance() const
{
	AudioComponent::TestInstance();

	Check(&audioComponentSocket);
	Check(&audioControlEventSocket);
	if (isRunning)
	{
		Check(audioControlEventIterator);
	}
	else
	{
		Verify(audioControlEventIterator == NULL);
	}
	Verify(tempo >= 1 && tempo <= 600);
	return True;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlSequence::StartSequence()
{
	Check(this);

	//
	// If already running, stop sequence
	//
	if (isRunning)
	{
		StopSequence();
	}

	//
	// Make an iterator for the control sequence
	//
	Verify(audioControlEventIterator == NULL);
	audioControlEventIterator =
		new AudioControlEventIterator(&audioControlEventSocket);
	Register_Object(audioControlEventIterator);

	startTime = AudioTime::Now();
	isRunning = True;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlSequence::StopSequence()
{
	Check(this);

	//
	// If not running, return
	//
	if (!isRunning)
		return;

	//
	// Find next on or off event
	//
	AudioControlEvent *control_event;

	Check(audioControlEventIterator);
	while ((control_event = audioControlEventIterator->ReadAndNext()) != NULL)
	{
		Check(control_event);
		if (!control_event->Chase(audioComponentSocket.GetCurrent()))
			break;
	}

	//
	// Destroy the iterator for the control sequence
	//
	Unregister_Object(audioControlEventIterator);
	delete audioControlEventIterator;
	audioControlEventIterator = NULL;

	isRunning = False;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlSequence::RunSequence()
{
	Check(this);

	//
	// If the sequence is not running, then return
	//
	if (!isRunning)
		return;

	//
	// Play events which have a start time less then the
	// current time
	//
	AudioControlEvent *control_event;

	Check(audioControlEventIterator);
	control_event = audioControlEventIterator->GetCurrent();
	while (
		control_event != NULL &&
		IsEventReady(control_event)
	)
	{
		#if DEBUG_LEVEL>0
			if (dumpValue)
			{
				Dump(*control_event);
			}
		#endif
		
		#if 0
		//
		// HACK - convert start control to use duration
		//
		Verify(control_event->audioControlID != StopAudioControlID);
		if (control_event->audioControlID == StartAudioControlID)
		{
			//
			// Convert ticks to seconds
			// seconds = ticks / (divisionsPerBeat (t/b) * tempo (b/60s))
			//
			Scalar denominator = 
				(Scalar)divisionsPerBeat * (Scalar)tempo / 60.0f;
			Verify(!Small_Enough(denominator));
			control_event->audioControlValue =
				control_event->audioControlValue / denominator;
		}
		#endif

		//
		// Send the controller to the connected audio component
		//
		Check(control_event);
		control_event->Send(audioComponentSocket.GetCurrent());

		//
		// Step to next event
		//
		audioControlEventIterator->Next();
		control_event = audioControlEventIterator->GetCurrent();
	}

	//
	// If there are no more events to be played then stop running
	//
	if (control_event == NULL)
	{
		StopSequence();
		if (isLooped)
		{
			StartSequence();
		}
	}
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlSequence::ReceiveControl(
		AudioControlID control_ID,
		AudioControlValue control_value
	)
{
	Check(this);

	switch(control_ID)
	{
	case StartAudioControlID:
		StartSequence();
		break;

	case StopAudioControlID:
		StopSequence();
		break;

	case IdleAudioControlID:
		RunSequence();
		break;

	case TempoAudioControlID:
		tempo = control_value;
		Verify(tempo >= 1 && tempo <= 600);
		break;

	default:
		break;
	}
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioControlSequence::IsEventReady(AudioControlEvent *audio_control_event)
{
	Check(this);

	AudioTime offset_time(startTime);
	offset_time += CalculateEventSeconds(audio_control_event);
	return (offset_time <= AudioTime::Now());
}

//
//#############################################################################
//#############################################################################
//
Scalar
	AudioControlSequence::CalculateEventSeconds(
		AudioControlEvent *audio_control_event
	)
{
	Check(this);
	Check(audio_control_event);

	//
	// Convert ticks to seconds
	// seconds = ticks / (divisionsPerBeat (t/b) * tempo (b/60s))
	//
	Scalar denominator = (Scalar)divisionsPerBeat * (Scalar)tempo / 60.0f;
	Verify(!Small_Enough(denominator));
	return (Scalar)audio_control_event->GetStartTick() / denominator;
}

//#############################################################################
//####################    CreateAudioControlEventStream    ####################
//#############################################################################

CreateAudioControlEventStream::CreateAudioControlEventStream():
	audioControlEventSocket(NULL, False)
{
	inputStream = NULL;
	outputStream = NULL;
	divisionsPerBeat = 120;
	tempo = 100;
}

//
//#############################################################################
//#############################################################################
//
CreateAudioControlEventStream::~CreateAudioControlEventStream(void)
{
	AudioControlEventIterator iterator(&audioControlEventSocket);
	Check(&iterator);
	iterator.DeletePlugs();
}

//
//#############################################################################
//#############################################################################
//
Logical
	CreateAudioControlEventStream::TestInstance() const
{
	MidiParse::TestInstance();
	Check(&audioControlEventSocket);
	return True;
}

//
//#############################################################################
//#############################################################################
//
void
	CreateAudioControlEventStream::Parse(
		MemoryStream *input_stream,
		MemoryStream *output_stream,
		SignedLongWord tempo_argument
	)
{
	Check(this);
	Check(input_stream);
	Check(output_stream);

	inputStream = input_stream;
	outputStream = output_stream;

	//
	// Parse the stream
	//
	tempo = tempo_argument;
	Verify(GetNumberOfEvents() == 0);
	MidiParse::Parse();

	//
	// Write the events
	//
	AudioControlEventIterator iterator(&audioControlEventSocket);
	AudioControlEvent *control_event;

	Check(&iterator);
	while ((control_event = iterator.ReadAndNext()) != NULL)
	{
		Check(control_event);
		MemoryStream_Write(outputStream, control_event);
	}
}

//
//#############################################################################
//#############################################################################
//
CollectionSize
	CreateAudioControlEventStream::GetNumberOfEvents()
{
	Check(this);

	AudioControlEventIterator iterator(&audioControlEventSocket);
	Check(&iterator);
	return iterator.GetSize();
}

//
//#############################################################################
//#############################################################################
//
MidiParse::SignedWord
	CreateAudioControlEventStream::Mf_getc()
{
	Check(this);

	unsigned char c;

	MemoryStream_Read(inputStream, &c);
	return c;
}

//
//#############################################################################
//#############################################################################
//
void
	CreateAudioControlEventStream::Mf_error(char *str)
{
	Check(this);
	Check_Pointer(str);
	Fail(str);
}

//
//#############################################################################
//#############################################################################
//
void
	CreateAudioControlEventStream::Mf_header(
		SignedWord,
		SignedWord,
		SignedWord division
	)
{
	Check(this);
	divisionsPerBeat = division;
}

//
//#############################################################################
//#############################################################################
//
void
	CreateAudioControlEventStream::Mf_on(
		SignedWord channel,
		SignedWord pitch,
		SignedWord vol
	)
{
	Check(this);

	//
	// Interpret vol 0 as note off
	//
	if (vol == 0)
	{
		Mf_off(channel, pitch, vol);
		return;
	}

	//
	// Verify that last event was a stop
	//
	#if DEBUG_LEVEL>0
		{
			AudioControlEventIterator iterator(&audioControlEventSocket);
			AudioControlEvent *last_control_event;
			
			iterator.Last();
			if ((last_control_event = iterator.GetCurrent()) != NULL)
			{
				Check(last_control_event);
				Verify(last_control_event->audioControlID == StopAudioControlID);
			}
		}
	#endif

	AudioTick current_ticks = CurTime();
	AudioControlEvent *audio_control_event;

	//
	// Add note value event
	//
	audio_control_event
		= new AudioControlEvent(
			current_ticks,
			NoteAudioControlID,
			(Scalar)pitch
		);
	Register_Object(audio_control_event);
	audioControlEventSocket.AddValue(audio_control_event, current_ticks);

	//
	// Add velocity event
	//
	audio_control_event
		= new AudioControlEvent(
			current_ticks,
			AttackVolumeAudioControlID,
			(Scalar)vol / (Scalar)127
		);
	Register_Object(audio_control_event);
	audioControlEventSocket.AddValue(audio_control_event, current_ticks);

	//
	// Add start event
	//
	audio_control_event
		= new AudioControlEvent(
			current_ticks,
			StartAudioControlID,
			0.0f
		);
	Register_Object(audio_control_event);
	audioControlEventSocket.AddValue(audio_control_event, current_ticks);
}

//
//#############################################################################
//#############################################################################
//
void
	CreateAudioControlEventStream::Mf_off(
		SignedWord,
		SignedWord pitch,
		SignedWord
	)
{
	Check(this);

   AudioTick current_ticks = CurTime();
	AudioControlEvent *audio_control_event;
	
	#if 0
		//
		// HACK - convert start control to use duration
		//
		AudioControlEventIterator 
			iterator(&audioControlEventSocket);
		
		iterator.Last();
		audio_control_event = iterator.GetCurrent();
		Check(audio_control_event);
		Verify(audio_control_event->audioControlID == StartAudioControlID);
				
		audio_control_event->audioControlValue = 
			current_ticks - audio_control_event->startTick;			
	#else
		//
		// Add note value event
		//
		audio_control_event
			= new AudioControlEvent(
				current_ticks,
				NoteAudioControlID,
				(Scalar)pitch
			);
		Register_Object(audio_control_event);
		audioControlEventSocket.AddValue(audio_control_event, current_ticks);

		//
		// Add stop event
		//
		audio_control_event
			= new AudioControlEvent(
				current_ticks,
				StopAudioControlID,
				0.0f
			);
		Register_Object(audio_control_event);
		audioControlEventSocket.AddValue(audio_control_event, current_ticks);
	#endif		
}

//
//#############################################################################
//#############################################################################
//
void
	CreateAudioControlEventStream::Mf_tempo(SignedLongWord)
{
	Check(this);
	// tempo = i1; // HACK - Cakewalk appears to produce bogus tempo
}
