#include <stdlib.h>

#include <poll.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <asoundlib.h>

#include <stdint.h>
#include <limits.h>
#include <glib.h>

#define bool int
#define false 0
#define true 1

snd_pcm_t *pcm = NULL;

bool init_audio_bug ()
{	
	bool result = false;
	snd_pcm_hw_params_t *params = NULL;
	uint32_t buffer_time = 500000; // request 0.5 seconds of buffer time.
	int err = 0;
	int dir = 0;
	int channels = 1;
	unsigned int rate = 44100;
	unsigned int actual_rate = rate;

	// Open a pcm device
	result = snd_pcm_open (&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0);
	if (result != 0) {
		fprintf (stderr, "AudioNode::Initialize (): cannot open audio device: %s\n", snd_strerror (result));
		pcm = NULL;
		return false;
	}

	snd_output_t *output = NULL;
	err = snd_output_stdio_attach (&output, stdout, 0);
	if (err < 0) {
		fprintf(stderr, "AudioNode::SetupHW (): Could not create alsa output: %s\n", snd_strerror (err));
	}

	err = snd_pcm_hw_params_malloc (&params);
	if (err < 0) {
		fprintf(stderr, "AudioNode::SetupHW (): Audio HW setup failed (malloc): %s\n", snd_strerror (err));
		return false;
	}

	// choose all parameters
	err = snd_pcm_hw_params_any (pcm, params);
	if (err < 0) {
		fprintf (stderr, "AudioNode::SetupHW (): Audio HW setup failed (no configurations available): %s\n", snd_strerror (err));
		goto cleanup;
	}
	
	if (output != NULL) {
		printf ("AudioNode::SetupHW (): hw configurations:\n");
		snd_pcm_hw_params_dump (params, output);
	}
	
	// enable software resampling
	err = snd_pcm_hw_params_set_rate_resample (pcm, params, 1);
	if (err < 0) {
		fprintf (stderr, "AudioNode::SetupHW (): Audio HW setup failed (could not enable resampling): %s\n", snd_strerror (err));
		goto cleanup;
	}
	
	// set transfer mode (mmap in our case)
	err = snd_pcm_hw_params_set_access (pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED);
	if (err < 0) {
		fprintf (stderr, "AudioNode::SetupHW (): Audio HW setup failed (access type not available for playback): %s\n", snd_strerror(err));
		goto cleanup;
	}

	// set audio format
	err = snd_pcm_hw_params_set_format (pcm, params, SND_PCM_FORMAT_S16);
	if (err < 0) {
		fprintf (stderr, "AudioNode::SetupHW (): Audio HW setup failed (sample format not available for playback): %s\n", snd_strerror(err));
		goto cleanup;
	}
	
	// set channel count
	err = snd_pcm_hw_params_set_channels (pcm, params, channels);
	if (err < 0) {
		fprintf (stderr, "AudioNode::SetupHW (): Audio HW setup failed (channels count %i not available for playback): %s\n", channels, snd_strerror (err));
		goto cleanup;
	}
	
	// set sample rate
	err = snd_pcm_hw_params_set_rate_near (pcm, params, &actual_rate, 0);
	if (err < 0) {
		fprintf (stderr, "AudioNode::SetupHW (): Audio HW setup failed (sample rate %i Hz not available for playback): %s\n", rate, snd_strerror (err));
		goto cleanup;
	} else if (actual_rate != rate) {
		fprintf (stderr, "AudioNode::SetupHW (): Audio HW setup failed (sample rate %i Hz not available for playback, only got %i Hz).\n", rate, actual_rate);
		goto cleanup;
	}
	
	// set the buffer time
	err = snd_pcm_hw_params_set_buffer_time_near (pcm, params, &buffer_time, &dir);
	if (err < 0) {
		fprintf (stderr, "AudioNode::SetupHW (): Audio HW setup failed (unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror (err));
		goto cleanup;
	}

	// write the parameters to device
	err = snd_pcm_hw_params (pcm, params);
	if (err < 0) {
		fprintf (stderr, "AudioNode::SetupHW (): Audio HW setup failed (unable to set hw params for playback: %s)\n", snd_strerror (err));
		goto cleanup;
	}
	
	printf ("AudioNode::SetupHW (): succeeded\n");
	if (output != NULL) {
		snd_pcm_hw_params_dump (params, output);
	}

	printf ("AudioNode::SetupHW (): hardware pause support: %s\n", snd_pcm_hw_params_can_pause (params) == 0 ? "no" : "yes"); 

	result = true;
	
cleanup:
	snd_pcm_hw_params_free (params);
	
	return result;
}

void *loop (void *context)
{
	printf ("Started loop\n");

	if (!init_audio_bug ())
		return NULL;

	int result;
	int size = 8568;
	void *silence = g_malloc0 (size);
	int i;
	snd_pcm_sframes_t delay;	
	int fd;

	fd = open ("mini.wav", O_RDONLY);
	if (fd < 0)
		printf ("Open failed: %s\n", strerror (errno));
	if (read (fd, silence, 8568) <= 0)
		printf ("Read failed: %s\n", strerror (errno));
	snd_pcm_writei (pcm, silence, size / 2);
	close (fd);

//	snd_pcm_delay (pcm);
	if ((result = snd_pcm_start (pcm)) < 0) {
		printf ("start failed: %s\n", snd_strerror (result));
	}
	printf ("Sleeping...\n");
	sleep (1);
	printf ("Slept\n");

	//snd_pcm_drop (pcm);
	snd_pcm_close (pcm);

	printf ("Exited loop\n");
}


int main ()
{
	pthread_t thread;
	pthread_create (&thread, NULL, loop, NULL);
	printf ("About to join\n");
	pthread_join (thread, NULL);
	printf ("Joined\n");
	return 0;
}

