
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib.h>
#include <sys/time.h>
#include <string>
#include <time.h>
#include <fcntl.h>
#ifdef BUILD_WINDOWS
# include <backtrace.h>
#else
# include <execinfo.h>
#endif

#include "debug.h"
#include "convert.h"
#include "gui.h"
#include "video.h"
#include "configuration.h"
#include "debayer.h"


extern GtkBuilder *_builder_;						// work around for threads
GtkWidget *debug_da = NULL;
GdkPixbuf *debug_pixbuf = NULL;
VideoFrame debug_frame;
int debug_uninit = 1;
GMutex debug_mutex;
time_t debug_time;
#define LEN 2048

void debug_init() {
	debug_uninit = 0;
	g_mutex_init (&debug_mutex);
	debug_time = time(NULL);
}

void debug_tofile(char *fname, int isheader, char *fmt, ...) {
	struct timeval tv;
	va_list args;
	char buffer[LEN];
	int fd, len;
	std::string fn;
	
	if (debug_uninit) debug_init();
	debug_lock_mutex();

	// locale set to C, floating points decimals are .
	std::string s = setlocale(LC_ALL, NULL);
	setlocale (LC_ALL, "C");

	// append time to filename to the start of the filename
	struct tm *timetm = localtime(&debug_time);
	char timestr[64] = "";
	strftime(timestr, 63, "%Y%m%d-%H%M%S", timetm);
	fn = "debug-" + (std::string)timestr + "-" + (std::string)fname;

	gettimeofday(&tv,NULL);

#ifdef BUILD_WINDOWS
	fd = open(fn.c_str(), O_WRONLY | O_APPEND | O_CREAT);
#else
	fd = open(fn.c_str(), O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
	if (fd < 0) errorexit((char*)"%s:%d could not open debug '%s' file. Error:%s\n", __FILE__, __LINE__, fname, strerror(errno));

	if (isheader)   snprintf (buffer, 32, "time,");
	else 			snprintf (buffer, 32, "%lld,", (long long int) (tv.tv_sec)*1000 + (long long int) tv.tv_usec/1000);
	len = strlen (buffer);
	va_start (args, fmt);
	vsnprintf (buffer+len, LEN-1-len, fmt, args);
	va_end (args);
	buffer[LEN-1] = 0;
	write (fd, buffer, strlen(buffer));
	close (fd);

	// reset locale to user setting
	setlocale (LC_ALL, s.c_str());

	debug_unlock_mutex();
}


gboolean debug_thread_cb (gpointer data) {
	debug_lock_mutex();

	//
	// create video drawarea if needed
	if (debug_da == NULL)
		debug_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "debug-da"));

	if (debug_pixbuf == NULL) {
		debug_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, 100, 100);
		memset (gdk_pixbuf_get_pixels(debug_pixbuf), 0, 3 * gdk_pixbuf_get_height(debug_pixbuf) * gdk_pixbuf_get_width(debug_pixbuf));
	}

	debug_frame.ToPixbuf(&debug_pixbuf);				// convert Frame to pixeldata

	// redraw drawarea on screen.
    gdk_window_invalidate_rect(gtk_widget_get_window(debug_da), NULL, true);

	debug_unlock_mutex();
	return false;
}


inline void debug_lock_mutex() {
	g_mutex_lock(&debug_mutex);
};


inline void debug_unlock_mutex() {
	g_mutex_unlock(&debug_mutex);
};


void debug_drawraw(unsigned char *data, int pixfmt, int w, int h) {
	ConvertData cd;
	int size, bytesperpixel = 0;

	switch (pixfmt) {
	case (V4L2_PIX_FMT_SGRBG8):
		bytesperpixel = 1;
		break;
	case (V4L2_PIX_FMT_SGRBG16):
		bytesperpixel = 2;
		break;
	case (V4L2_PIX_FMT_BGR32):
	case (V4L2_PIX_FMT_RGB32):
		bytesperpixel = 4;
		break;
	case (V4L2_PIX_FMT_BGR24):
	case (V4L2_PIX_FMT_RGB24):
		bytesperpixel = 3;
		break;
	default:
		return;
	}

	size = bytesperpixel * w * h;
	debug_lock_mutex();

	ConvertStart(&cd, pixfmt);
	Convert(&cd, &debug_frame, data, size, pixfmt, w, h);
	ConvertStop(&cd, pixfmt);

	gdk_threads_add_idle(debug_thread_cb, NULL);

	debug_unlock_mutex();
};


#define BT_BUF_SIZE 100
void debug_backtrace () {
#ifndef BUILD_WINDOWS
	unsigned int nptrs;
	void *buffer[BT_BUF_SIZE];
	char **strings;

	nptrs = backtrace(buffer, BT_BUF_SIZE);
	printf("backtrace() returned %d addresses\n", nptrs);

	/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
	 *             would produce similar output to the following: */

	strings = backtrace_symbols(buffer, nptrs);
	if (strings == NULL) {
		perror("backtrace_symbols");
		exit(EXIT_FAILURE);
	}

	for (size_t j = 0; j < nptrs; j++)
		printf("%s\n", strings[j]);
	free(strings);
#endif
};

