/* compile with gcc -Wall -o sample-photobooth -lgphoto2 sample-photobooth.c
 *
 */

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

#include <gphoto2/gphoto2.h>

#include "samples.h"

#define CONFIG_FILE	"config.txt"
#define PREVIEW		"preview.jpg"

static void errordumper(GPLogLevel level, const char *domain, const char *str, void *data) {
/*	printf("%s (data %p)\n", str,data);*/
}

/* set by signal handler */
static int capture_now = 0;
static int read_config = 0;

static int
capture_to_file(Camera *camera, GPContext *context, char *fn) {
	int fd, retval;
	CameraFile *file;
	CameraFilePath camera_file_path;
	char	*s, *t;

	retval = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
	if (retval < GP_OK) return retval;

	s = strrchr (fn, '.');
	t = strrchr (camera_file_path.name, '.');
	/* replace the suffix by the one sent by the camera .. for RAW capture */
	if (t && s && (strlen(t+1) == 3) && (strlen(s+1) == 3)) {
		strcpy (s+1, t+1);
	}

	fd = open (fn, O_CREAT | O_WRONLY | O_BINARY, 0644);
	if (fd == -1)
		return GP_ERROR;

	retval = gp_file_new_from_fd(&file, fd);
	if (retval < GP_OK) {
		close(fd);
		unlink(fn);
		return retval;
	}

	retval = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name,
		     GP_FILE_TYPE_NORMAL, file, context);
	if (retval < GP_OK) {
		gp_file_free(file);
		unlink(fn);
		return retval;
	}

	retval = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name, context);
	gp_file_free(file);
	return GP_OK;
}

static void
sig_handler_capture_now (int sig_num)
{
#if !defined (WIN32)
	signal (SIGUSR1, sig_handler_capture_now);
#endif
	capture_now = 1;
}

static void
sig_handler_read_config (int sig_num)
{
#if !defined (WIN32)
	signal (SIGUSR2, sig_handler_read_config);
#endif
	read_config = 1;
}

int
main(int argc, char **argv) {
	Camera	*camera;
	int	retval;
	int	capturecnt = 0;
	GPContext *context = sample_create_context();

	printf("Sample photobooth.\n");
	printf("Continuously stores previews in 'preview.jpg'.\n");
	printf("kill -USR1 %d to take a capture.\n", getpid());
	printf("kill -USR2 %d to read the 'config.txt'.\n", getpid());
	printf("kill -TERM %d to finish.\n", getpid());

#if !defined (WIN32)
	signal (SIGUSR1, sig_handler_capture_now);
	signal (SIGUSR2, sig_handler_read_config);
#endif

	gp_log_add_func(GP_LOG_ERROR, errordumper, 0);
	gp_camera_new(&camera);

	retval = gp_camera_init(camera, context);
	if (retval != GP_OK) {
		printf("  Retval: %d\n", retval);
		exit (1);
	}

	while (1) {
		CameraFile *file;
		char output_file[32];

		/*
		 * Capture a full picture on demand. Use unique filenames.
		 */
		if (capture_now) {
			capture_now = 0;
			sprintf(output_file, "image-%04d.jpg", capturecnt++);
			retval = capture_to_file(camera, context, output_file);
			if (retval == GP_OK)
				fprintf (stderr, "captured to %s\n", output_file);
		}

		/*
		 * Read configuration changes from "config.txt".
		 * Expected are key=value pairs where value is the value seen in "--get-config key", e.g.
		 * 	iso=Auto
		 * 	iso=200
		 * 	shutterspeed=1/200
		 */
		if (read_config) {
			FILE	*config;
			char	buf[512];

			read_config = 0;

			config = fopen (CONFIG_FILE, "r");
			while (fgets (buf, sizeof(buf), config)) {
				char	*s;

				/* kill linefeeds */
				s = strchr(buf,'\r');
				if (s)
					*s=0;
				s = strchr(buf,'\n');
				if (s)
					*s=0;

				s = strchr(buf,'=');
				if (!s) continue;

				*s=0;
				retval = set_config_value_string (camera, buf, s+1, context);
				if (retval < GP_OK)
					fprintf (stderr, "setting configuration '%s' to '%s' failed with %d.\n", buf, s+1, retval);
				else
					fprintf (stderr, "changed configuration '%s' to '%s'\n", buf, s+1);
			}
			fclose (config);
		}

		/*
		 * Capture a preview on every loop. Save as preview.jpg.
		 */
		retval = gp_file_new(&file);
		if (retval != GP_OK) {
			fprintf(stderr,"gp_file_new: %d\n", retval);
			exit(1);
		}

		retval = gp_camera_capture_preview(camera, file, context);
		if (retval != GP_OK) {
			fprintf(stderr,"gp_camera_capture_preview failed: %d\n", retval);
			exit(1);
		}
		retval = gp_file_save(file, PREVIEW);
		if (retval != GP_OK) {
			fprintf(stderr,"saving preview failed: %d\n", retval);
			exit(1);
		}
		gp_file_unref(file);
		/*
		 * Check and drain the event queue.
		 * 	Download newly captured images.
		 *	Ignore the rest events.
		 */
		while (1) {
			CameraEventType	evttype;
			CameraFilePath	*path;
			void    	*evtdata;
			int		fd;

			evtdata = NULL;
			retval = gp_camera_wait_for_event (camera, 1, &evttype, &evtdata, context);
			if (retval != GP_OK) break;
			switch (evttype) {
			case GP_EVENT_FILE_ADDED: {
				char	*t;

				path = (CameraFilePath*)evtdata;
				t = strrchr (path->name, '.');
				if (t && (strlen(t+1) == 3)) {
					sprintf(output_file, "image-%04d.%s", capturecnt++, t+1);
				} else {
					sprintf(output_file, "image-%04d.jpg", capturecnt++);
				}

				fd = open (output_file, O_CREAT | O_WRONLY | O_BINARY, 0644);
				retval = gp_file_new_from_fd(&file, fd);
				retval = gp_camera_file_get(camera, path->folder, path->name,
					     GP_FILE_TYPE_NORMAL, file, context);

				retval = gp_camera_file_delete(camera, path->folder, path->name, context);
				gp_file_free(file);
				free (evtdata);
				fprintf (stderr, "saved to %s\n", output_file);
				break;
			}
			case GP_EVENT_FILE_CHANGED:
				path = (CameraFilePath*)evtdata;
				printf("File changed on camera: %s / %s\n", path->folder, path->name);
				free (evtdata);
				break;
			case GP_EVENT_FOLDER_ADDED:
				path = (CameraFilePath*)evtdata;
				printf("Folder added on camera: %s / %s\n", path->folder, path->name);
				free (evtdata);
				break;
			case GP_EVENT_CAPTURE_COMPLETE:
				printf("Capture Complete.\n");
				break;
			case GP_EVENT_TIMEOUT:
				break;
			case GP_EVENT_UNKNOWN:
				if (evtdata) {
					printf("Unknown event: %s.\n", (char*)evtdata);
					free (evtdata);
				} else {
					printf("Unknown event.\n");
				}
				break;
			default:
				printf("Type %d?\n", evttype);
				break;
			}
			if (evttype == GP_EVENT_TIMEOUT)
				break;
		}
	}
	gp_camera_exit(camera, context);
	return 0;
}
