#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/pulseaudio.h>
#define BUFSIZE 1024

pa_mainloop *m_pulseaudio_mainloop;

void cb(__attribute__((unused)) pa_context *pulseaudio_context, const pa_server_info *i, void *userdata){

	//getting default sink name
    struct audio_data *audio = (struct audio_data *)userdata;
	audio->source = malloc(sizeof(char) * 1024);

	strcpy(audio->source,i->default_sink_name);

	//appending .monitor suufix
	audio->source = strcat(audio->source, ".monitor");

	//quiting mainloop
    pa_context_disconnect(pulseaudio_context);
    pa_context_unref(pulseaudio_context);
	pa_mainloop_quit(m_pulseaudio_mainloop, 0);
    pa_mainloop_free(m_pulseaudio_mainloop);
}


void pulseaudio_context_state_callback(pa_context *pulseaudio_context,
                                                  void *userdata) {

	//make sure loop is ready	
	switch (pa_context_get_state(pulseaudio_context))
	{
	case PA_CONTEXT_UNCONNECTED:
	//printf("UNCONNECTED\n");
	break;
	case PA_CONTEXT_CONNECTING:
	//printf("CONNECTING\n");
	break;
	case PA_CONTEXT_AUTHORIZING:
	//printf("AUTHORIZING\n");
	break;
	case PA_CONTEXT_SETTING_NAME:
	//printf("SETTING_NAME\n");
	break;
	case PA_CONTEXT_READY://extract default sink name
	//printf("READY\n");
	pa_operation_unref(pa_context_get_server_info(
	pulseaudio_context, cb, userdata));
	break;
	case PA_CONTEXT_FAILED:
	printf("failed to coennect to pulseaudio server\n");
	exit(EXIT_FAILURE);
	break;
	case PA_CONTEXT_TERMINATED:
	printf("TERMINATED\n");
	pa_mainloop_quit(m_pulseaudio_mainloop, 0);
	break;	  
	}
}


void getPulseDefaultSink(void* data) {


    	struct audio_data *audio = (struct audio_data *)data;
	pa_mainloop_api *mainloop_api;
	pa_context *pulseaudio_context;
	int ret;

	// Create a mainloop API and connection to the default server
	m_pulseaudio_mainloop = pa_mainloop_new();

	mainloop_api = pa_mainloop_get_api(m_pulseaudio_mainloop);
	pulseaudio_context = pa_context_new(mainloop_api, "cava device list");


	// This function connects to the pulse server
	pa_context_connect(pulseaudio_context, NULL, PA_CONTEXT_NOFLAGS,
			       NULL);


//        printf("connecting to server\n");

	//This function defines a callback so the server will tell us its state.
	pa_context_set_state_callback(pulseaudio_context,
			                  pulseaudio_context_state_callback,
			                  (void*)audio);



	//starting a mainloop to get default sink

	//starting with one nonblokng iteration in case pulseaudio is not able to run
	if (!(ret = pa_mainloop_iterate(m_pulseaudio_mainloop, 0, &ret))){
        printf("Could not open pulseaudio mainloop to "
                                          "find default device name: %d\n"
                                        "check if pulseaudio is running\n",
                   ret);

        exit(EXIT_FAILURE);
        }

	pa_mainloop_run(m_pulseaudio_mainloop, &ret);
	

}

void* input_pulse(void* data)
{

    struct audio_data *audio = (struct audio_data *)data;
    int i, n;
	int16_t buf[BUFSIZE / 2];

	/* The sample type to use */
	static const pa_sample_spec ss = {
		.format = PA_SAMPLE_S16LE,
		.rate =  44100,
		.channels = 2
		};
	static const pa_buffer_attr pb = {
	.maxlength = BUFSIZE * 2,
	.fragsize = BUFSIZE
	};

	pa_simple *s = NULL;
	int error;

	if (!(s = pa_simple_new(NULL, "cava", PA_STREAM_RECORD, audio->source, "audio for cava", &ss, NULL, &pb, &error))) {
		//fprintf(stderr, __FILE__": Could not open pulseaudio source: %s, %s. To find a list of your pulseaudio sources run 'pacmd list-sources'\n",audio->source, pa_strerror(error));
        sprintf(audio->error_message, __FILE__": Could not open pulseaudio source: %s, %s. To find a list of your pulseaudio sources run 'pacmd list-sources'\n",audio->source, pa_strerror(error));
		audio->terminate = 1;
        pthread_exit(NULL);
	}

	n = 0;
               
	while (1) {
        	/* Record some data ... */
        	if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
            //fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
        	//exit(EXIT_FAILURE);
            sprintf(audio->error_message, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
            audio->terminate = 1;
            pthread_exit(NULL);
		}

		 //sorting out channels

	        for (i = 0; i < BUFSIZE / 2; i += 2) {

                                if (audio->channels == 1) audio->audio_out_l[n] = (buf[i] + buf[i + 1]) / 2;

                                //stereo storing channels in buffer
                                if (audio->channels == 2) {
                                        audio->audio_out_l[n] = buf[i];
                                        audio->audio_out_r[n] = buf[i + 1];
                                        }

                                n++;
                                if (n == 2048 - 1)n = 0;
                        }

		if (audio->terminate == 1) {            		
			pa_simple_free(s);
			break;
		    }
        }

	return 0;
}
