#include <switch.h>
#include <unistd.h>

#include <iostream>
#include <fstream>

// AWS Core includes
#include <aws/core/Aws.h>
#include <aws/core/auth/AWSCredentialsProviderChain.h>
#include <aws/core/client/AWSClient.h>
#include <aws/core/utils/Outcome.h>

// AWS Polly Includes
#include <aws/polly/PollyClient.h>
#include <aws/polly/model/SynthesizeSpeechRequest.h>
#include <aws/polly/model/SynthesizeSpeechResult.h>
#include <aws/polly/PollyRequest.h>

#include <aws/core/utils/stream/ResponseStream.h>

static Aws::SDKOptions aws_options;

SWITCH_MODULE_LOAD_FUNCTION(mod_polly_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_polly_shutdown);
SWITCH_MODULE_DEFINITION(mod_polly, mod_polly_load, mod_polly_shutdown, NULL);

static switch_event_node_t *NODE = NULL;

static struct {
	char *command;
} globals;

SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_command, globals.command);

struct polly_data {
	char *voice_name;
	int rate;
	char *file;
	switch_file_handle_t *fh;
};

typedef struct polly_data polly_t;

static int GetStreamSize(Aws::IOStream* stream){
    // Ensure the stream is at the beginning
                stream->seekg (0, std::ios::beg);
    int begin = stream->tellg();
                stream->seekg (0, std::ios::end);
    int end =   stream->tellg();
                stream->seekg (0, std::ios::beg);

    return end-begin;
}

static char* GetStreamBytes(Aws::IOStream* stream){
    // Read the stream bytes into a memory block
    int size = GetStreamSize(stream);
    char* bytes = new char[size];
    stream->read(bytes, size);

    return bytes;
}

static void TestPolly()
{
    Aws::Polly::PollyClient pollyClient;
    Aws::Polly::Model::SynthesizeSpeechRequest speechRequest;

    speechRequest.SetVoiceId(Aws::Polly::Model::VoiceId::Kendra);
    speechRequest.SetOutputFormat(Aws::Polly::Model::OutputFormat::pcm);

    speechRequest.SetText("Flight 1 2 7 is now headed to Denver. Please make appropriate adjustments.");
    auto result = pollyClient.SynthesizeSpeech(speechRequest);

    if (result.IsSuccess()){
        std::cout << "Speech synthesis was successfull. Saving to file now..." << std::endl;

        Aws::String     file_name = "polly.wav";
        Aws::IOStream*  audio_stream = &result.GetResult().GetAudioStream();
        Aws::OFStream   voice_file;

        // Get the bytes from the stream and save them into a file
        voice_file.open(file_name.c_str(), std::ios::out | std::ios::binary);
        voice_file.write(GetStreamBytes(audio_stream), GetStreamSize(audio_stream));
        voice_file.close();

        std::cout << "Done!" << std::endl;
    }
    else{
        std::cout << "Speech synthesis failed.\n";
        std::cout << "Error: " << result.GetError().GetMessage();
    }
}

static int load_polly_config(void)
{
	return SWITCH_STATUS_SUCCESS;
}

static void event_handler(switch_event_t *event)
{
	if (event->event_id == SWITCH_EVENT_RELOADXML) {
		if (load_polly_config() != SWITCH_STATUS_SUCCESS) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to reload config file\n");
		}
	}
}

static switch_status_t polly_speech_open(switch_speech_handle_t *sh, const char *voice_name, int rate, int channels, switch_speech_flag_t *flags)
{
	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t polly_speech_close(switch_speech_handle_t *sh, switch_speech_flag_t *flags)
{
	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t polly_speech_feed_tts(switch_speech_handle_t *sh, char *text, switch_speech_flag_t *flags)
{
	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t polly_speech_read_tts(switch_speech_handle_t *sh, void *data, size_t *datalen, switch_speech_flag_t *flags)
{
	return SWITCH_STATUS_SUCCESS;
}

static void polly_speech_flush_tts(switch_speech_handle_t *sh)
{
}

static void polly_text_param_tts(switch_speech_handle_t *sh, char *param, const char *val)
{
}

static void polly_numeric_param_tts(switch_speech_handle_t *sh, char *param, int val)
{
}

static void polly_float_param_tts(switch_speech_handle_t *sh, char *param, double val)
{
}

SWITCH_MODULE_LOAD_FUNCTION(mod_polly_load)
{
  Aws::InitAPI(aws_options); // Danger!  : This is good for now since polly is the only one using Aws.  We need to move this to a global AWS initializer
  TestPolly();
	return SWITCH_STATUS_SUCCESS;
}

SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_polly_shutdown)
{
	Aws::ShutdownAPI(aws_options); // Danger!  : This is good for now since polly is the only one using Aws.  We need to move this to a global AWS initializer
	return SWITCH_STATUS_UNLOAD;
}

