/*
olcPGEX_AudioSource.h

+-------------------------------------------------------------+
|         OneLoneCoder Pixel Game Engine Extension            |
|                     AudioSource v1.0                        |
+-------------------------------------------------------------+

What is this?
~~~~~~~~~~~~~
This is an extension to the olcPixelGameEngine v2.16 and above.

It is to be used in conjunction with olcPGEX_AudioListener.h.

A detailed description and instructions can be found in that
header file, please refer to it instead :-)

Enjoy!



License (OLC-3)
~~~~~~~~~~~~~~~

Copyright 2018 - 2019 OneLoneCoder.com

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer.

2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Author
~~~~~~
Justin Richards

*/

#ifndef OLC_PGEX_AUDIO_SOURCE
#define OLC_PGEX_AUDIO_SOURCE

#pragma once
#include "olcPGEX_AudioListener.h"

class olcPGEX_AudioSource : public olc::PGEX
{
public:
	// Pointer to the Audio Listener for this object
	olcPGEX_AudioListener* AL=nullptr;

	// Handle for this particular copy of the sound
	int handle = 255;

	// Maximum copies of this sound allowed for this audio source
	//const int nMaxSamples = 4;
	//int nSampleHandles[4] = { 255, 255, 255, 255 };

	// Audio Sample ID used to locate which sound to play
	int nID;

	// Convenient BOOL to determine playback status
	bool bIsPlaying = false;

	// Current Playback Speed
	float fPlaySpeed = 1.0f;

	// Volume
	float fVolume = 1.0f;
	float fMinVolume = 0.0f;
	float fMaxVolume = 1.0f;

	// Does the Audio Sample Loop?
	bool bLooping = false;

	// Paused status
	bool bPaused = false;

	// Object position, used for calculating volume
	olc::vf2d pos = { 0.0f, 0.0f };


	// Instruct Audio Listener to load this sound (if not loaded already)
	void LoadAudioSample(int ID, const char* fileName);

	int PlayCentered(float speed = 1.0f, float vol = 1.0f, bool looping = false, bool paused = false);

	// Play the Audio Sample, with given parameters
	int Play(vf2d pos, float speed = 1.0f, float vol = 1.0f, bool looping = false, bool paused = false);

	// Pause or Un-Pause - maintains the playback position and handle
	void Pause(bool pauseState = true);

	// Stop - playback position and handle will be lost
	void Stop();

	void Stop(int handle);

	// Audio Modulation - control the speed of playback
	void ModulateAudio(float minPlaySpeed, float maxPlaySpeed, float modulation, bool precise = false, bool deferred = false);

	// Adjust Volume
	void SetVolume(float vol, float minVol = 0.0f, float maxVol = 1.0f);
	void SetVolume(int handle,float vol);

	// Set Default Parameters
	void SetDefaults(float speed, float vol, float minVol, float maxVol, bool looping);
};

#ifdef AUDIO_SOURCE_IMPLEMENTATION
#undef AUDIO_SOURCE_IMPLEMENTATION


void olcPGEX_AudioSource::LoadAudioSample(int ID, const char* fileName)
{
	// Link the IDs together
	nID = ID;

	// Call the Audio Listener to load the sample
	AL->LoadAudioSample(ID, fileName);
}

int olcPGEX_AudioSource::PlayCentered(float speed, float vol, bool looping, bool paused)
{
	// Set parameters
	fPlaySpeed = speed;
	fVolume = vol;
	bLooping = looping;
	bPaused = paused;
	this->pos = pos;

	// Assign a handle to this instance of the sound we are about to play
	handle = AL->soloud.play(*AL->GetAudioSampleByID(nID)->wav, fVolume, 0.0f, bPaused);

	// Set speed and looping 
	AL->soloud.setRelativePlaySpeed(handle, fPlaySpeed);
	AL->soloud.setLooping(handle, looping);

	// Update Play status
	bIsPlaying = true;
	return handle;
}

int olcPGEX_AudioSource::Play(vf2d pos, float speed, float vol, bool looping, bool paused)
{
	// Set parameters
	fPlaySpeed = speed;
	fVolume = vol*std::max(0.f,abs(1-std::min(1.0f,(AL->GetDistance(pos)/1024.f))));
	bLooping = looping;
	bPaused = paused;
	this->pos = pos;

	// Assign a handle to this instance of the sound we are about to play
	handle = AL->soloud.play(*AL->GetAudioSampleByID(nID)->wav, fVolume, 0.0f, bPaused);

	AL->handles.push_back({pos,vol,handle});

	// Set speed and looping 
	AL->soloud.setRelativePlaySpeed(handle, fPlaySpeed);
	AL->soloud.setLooping(handle, looping);

	AL->soloud.setPan(handle,std::clamp((pos.x-AL->vecPos.x)/1024,-1.f,1.f));
	
	// Update Play status
	bIsPlaying = true;

	return handle;
}

void olcPGEX_AudioSource::Pause(bool pauseState)
{
	// Use the Audio Listener to pause or un-pause the sound as neccessary
	AL->soloud.setPause(handle, pauseState);

	// Update Play status
	bIsPlaying = !pauseState;
}

void olcPGEX_AudioSource::Stop(int handle)
{
	// Use the Audio Listener to stop the sound
	AL->soloud.stop(handle);
}

void olcPGEX_AudioSource::Stop()
{
	// Use the Audio Listener to stop the sound
	AL->soloud.stop(handle);

	// The current handle will now point to nothing, so we set it to MAX so we can test for validity if need be
	handle = 255;

	// Update Play status
	bIsPlaying = false;
}

void olcPGEX_AudioSource::ModulateAudio(float minPlaySpeed, float maxPlaySpeed, float modulation, bool precise, bool deferred)
{
	// Apply the modulation
	if (precise)
		fPlaySpeed = modulation;
	else
		fPlaySpeed += modulation;

	// Adjust the play speed to keep it within range
	if (fPlaySpeed < minPlaySpeed) fPlaySpeed = minPlaySpeed;
	else if (fPlaySpeed > maxPlaySpeed) fPlaySpeed = maxPlaySpeed;

	// If multiple adjustments to the playback speed are required, then the Audio Listener update itself can be
	// deferred until the very last adjustment is made... A small optimisation
	if (!deferred)
		AL->soloud.setRelativePlaySpeed(handle, fPlaySpeed);
}

void olcPGEX_AudioSource::SetVolume(int handle,float vol)
{
	// Set volume
	fVolume = vol;

	// Instruct the Audio Listener to apply the volume change
	AL->soloud.setVolume(handle, fVolume);
}

void olcPGEX_AudioSource::SetVolume(float vol, float minVol, float maxVol)
{
	// Set volume
	fVolume = vol;

	// Clamp volume withing set bounds
	if (fVolume < minVol)
		fVolume = minVol;
	else if (fVolume > maxVol)
		fVolume = maxVol;

	// Instruct the Audio Listener to apply the volume change
	AL->soloud.setVolume(handle, fVolume);
}

void olcPGEX_AudioSource::SetDefaults(float speed, float vol, float minVol, float maxVol, bool looping)
{
	// Set defaults for this particular Audio Source
	fPlaySpeed = speed;
	fVolume = vol;
	fMinVolume = minVol;
	fMaxVolume = maxVol;
	bLooping = looping;
}

#endif // AUDIO_SOURCE_IMPLEMENTATION
#endif

typedef olcPGEX_AudioSource Audio;