#include "../../src/mixframemanager.h"

gboolean stop_thread = FALSE;
GCond* data_cond = NULL;
GMutex* data_mutex = NULL;


void *deque_function(void *data) {

	MixFrameManager *fm = (MixFrameManager *) data;
	MIX_RESULT mixresult;
	MixVideoFrame *mvf = NULL;
	guint64 pts;
	while(!stop_thread) {

		g_mutex_lock (data_mutex);

		mixresult = mix_framemanager_dequeue(fm, &mvf);
		if(mixresult == MIX_RESULT_SUCCESS) {
			mixresult = mix_videoframe_get_timestamp(mvf, &pts);
			g_print("dequeued timestamp = %"G_GINT64_FORMAT"\n", pts);
			/* mix_videoframe_unref(mvf); */
		} else if(mixresult == MIX_RESULT_FRAME_NOTAVAIL) {
			g_print("mixresult == MIX_RESULT_FRAME_NOTAVAIL\n");
			g_cond_wait (data_cond, data_mutex);
		}

		g_mutex_unlock (data_mutex);

	}
}

void shuffle(GPtrArray *list) {
	guint idx, jdx;
	guint len = list->len;
	for (idx = 0; idx < len - 1; idx++) {
		jdx = rand() % len;
		if (idx != jdx) {
			gpointer tmp = g_ptr_array_index(list, jdx);
			g_ptr_array_index(list, jdx) = g_ptr_array_index(list, idx);
			g_ptr_array_index(list, idx) = tmp;
		}
	}
}

int main() {
	MIX_RESULT mixresult;

	gint fps_n = 24000;
	gint fps_d = 1001;

/*
	gint fps_n = 2500000;
	gint fps_d = 104297;
*/
	GPtrArray *fa = NULL;
	MixFrameManager *fm = NULL;
	MixVideoFrame *mvf = NULL;
	MixVideoFrame *mvf_1st = NULL;

	gint idx = 0;
	guint64 pts = 0;

	GThread *deque_thread = NULL;
	GError *deque_thread_error = NULL;

	/* first ting first */
	g_type_init();

	/* create frame manager */
	fm = mix_framemanager_new();
	if (!fm) {
		goto cleanup;
	}

	/* initialize frame manager */
	mixresult = mix_framemanager_initialize(fm,
			MIX_FRAMEORDER_MODE_DISPLAYORDER, fps_n, fps_d);
	if (mixresult != MIX_RESULT_SUCCESS) {
		goto cleanup;
	}

	/* create frame_array */
	fa = g_ptr_array_sized_new(64);
	if (!fa) {
		goto cleanup;
	}

	for (idx = 0; idx < 16; idx++) {
		/* generate MixVideoFrame */
		mvf = mix_videoframe_new();
		if (!mvf) {
			goto cleanup;
		}

		pts = idx * G_USEC_PER_SEC * G_GINT64_CONSTANT(1000) * fps_d / fps_n;
		mixresult = mix_videoframe_set_timestamp(mvf, pts);
		if (mixresult != MIX_RESULT_SUCCESS) {
			goto cleanup;
		}

		g_print("original timestamp = %"G_GINT64_FORMAT"\n", pts);

		if (idx == 0) {
			mvf_1st = mvf;
		} else {
			g_ptr_array_add(fa, (gpointer) mvf);
		}
	}

	/* shuffle the array */
	shuffle( fa);

	data_mutex = g_mutex_new ();
	if(!data_mutex) {
		goto cleanup;
	}

	data_cond =  g_cond_new();
	if(!data_cond) {
		goto cleanup;
	}


	/* create another thread to dequeue */
	deque_thread = g_thread_create((GThreadFunc) deque_function, (void *) fm,
			TRUE, &deque_thread_error);
	if (!deque_thread) {
		goto cleanup;
	}

	/* enqueue */
	mixresult = mix_framemanager_enqueue(fm, mvf_1st);
	if (mixresult != MIX_RESULT_SUCCESS) {
		goto cleanup;
	}

	mixresult = mix_videoframe_get_timestamp(mvf_1st, &pts);
	if (mixresult != MIX_RESULT_SUCCESS) {
		goto cleanup;
	}
	g_print("shuffled timestamp = %"G_GINT64_FORMAT"\n", pts);

	for (idx = 0; idx < fa->len; idx++) {

		g_mutex_lock (data_mutex);

		/* wait for 100ms to enqueue another frame */
		g_usleep(G_USEC_PER_SEC / 10 );

		mvf = (MixVideoFrame *) g_ptr_array_index(fa, idx);
		mixresult = mix_framemanager_enqueue(fm, mvf);

		/* wake up deque thread */
		g_cond_signal (data_cond);


		g_mutex_unlock (data_mutex);

		if (mixresult != MIX_RESULT_SUCCESS) {
			goto cleanup;
		}

		mixresult = mix_videoframe_get_timestamp(mvf, &pts);
		if (mixresult != MIX_RESULT_SUCCESS) {
			goto cleanup;
		}

		g_print("shuffled timestamp = %"G_GINT64_FORMAT"\n", pts);
	}

	getchar();

	stop_thread = TRUE;

	/* wake up deque thread */
	g_cond_signal (data_cond);

	g_thread_join(deque_thread);

cleanup:

	if(data_mutex) {
		g_mutex_free(data_mutex);
	}

	if(data_cond) {
		g_cond_free(data_cond);
	}

	if (fm) {
		mix_framemanager_unref(fm);
	}

	if (fa) {
		g_ptr_array_free(fa, TRUE);
	}

	return 0;
}