/*************************************************************************/
/*  gibberish_stream.cpp                                                 */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "gibberish_stream.h"
#include "servers/audio_server.h"

//TODO: This class needs to be adapted to the new AudioStream API,
// or dropped if nobody cares about fixing it :) (GH-3307)

#if 0

int AudioStreamGibberish::get_channel_count() const {

	return 1;
}


static float _get_vol_at_pos(int p_pos, int p_len, int p_x_fade) {

	if (p_pos < p_x_fade)
		return float(p_pos)/p_x_fade;
	else if (p_pos>(p_len-p_x_fade))
		return float(p_len-p_pos)/p_x_fade;
	else
		return 1.0;

}
 int AudioStreamGibberish::randomize() {

	if (rand_idx==_rand_pool.size()) {

		for(int i=0;i<_rand_pool.size();i++) {

			SWAP(_rand_pool[i],_rand_pool[Math::rand()%_rand_pool.size()]);
		}
		rand_idx=0;
	}

	return _rand_pool[rand_idx++];
}

bool AudioStreamGibberish::mix(int32_t *p_buffer, int p_frames) {

	if (!active)
		return false;

	zeromem(p_buffer,p_frames*sizeof(int32_t));

	if (!paused && active_voices==0) {

		active_voices=1;
		playback[0].idx=randomize();
		playback[0].fp_pos=0;
		playback[0].scale=Math::random(1,1+pitch_random_scale);
	}

	for(int i=0;i<active_voices;i++) {

		RID s = _samples[playback[i].idx]->get_rid();

		uint64_t fp_pos=playback[i].fp_pos;
		const void *data = AudioServer::get_singleton()->sample_get_data_ptr(s);
		bool is16 = AudioServer::get_singleton()->sample_get_format(s)==AudioServer::SAMPLE_FORMAT_PCM16;
		int skip = AudioServer::get_singleton()->sample_is_stereo(s) ? 1: 0;
		uint64_t max = AudioServer::get_singleton()->sample_get_length(s) * uint64_t(FP_LEN);
		int mrate = AudioServer::get_singleton()->sample_get_mix_rate(s) * pitch_scale * playback[i].scale;
		uint64_t increment = uint64_t(mrate) * uint64_t(FP_LEN) / get_mix_rate();


		float vol_begin = _get_vol_at_pos(fp_pos>>FP_BITS,max>>FP_BITS,xfade_time*mrate);
		float vol_end = _get_vol_at_pos((fp_pos+p_frames*increment)>>FP_BITS,max>>FP_BITS,xfade_time*mrate);

		int32_t vol = CLAMP(int32_t(vol_begin * 65535),0,65535);
		int32_t vol_to = CLAMP(int32_t(vol_end * 65535),0,65535);
		int32_t vol_inc = (vol_to-vol)/p_frames;

		bool done=false;

		if (is16) {

			const int16_t *smp = (int16_t*)data;
			for(int i=0;i<p_frames;i++) {

				if (fp_pos >= max) {
					done=true;
					break;
				}

				int idx = (fp_pos>>FP_BITS)<<skip;
				p_buffer[i]+=int32_t(smp[idx])*vol;
				vol+=vol_inc;

				fp_pos+=increment;
			}
		} else {

			const int8_t *smp = (int8_t*)data;
			for(int i=0;i<p_frames;i++) {

				if (fp_pos >= max) {
					done=true;
					break;
				}

				int idx = (fp_pos>>FP_BITS)<<skip;
				p_buffer[i]+=(int32_t(smp[idx])<<8)*vol;
				vol+=vol_inc;
				fp_pos+=increment;
			}

		}

		playback[i].fp_pos=fp_pos;
		if (!paused && active_voices==1 && (vol_end < vol_begin || done)) {
			//xfade to something else i gues
			active_voices=2;
			playback[1].idx=randomize();
			playback[1].fp_pos=0;
			playback[1].scale=Math::random(1,1+pitch_random_scale);
		}

		if (done) {

			if (i==0 && active_voices==2) {
				playback[0]=playback[1];
				i--;
			}
			active_voices--;

		}
	}

	return true;
}


void AudioStreamGibberish::play() {
	if (active)
		stop();


	if (!phonemes.is_valid())
		return;


	List<StringName> slist;
	phonemes->get_sample_list(&slist);
	if (slist.size()==0)
		return;

	_samples.resize(slist.size());
	_rand_pool.resize(slist.size());

	int i=0;
	for(List<StringName>::Element *E=slist.front();E;E=E->next()) {

		_rand_pool[i]=i;
		_samples[i++]=phonemes->get_sample(E->get());
	}

	rand_idx=0;
	active_voices=0;
	active=true;
}

void AudioStreamGibberish::stop(){

	active=false;


}

bool AudioStreamGibberish::is_playing() const {

	return active;
}


void AudioStreamGibberish::set_paused(bool p_paused){

	paused=p_paused;
}

bool AudioStreamGibberish::is_paused(bool p_paused) const{

	return paused;
}

void AudioStreamGibberish::set_loop(bool p_enable){


}

bool AudioStreamGibberish::has_loop() const{

	return false;
}


float AudioStreamGibberish::get_length() const{

	return 0;
}


String AudioStreamGibberish::get_stream_name() const{

	return "Gibberish";
}


int AudioStreamGibberish::get_loop_count() const{

	return 0;
}


float AudioStreamGibberish::get_pos() const{

	return 0;
}

void AudioStreamGibberish::seek_pos(float p_time){


}


AudioStream::UpdateMode AudioStreamGibberish::get_update_mode() const{

	return AudioStream::UPDATE_NONE;
}

void AudioStreamGibberish::update(){


}


void AudioStreamGibberish::set_phonemes(const Ref<SampleLibrary>& p_phonemes) {

	phonemes=p_phonemes;

}

Ref<SampleLibrary> AudioStreamGibberish::get_phonemes() const {

	return phonemes;
}

void AudioStreamGibberish::set_xfade_time(float p_xfade) {

	xfade_time=p_xfade;
}

float AudioStreamGibberish::get_xfade_time() const {

	return xfade_time;
}

void AudioStreamGibberish::set_pitch_scale(float p_scale) {

	pitch_scale=p_scale;
}

float AudioStreamGibberish::get_pitch_scale() const {

	return pitch_scale;
}

void AudioStreamGibberish::set_pitch_random_scale(float p_random_scale) {

	pitch_random_scale=p_random_scale;
}

float AudioStreamGibberish::get_pitch_random_scale() const {

	return pitch_random_scale;
}

void AudioStreamGibberish::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_phonemes","phonemes"),&AudioStreamGibberish::set_phonemes);
	ClassDB::bind_method(D_METHOD("get_phonemes"),&AudioStreamGibberish::get_phonemes);

	ClassDB::bind_method(D_METHOD("set_pitch_scale","pitch_scale"),&AudioStreamGibberish::set_pitch_scale);
	ClassDB::bind_method(D_METHOD("get_pitch_scale"),&AudioStreamGibberish::get_pitch_scale);

	ClassDB::bind_method(D_METHOD("set_pitch_random_scale","pitch_random_scale"),&AudioStreamGibberish::set_pitch_random_scale);
	ClassDB::bind_method(D_METHOD("get_pitch_random_scale"),&AudioStreamGibberish::get_pitch_random_scale);

	ClassDB::bind_method(D_METHOD("set_xfade_time","sec"),&AudioStreamGibberish::set_xfade_time);
	ClassDB::bind_method(D_METHOD("get_xfade_time"),&AudioStreamGibberish::get_xfade_time);

	ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"phonemes",PROPERTY_HINT_RESOURCE_TYPE,"SampleLibrary"),"set_phonemes","get_phonemes");
	ADD_PROPERTY( PropertyInfo(Variant::REAL,"pitch_scale",PROPERTY_HINT_RANGE,"0.01,64,0.01"),"set_pitch_scale","get_pitch_scale");
	ADD_PROPERTY( PropertyInfo(Variant::REAL,"pitch_random_scale",PROPERTY_HINT_RANGE,"0,64,0.01"),"set_pitch_random_scale","get_pitch_random_scale");
	ADD_PROPERTY( PropertyInfo(Variant::REAL,"xfade_sec",PROPERTY_HINT_RANGE,"0.001,0.5,0.001"),"set_xfade_time","get_xfade_time");

}

AudioStreamGibberish::AudioStreamGibberish() {

	xfade_time=0.1;
	pitch_scale=1;
	pitch_random_scale=0;
	active=false;
	paused=false;
	active_voices=0;
}
#endif