/* jpeg-read.c
 * This file is part of Amsam photo cropping program
 * Copyright 2021, 2023, 2024, 2025, 2026 Nandakumar Edamana
 * Distributed under GNU General Public License, version 3
 */ 

#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>

#include "jpeg-read.h"


_Bool jpeg_log_enabled;

void proxy_document_construct(ProxyDocument *this, ImageDocument *prevdoc)
{
	this->rawdoc = NULL;
	this->prevdoc = prevdoc;
}

void proxy_document_destruct(ProxyDocument *this)
{
	if (this->rawdoc) {
		raw_jpeg_document_destruct((RawJpegDocument *) (this->rawdoc));
		free(this->rawdoc);
	}

	if (this->prevdoc) {
		image_document_destruct(this->prevdoc);
		free(this->prevdoc);
	}
}

ImageDocument * image_document_from_raw_jpeg_document(RawJpegDocument *jpegdoc, const char *path)
{
	int outw = jpegdoc->width;

	int outh = jpegdoc->height;
	double scl = 1.0;

	while (outw > 2000 || outw > 2000) {
		scl = scl / 2;
		outw = outw / 2;
		outh = outh / 2;
		logjpeg(path, "proxy scale set to: %.2f)", outw, outh, scl);
	}

	logjpeg(path, "proxy dimensions: %d x %d (scale: %.2f)", outw, outh, scl);

	if (outw <= 0 || outh <= 0) {
		outw = jpegdoc->width;
		outh = jpegdoc->height;
		scl = 1.0;
	}

	return image_document_from_raw_jpeg_document_low(jpegdoc, outw, outh, scl, 0, 0);
}

ImageDocument * image_document_from_raw_jpeg_document_low(RawJpegDocument *jpegdoc, int outw, int outh, double scl, int offxin, int offyin)
{
	ImageDocument *doc = (ImageDocument *) malloc(sizeof(ImageDocument));
	if (doc == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	image_document_construct(doc, outw, outh, drawing_params_default(), true);

	Image *img = image_document_get_bglayer_must(doc)->img;

	for (int y = 0; y < doc->height; y += 1) {
		int srcy = offyin + (int) (y / scl);

		assert(srcy < jpegdoc->height);
		for (int x = 0; x < doc->width; x += 1) {
			int srcx = offxin + (int) (x / scl);

			assert(srcx < jpegdoc->width);
			Pixel srgbpxl = pixel_default();
			for (int i = 0; i < 3; i += 1) {
				unsigned char cval8 = jpegdoc->lines[srcy][srcx * 3 + i];
				srgbpxl.rgb[i] = cval8 / 255.0;
			}

			srgbpxl.rgb[3] = 1;

			nan_img_set_linear_pixel(img, x, y, srgbpixel2linear(srgbpxl));
		}
	}

	ImageDocument *_tmp_1;
	_tmp_1 = doc;
	doc = NULL;
	return _tmp_1;
}

ProxyDocument * read_proxy_document_from_jpeg_fp(FILE *fp, const char *path)
{
	RawJpegDocument *rawdocnul = read_raw_jpeg_document_from_fp(fp, path);

	if (rawdocnul) {
		ImageDocument *doc = image_document_from_raw_jpeg_document(rawdocnul, path);
		ProxyDocument *proxydoc = (ProxyDocument *) malloc(sizeof(ProxyDocument));
		if (proxydoc == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}

		proxy_document_construct(proxydoc, doc);
		RawJpegDocument *_ngg_tmp_3 = rawdocnul;
		if (proxydoc->rawdoc) {
			raw_jpeg_document_destruct((RawJpegDocument *) (proxydoc->rawdoc));
			free(proxydoc->rawdoc);
		}

		proxydoc->rawdoc = _ngg_tmp_3;
		return proxydoc;
	}

	return NULL;
}

RawJpegDocument * read_raw_jpeg_document_from_fp(FILE *fp, const char *path)
{
	fprintf(stderr, "TODO FIXME IMPORTANT define a custom error handler or libjpeg will call exit()\n");

	struct jpeg_decompress_struct jdecomp;
	struct jpeg_error_mgr jerr;

	jdecomp.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&jdecomp);

	jpeg_stdio_src(&jdecomp, fp);
	jpeg_read_header(&jdecomp, true);

	if (3 != jdecomp.num_components) {
		perrorjpeg(path, NULL, "num_components != 3");
		return NULL;
	}

	jpeg_start_decompress(&jdecomp);

	RawJpegDocument *jpegdoc = (RawJpegDocument *) malloc(sizeof(RawJpegDocument));
	if (jpegdoc == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	raw_jpeg_document_construct(jpegdoc, (int) (jdecomp.output_width), (int) (jdecomp.output_height));

	while (jdecomp.output_scanline < jdecomp.output_height) {
		jpeg_read_scanlines(&jdecomp, jpegdoc->lines + (int) (jdecomp.output_scanline), 1);
	}

	jpeg_finish_decompress(&jdecomp);
	jpeg_destroy_decompress(&jdecomp);

	RawJpegDocument *_tmp_1;
	_tmp_1 = jpegdoc;
	jpegdoc = NULL;
	return _tmp_1;
}

ProxyDocument * read_proxy_document_from_jpeg(const char *path)
{
	FILE *fpnul = fopen(path, "rb");
	if (fpnul) {
		FILE *fp;
		fp = (FILE *) fpnul;
		ProxyDocument *retval = read_proxy_document_from_jpeg_fp(fp, path);
		fclose(fp);
		return retval;
	}

	perrorjpeg(path, NULL, "fopen() failed; message: %s", strerror(errno));

	return NULL;
}

void logjpeg(const char *path, const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	if (jpeg_log_enabled) {
		size_t _tmp_1 = strlen(path);
		size_t _tmp_2 = strlen(fmt);
		size_t _tmp_catpos = 0u;
		char *_tmp_3 = (char *) calloc((size_t) (1u + _tmp_2 + 2u + _tmp_1 + 14u + 1u), sizeof(char));
		if (_tmp_3 == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}

		char *_tmp_catstr = (char *) _tmp_3;
		memcpy(_tmp_catstr + _tmp_catpos, "jpeg reading: ", 14u);
		_tmp_catpos += 14u;
		memcpy(_tmp_catstr + _tmp_catpos, path, _tmp_1);
		_tmp_catpos += _tmp_1;
		memcpy(_tmp_catstr + _tmp_catpos, ": ", 2u);
		_tmp_catpos += 2u;
		memcpy(_tmp_catstr + _tmp_catpos, fmt, _tmp_2);
		_tmp_catpos += _tmp_2;
		_tmp_catstr[_tmp_catpos++] = '\n';
		_tmp_catstr[_tmp_catpos] = '\0';
		char *_ngg_tmp_0 = _tmp_catstr;
		vfprintf(stderr, _ngg_tmp_0, ap);
		if (_ngg_tmp_0) {
			free(_ngg_tmp_0);
		}
	}
}

void perrorjpeg(const char *path, FILE *fp, const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	if (fp) {
		FILE *fpnn;
		fpnn = (FILE *) fp;
		fprintf(stderr, "at offset %ld: ", ftell(fpnn));
	}

	size_t _tmp_1 = strlen(fmt);
	size_t _tmp_catpos = 0u;
	char *_tmp_2 = (char *) calloc((size_t) (1u + _tmp_1 + 20u + 1u), sizeof(char));
	if (_tmp_2 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	char *_tmp_catstr = (char *) _tmp_2;
	memcpy(_tmp_catstr + _tmp_catpos, "error reading jpeg: ", 20u);
	_tmp_catpos += 20u;
	memcpy(_tmp_catstr + _tmp_catpos, fmt, _tmp_1);
	_tmp_catpos += _tmp_1;
	_tmp_catstr[_tmp_catpos++] = '\n';
	_tmp_catstr[_tmp_catpos] = '\0';
	char *_ngg_tmp_1 = _tmp_catstr;
	vfprintf(stderr, _ngg_tmp_1, ap);
	if (_ngg_tmp_1) {
		free(_ngg_tmp_1);
	}
}
