

#include <unistd.h>

#include "convert.h"
#include "config.h"
#include "gui.h"
#include "output.h"
#include "configuration.h"
#include "error.h"

extern GtkBuilder *_builder_;						// work around for threads
extern Output output;

/*
 * callback from output thread
 */
gboolean cb_thread_output (gpointer data) {
	int mode = output.GetStatus();
	GtkWidget *btncapture = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn_inputcapture"));
	GtkWidget *btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn_inputstop"));
	GtkWidget *btnsnapshot = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn_inputsnapshot"));
	GtkWidget *btninputdest = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "input_btn_destpath"));
	GtkWidget *inputdest = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "input_entry_destpath"));

	if (mode & OUTPUT_MODE_SER_STARTED) {
		gtk_widget_set_sensitive(btnstop, true);
		gtk_widget_set_sensitive(btncapture, false);
		gtk_widget_set_sensitive(btnsnapshot, false);
		gtk_widget_set_sensitive(btninputdest, false);
		gtk_widget_set_sensitive(inputdest, false);
	}
	else {
		gtk_widget_set_sensitive(btnstop, false);
		gtk_widget_set_sensitive(btncapture, true);
		gtk_widget_set_sensitive(btnsnapshot, true);
		gtk_widget_set_sensitive(btninputdest, true);
		gtk_widget_set_sensitive(inputdest, true);
	}

	return false;
}


void input_getdestandsufix (std::string *sdir, std::string *ssufix) {
	GtkWidget *inputdest = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "input_entry_destpath"));
	GtkWidget *inputsufix = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn_input_filesufix"));
	if (sdir != NULL) 	*sdir = gtk_entry_get_text(GTK_ENTRY(inputdest));
	if (ssufix != NULL)	*ssufix = gtk_entry_get_text(GTK_ENTRY(inputsufix));
}


void cb_input_btncapture (GtkWidget *widget, gpointer data) {
	std::string sdir, ssufix;

	printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
	input_getdestandsufix(&sdir, &ssufix);
	output.SERStart(sdir, ssufix);
};


void cb_input_btnsnapshot (GtkWidget *widget, gpointer data) {
	std::string sdir, ssufix;

	printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
	input_getdestandsufix(&sdir, &ssufix);
	// output.Capture(sdir, ssufix);
};

void cb_input_btnstop (GtkWidget *widget, gpointer data) {
	printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
	output.SERStop();
};


//
// C / C++ Wrapper for the thread function
//
gpointer _OutputThread (gpointer data) {
	output.Thread ();
	return NULL;
};



/*
 *
 */
Output::Output() { // @suppress("Class members should be properly initialized")
	printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);

	outputser = NULL;
	g_mutex_init (&mutexin);
	g_mutex_init (&muteximage);
	g_mutex_init (&mutextmp);
	g_mutex_init (&mutex);
	running = 1;
	inFrame.Delete();
	mode = 0;

	thread = NULL;
	thread = g_thread_new("Output", _OutputThread, NULL);
};


void Output::NotifyGTK () {
	gdk_threads_add_idle(cb_thread_output, NULL);
}


/*
 * open output file and set mode flag
 * all the other SER stuff will be set with the first frame
 */
void Output::SERStart (std::string destpath, std::string sufix) {
	time_t t = time(NULL);
	struct tm *tmptr;
	char fname[LEN_FILENAME];
	char fullfname[LEN_FULLFILENAME];

	//
	// check to create file, if not possible try creating the directory and create the file again
	tmptr = localtime(&t);
	strftime(fname, LEN_FILENAME, "%Y%m%d-%H%M%S", tmptr);
	snprintf (fullfname, LEN_FULLFILENAME, "%s/%s-%s.ser", destpath.c_str(), fname, sufix.c_str());
	outputfilename = fullfname;
	printf ("%s:%d SERStart Destination File: %s\n", __FILE__, __LINE__, outputfilename.c_str());

	mode |= OUTPUT_MODE_SER_STARTED;
	NotifyGTK();
};


/*
 * clode output file
 */
void Output::SERStop () {
	mode &= ~(OUTPUT_MODE_SER_STARTED);
	if (outputser) {
		// close SER and delete object --> deleting the object should also rewrite the write the header
		delete outputser;
		outputser = NULL;
	}
	NotifyGTK();
};


Output::~Output() {
	running = 0;
	if (thread) {
		g_thread_join (thread);
		thread = NULL;
	}
};



int Output::NewFrame(VideoFrameRaw *rawframe) {
	if (rawframe == NULL) return -1;

	//	printf ("%s:%d Output::NewFrame Frame Raw: %d x %d    Type: %s\n", __FILE__, __LINE__, rawframe->w, rawframe->h, convert_from_pixelformat(rawframe->pixfmt).c_str());
	if (inFrameNew) {
		printf ("%s:%d Output::NewFrame truncating one frame.\n", __FILE__, __LINE__);
	}

	LockInMutex();
	inFrame.CopyFrom(rawframe);
	inFrameNew = 1;
	UnLockInMutex();
	return 0;
};


//
// NewFrame: will set new frame
// Thread: newFrame |------> Find Object --- not found  ---> send gui information
void Output::Thread() {
	while (running) {
		// check for new frame
		LockInMutex();

		if (inFrameNew == 1) {
			if (mode & OUTPUT_MODE_SER_STARTED) {
				if (outputser == NULL) {	// first frame of ser output
					int pixsize;
					int serformat;
					outputserw = inFrame.w;
					outputserh = inFrame.h;
					outputserfmt = inFrame.pixfmt;

					if (!ser_convert_type_to_ser(outputserfmt, &pixsize, &serformat)) {
						errormessage_display((char*)"windows-main", (char*)"Error", (char*)"Pixelformat [%s] not supported.", convert_from_pixelformat(outputserfmt).c_str());
						SERStop();
					}

					outputser = new SER();
					if (outputser) outputser->setWidth(outputserw);
					if (outputser) outputser->setHeight(outputserh);
					if (outputser) outputser->setColorID(serformat);
					if (outputser) outputser->setPixelDepth(pixsize);
					if (outputser->setFile((char *)outputfilename.c_str()) == -1) {
						errormessage_display((char*)"windows-main", (char*)"Error", (char*)"Could not set output file [%s]. Error: %s", outputfilename.c_str(), strerror(errno));
						SERStop();
					}
					if (outputser) outputser->setObserver((char *)"FIXME:read username from OS");
					if (outputser) outputser->setTelescope((char *)"FIXME:not implemented yet");
					if (outputser) outputser->setInstrument((char *)"FIXME:just read device from driver");
					if (outputser) outputser->setNumberOfFrames(0);
					if (outputser) outputser->writeHeader();
				}

				else {
					//
					// check if the frame type or size changed
					if (outputserw != inFrame.w || outputserh != inFrame.h || outputserfmt != inFrame.pixfmt) {
						errormessage_display((char*)"windows-main", (char*)"Error", (char*)"Frame format or dimensions changed.\n(%s @ %d x %d) != (%s @ %d x %d)\n",
								convert_from_pixelformat(outputserfmt).c_str(), outputserw, outputserh,
								convert_from_pixelformat(inFrame.pixfmt).c_str(), inFrame.w, inFrame.h);
						SERStop();
					}
				}

				if (outputser) {	// we need to check in case outputser got deleted.
					//
					// write frame
					outputser->appendFrame(inFrame.data);
				}
			}

			inFrameNew = 0;
		}

		UnLockInMutex();

		usleep (1000);			// sleep 10ms
	}
}


