
#include <errno.h>
#include <string.h>
#include "video.h"
#include "config.h"
#include "gui.h"
#include "error.h"
#include "convert.h"
#include "videodev.h"

VideoFrameRaw::VideoFrameRaw() {
	size = 0;
	data = NULL;
	w = 0;
	h = 0;
	pixfmt = 0x0;
}

VideoFrameRaw::~VideoFrameRaw() {
	if (data != NULL) {
		free (data);
		data = NULL;
	}
}


int VideoFrameRaw::CopyFrom(int spixfmt, int sw, int sh, int ssize, unsigned char *sdata) {
	if (size != ssize) {
		if (ReAlloc(ssize) != 0) {
			errorexit((char*) "%s:%d %s could not reallocate memory. Error:%s\n",
					__FILE__, __LINE__, __FUNCTION__, strerror(errno));
		}
	}

	if (data == NULL) {
		errorexit((char*) "%s:%d %s invalid pointer of data. Error:%s\n",
				__FILE__, __LINE__, __FUNCTION__, strerror(errno));
	}

	w = sw;
	h = sh;
	pixfmt = spixfmt;
	memcpy (data, sdata, ssize);

	return 1;
}


int VideoFrameRaw::CopyFrom(VideoFrameRaw *src) {
	if (src == NULL) return 0;
	return CopyFrom(src->pixfmt, src->w, src->h, src->size, src->data);
}


int VideoFrameRaw::CopyFrom(VideoFrame *src) {
	if (src == NULL) return 0;
	return CopyFrom(V4L2_PIX_FMT_RGB24, src->w, src->h, src->size, src->data);
}


int VideoFrameRaw::ReAlloc(int newsize) {
	unsigned char *newdata = NULL;
	if (newsize == 0) {
		free (data);
		data = NULL;
		size = newsize;
		return 0;
	}
	if (data == NULL) {
		if ((data = (unsigned char*) malloc (newsize)) == NULL) return -1;
	}
	else if (size < newsize || size > newsize) {
		if ((newdata = (unsigned char*) realloc (data, newsize)) == NULL) {
			free (data);
			data = NULL;
			size = 0;
			return -1;
		}
		data = newdata;
	}
	size = newsize;
	return 0;
}


int VideoFrameRaw::RectCopyFrom(VideoFrameRaw *src, int rectx, int recty, int rectw, int recth) {
	int res = 0;
	pixfmt = src->pixfmt;

	res = PixCopy (src->data, src->pixfmt, src->w, src->h,
				 &data, &size, &w, &h,
				 rectx, recty, rectw, recth);

	return res;
}


#define VIDEOFRAME_DEPTH_BYTES 3
VideoFrame::VideoFrame() {
	data = NULL;
	w = 0;
	h = 0;
	size = 0;
};


VideoFrame::~VideoFrame() {
	if (data) {
		free (data);
		data = NULL;
	}

	w = 0;
	h = 0;
	size = 0;
};


//
// we will only allocate a new image if the needed size increases
void VideoFrame::SetSize(int nw, int nh) {
	uint32_t newsize = VIDEOFRAME_DEPTH_BYTES * nh * nw;

	if (newsize > size || data == NULL) {
		unsigned char *newdata = (unsigned char *) malloc (newsize);
		if (data != NULL) free (data);
		data = newdata;
		size = newsize;
	}
	w = nw;
	h = nh;
};


VideoFrame VideoFrame::operator=(VideoFrame rightside) {
	w = rightside.w;
	h = rightside.h;
	size = rightside.size;
	if (size > 0) data = (unsigned char *) malloc (rightside.size);
	if (rightside.data != NULL && size > 0) {
		memcpy (data, rightside.data, size);
	}
	return *this;
}


void VideoFrame::CopyFrom(VideoFrame *source) {
	SetSize (source->w, source->h);
	memcpy(data, source->data, VIDEOFRAME_DEPTH_BYTES * w * h);
}



void VideoFrame::CopyFrom(FloatImage *source) {
	float min, max, f;
	int x, y, i, idx;
	unsigned char v;

	SetSize (source->w, source->h);

	// find min max;
	min = source->data[0];
	max = source->data[0];
	for (y = 0; y < h; y++) for (x = 0; x < w; x++)
		for (i = 0; i < 3; i++) {
			idx = 3 * (y * w + x);
			if (min > source->data[idx+i]) min = source->data[idx+i];
			if (max < source->data[idx+i]) max = source->data[idx+i];
		}

	for (y = 0; y < h; y++) for (x = 0; x < w; x++)
		for (i = 0; i < 3; i++) {
			idx = 3 * (y * w + x);
			f = (float)(255.0 * (source->data[idx+i]-min) / (max-min));
			if (f < 0.0) v = 0;
			else if (f > 255.0) v = 255;
			else v = f;
			data[idx+i] = v;
		}
}



void VideoFrame::CopyTo(FloatImage *dest) {
	int x, y, i, idx;

	if (dest == NULL) return;

	dest->SetSize (w, h);
	for (y = 0; y < h; y++) for (x = 0; x < w; x++)
		for (i = 0; i < 3; i++) {
			idx = 3 * (y * w + x);
			dest->data[idx+i] = data[idx+i];
		}
}


void VideoFrame::ToPixbuf(GdkPixbuf** dest) {
	int destw, desth, bytelen;
	unsigned char *destpixel;

	if (dest == NULL) return;

	desth = gdk_pixbuf_get_height(*dest);
	destw = gdk_pixbuf_get_width(*dest);

	// check if the memory allocation way ok.
	if (destw != w || desth != h) {
		g_object_unref (*dest);
		*dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, w, h);
	}

	// copy data
	destpixel = gdk_pixbuf_get_pixels(*dest);
	bytelen = gdk_pixbuf_get_byte_length(*dest);
	if (bytelen < h * w * 3) {
		printf ("******* Error: %s:%d bytelen (set to:%d) is not %d\n", __FILE__, __LINE__, bytelen,  (h * w * 3));
		printf ("******* please inform the maintainer of this project.\n");
		printf ("******* this should not have happened at all.\n");
		exit (-1);
	}

	memcpy (destpixel, data, 3*w*h);
}


FloatImage::FloatImage() {
	data = NULL;
	w = 0;
	h = 0;
	size = 0;
};


FloatImage::~FloatImage() {
	if (data) {
		free (data);
		data = NULL;
	}

	w = 0;
	h = 0;
	size = 0;
};


//
// we will only allocate a new image if the needed size increases
void FloatImage::SetSize(int nw, int nh) {
	uint32_t newsize = 3 * nh * nw * sizeof(float);

	if (newsize > size || data == NULL) {
		float *newdata = (float *) malloc (newsize);
		if (data != NULL) free (data);
		data = newdata;
		size = newsize;
	}
	w = nw;
	h = nh;
};


FloatImage FloatImage::operator=(FloatImage rightside) {
	w = rightside.w;
	h = rightside.h;
	size = rightside.size;
	if (size > 0) data = (float *) malloc (rightside.size);
	if (rightside.data != NULL && size > 0) {
		memcpy (data, rightside.data, size);
	}
	return *this;
}



void FloatImage::CopyFrom(FloatImage *source) {
	SetSize (source->w, source->h);
	memcpy(data, source->data, 3 * w * h);
}

