/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#+
#+     Internet Radio Automation & Encoding Toolkit
#+
#+     Copyright (C) 2018, 2019, 2020, 2021, 2022 by Kevin C. O'Kane
#+
#+     Kevin C. O'Kane
#+     kc.okane@gmail.com
#+     https://www.cs.uni.edu/~okane
#+     https://threadsafebooks.com/
#+
#+ This program is free software; you can redistribute it and/or modify
#+ it under the terms of the GNU General Public License as published by
#+ the Free Software Foundation; either version 2 of the License, or
#+ (at your option) any later version.
#+
#+ This program is distributed in the hope that it will be useful,
#+ but WITHOUT ANY WARRANTY; without even the implied warranty of
#+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#+ GNU General Public License for more details.
#+
#+ You should have received a copy of the GNU General Public License
#+ along with this program; if not, write to the Free Software
#+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

// Oct 12, 2022

#define _GNU_SOURCE


#define _GNU_SOURCE

#include "includes.h"
#include "common-externs.h"


#define SLASH '/'

extern GThread *camThread;
extern int camFlag;

void	camera0();

static struct {
	void* start;
	size_t length;
	} *buffers;

static struct v4l2_buffer bufferinfo;
static int fd;


extern struct THR1 thr_data;

static int xioctl (int fd, int request, void * arg) {
	int r;

	do r = ioctl (fd, request, arg);
	while (-1 == r && EINTR == errno);

	return r;
	}

int	cam_start(struct THR1 *tp) {

	sleep(1); // wait for gtk to start

	if( (fd = open("/dev/video0", O_RDWR)) < 0) {
		printf("*** Camera 0 not found.\n");
		// gtk_widget_hide(GTK_IMAGE(cam0_image));
		perror("open");
		gtk_widget_hide(camdraw);
		printf("--------------------------------------\n");
		return 0;
		}

	static struct v4l2_capability cap;

	if (xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
		perror("VIDIOC_QUERYCAP");
		return 0;
		}

	if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
		fprintf(stderr, "The device does not handle single-planar video capture.\n");
		return 0;
		}

	printf("Video card detected: %s\n", cap.card);

	static struct v4l2_format format;

//----------------------------------------------------------
//	v4l2-ctl -d /dev/video0 --list-formats-ext > modes
//----------------------------------------------------------

	format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	if(xioctl(fd, VIDIOC_G_FMT, &format) < 0) { // get format
		perror("VIDIOC_G_FMT");
		return 0;
		}

	printf("width %d\n", format.fmt.pix.width);
	printf("height %d\n", format.fmt.pix.height);
	printf("colorspace %d\n", format.fmt.pix.colorspace);
	printf("sizeimage %d\n", format.fmt.pix.sizeimage);
	printf("pixelformat %x\n", format.fmt.pix.pixelformat);
	printf("bytesperline %d\n", format.fmt.pix.bytesperline);

	format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;

	format.fmt.pix.width = 640;
	format.fmt.pix.height = 360;

	if (xioctl(fd, VIDIOC_S_FMT, &format) < 0) {
		perror("VIDIOC_S_FMT");
		return 0;
		}

	struct v4l2_streamparm streamparm;
	memset(&streamparm, 0, sizeof(streamparm));

	streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	if (xioctl(fd, VIDIOC_G_PARM, &streamparm) != 0) {
		return 0;
		}

	printf("FR numerator %d\n", streamparm.parm.capture.timeperframe.numerator);
	printf("FR denominator %d\n", streamparm.parm.capture.timeperframe.denominator);

	streamparm.parm.capture.timeperframe.numerator = 1;
	streamparm.parm.capture.timeperframe.denominator = 15;

	if(xioctl(fd,VIDIOC_S_PARM, &streamparm) !=0) {
		return 0;
		}

	static struct v4l2_requestbuffers bufrequest;

	memset(&bufrequest, 0, sizeof(bufrequest));
	bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	bufrequest.memory = V4L2_MEMORY_MMAP;
	bufrequest.count = 10;
	bufrequest.count = 100;

	if (xioctl(fd, VIDIOC_REQBUFS, &bufrequest) < 0) {
		perror("VIDIOC_REQBUFS");
		return 0;
		}

	printf("buffers acquired %d\n", bufrequest.count);

	buffers = calloc(bufrequest.count, sizeof(*buffers));

	int brc = bufrequest.count;

	for (int i = 0; i < bufrequest.count; i++) {

		memset(&bufferinfo, 0, sizeof(bufferinfo));

		bufferinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		bufferinfo.memory = V4L2_MEMORY_MMAP;
		bufferinfo.index = i;

		if (xioctl(fd, VIDIOC_QUERYBUF, &bufferinfo) < 0) {
			perror("VIDIOC_QUERYBUF");
			return 0;
			}

		buffers[i].length = bufferinfo.length;

		buffers[i].start = mmap(
		                       NULL,
		                       bufferinfo.length,
		                       PROT_READ | PROT_WRITE,
		                       MAP_SHARED,
		                       fd,
		                       bufferinfo.m.offset
		                   );

		if (buffers[i].start == MAP_FAILED) {
			perror("mmap");
			return 0;
			}

		}

//------------------------
//	Activate streaming
//------------------------

	static int type;
	type = bufferinfo.type;

	if(xioctl(fd, VIDIOC_STREAMON, &type) < 0) {
		perror("VIDIOC_STREAMON");
		return 0;
		}

	time_t t = time(0);

	for (int j=0; j<brc; j++) {
		bufferinfo.index=j;
		if(xioctl(fd, VIDIOC_QBUF, &bufferinfo) < 0) {
			perror("VIDIOC_QBUF");
			return 0;
			}
		}

	while (1) {

		if (gtk_stack_get_visible_child(GTK_STACK(stack1)) != Cam0)
			sleep(2);

		for (int j=0; j<brc; j++) {
			bufferinfo.index=j;
			if(xioctl(fd, VIDIOC_DQBUF, &bufferinfo) < 0) {
				perror("VIDIOC_QBUF");
				return 0;
				}

			/**************************************
						printf("sequence %d; buffer time %ld %ld; "
							"index %d\n",
							bufferinfo.sequence,
							bufferinfo.timestamp.tv_sec,
							bufferinfo.timestamp.tv_usec,
							bufferinfo.index);
			**************************************/

			gdk_threads_enter();


			int jpgfile =0;
			if((jpgfile = open("/tmp/myimage.jpeg", O_WRONLY | O_CREAT, 0660)) < 0) {
				perror("open");
				return 0;
				}

			write(jpgfile, buffers[j].start, buffers[j].length);
			close(jpgfile);
			camera0();

			gdk_threads_leave();

			if (! tp->camFlag) {

				printf("camera exit\n");

				if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0) {
					perror("VIDIOC_STREAMOFF");
					}

				close(fd);

				g_thread_unref(tp->camThread);
				g_thread_exit(NULL);

				return 1;
				}

			bufferinfo.index=j;
			if(xioctl(fd, VIDIOC_QBUF, &bufferinfo) < 0) {
				perror("VIDIOC_QBUF");
				return 0;
				}
			}

		}

	}

void	camera0() {
	GError *err = NULL;
	cambuf = gdk_pixbuf_new_from_file ("/tmp/myimage.jpeg", &err);
	if (cambuf == NULL) return;
	gtk_image_set_from_pixbuf(GTK_IMAGE(cam0_image), cambuf);
	g_object_unref(cambuf);
	while (gtk_events_pending ()) gtk_main_iteration ();
	return;
	}

void	on_cam0_active_toggled(GtkWidget *w) {

	if (! gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
		thr_data.camFlag = 0;
		gtk_widget_hide(w);
		gtk_widget_hide(cam0_image);
		}

	else {
//		attempt to re-start cam - not currently workking
//		thr_data.camFlag = 1;
//        	thr_data.camThread = g_thread_new ("camThread", cam_start, &thr_data);
		}

	}
