/**************************************************************

konvraw.c (C-Munipack project)
Conversion functions for OES Astro files
Copyright (C) 2003 David Motl, dmotl@volny.cz

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.

**************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <fitsio.h>

#include "config.h"
#include "konvcr3.h"
#include "comfun.h"
#include "console.h"

#include "cr3.h"

#define INVALID_CAMERA_TEMPERATURE -128

typedef struct _konv_cr3_t
{
	FILE* f;
	int unpacked;
	cr3_t raw;
} konv_cr3_t;

/* Returns nonzero if the file is in raw format */
int konv_cr3_test(const char* block, size_t length, size_t filesize)
{
	if (filesize >= 16 && length >= 16) 
		return *(uint32_t*)(block) == 0x18000000UL && memcmp(block + 4, "ftypcrx ", 8) == 0;
	return 0;
}

/* Read parameters from SBIG file */
int konv_cr3_open(tHandle* handle, const char* filename, CmpackOpenMode mode, unsigned flags)
{
	konv_cr3_t* idata;
	FILE* f;

	*handle = NULL;

	f = fopen(filename, "rb");
	if (!f)
		return CMPACK_ERR_OPEN_ERROR;

	idata = (konv_cr3_t*)cmpack_calloc(1, sizeof(konv_cr3_t));
	cr3_init(&idata->raw);
	if (cr3_open(&idata->raw, f) != 0) {
		fclose(f);
		cr3_free(&idata->raw);
		return CMPACK_ERR_UNKNOWN_FORMAT;
	}

	*handle = idata;
	return CMPACK_ERR_OK;
}

/* Close file and free allocated memory */
void konv_cr3_close(tHandle handle)
{
	konv_cr3_t* idata = (konv_cr3_t*)handle;

	if (idata) {
		if (idata->f)
			fclose(idata->f);
		cr3_free(&idata->raw);
		cmpack_free(idata);
	}
}

/* Get magic string */
CmpackBitpix konv_cr3_getbitpix(tHandle handle)
{
	return CMPACK_BITPIX_SLONG;
}

/* Get image data range */
int konv_cr3_getrange(tHandle handle, double* minvalue, double* maxvalue)
{
	if (minvalue)
		*minvalue = 0.0;
	if (maxvalue)
		*maxvalue = 16383.0;
	return 0;
}

/* Get image data */
int konv_cr3_getimage(tHandle handle, void* buf, int bufsize, CmpackChannel channel)
{
	unsigned chmask;
	konv_cr3_t* idata = (konv_cr3_t*)handle;

	switch (channel)
	{
	case CMPACK_CHANNEL_RED:	chmask = 0x01; break;
	case CMPACK_CHANNEL_GREEN:	chmask = 0x06; break;
	case CMPACK_CHANNEL_BLUE:	chmask = 0x08; break;
	case CMPACK_CHANNEL_0:		chmask = 0x01; break;
	case CMPACK_CHANNEL_1:		chmask = 0x02; break;
	case CMPACK_CHANNEL_2:		chmask = 0x04; break;
	case CMPACK_CHANNEL_3:		chmask = 0x08; break;
	case CMPACK_CHANNEL_RGG:	chmask = 0x07; break;
	default:					chmask = 0x0F; break;
	}

	if (!idata->unpacked) {
		if (cr3_unpack(&idata->raw) == CR3_SUCCESS)
			idata->unpacked = 1;
	}
	
	if (idata->unpacked) {
		cr3_t* raw = &idata->raw;
		int nx = (raw->imgdata.width + 1) >> 1, i, pixels, ny = (raw->imgdata.height + 1) >> 1;
		if (nx <= 0 || nx >= 0x4000 || ny <= 0 || ny >= 0x4000) {
			/* Invalid image size */
			return CMPACK_ERR_INVALID_SIZE;
		}
		if (bufsize < (int)(nx * ny * sizeof(int))) {
			/* Insufficient buffer */
			return CMPACK_ERR_BUFFER_TOO_SMALL;
		}
		if ((!raw->imgdata.filters && raw->imgdata.colors != 1) || !raw->imgdata.raw_alloc) {
			/* Invalid format */
			return CMPACK_ERR_UNKNOWN_FORMAT;
		}
		int row, col;
		int raw_row_stride = raw->imgdata.raw_width;
		int maxHeight = raw->imgdata.height;
		if (maxHeight > raw->imgdata.raw_height - raw->imgdata.top_margin)
			maxHeight = raw->imgdata.raw_height - raw->imgdata.top_margin;
		int maxWidth = raw->imgdata.width;
		if (maxWidth > raw->imgdata.raw_width - raw->imgdata.left_margin)
			maxWidth = raw->imgdata.raw_width - raw->imgdata.left_margin;

		memset(buf, 0, nx * ny * sizeof(int));
		for (row = 0; row < maxHeight; row++) {
			for (col = 0; col < maxWidth; col++) {
				int cc = (raw->imgdata.filters >> (((row << 1 & 14) | (col & 1)) << 1) & 3);
				if (chmask & (1 << cc)) {
					unsigned short val = raw->imgdata.raw_alloc[(row + raw->imgdata.top_margin) * raw_row_stride + (col + raw->imgdata.left_margin)];
					((int*)buf)[(row >> 1) * nx + (col >> 1)] += val;
				}
			}
		}
		if (channel == CMPACK_CHANNEL_GREEN) {
			pixels = nx * ny;
			for (i = 0; i < pixels; i++)
				((int*)buf)[i] /= 2;
		}
		if (channel == CMPACK_CHANNEL_SUM) {
			pixels = nx * ny;
			for (i = 0; i < pixels; i++)
				((int*)buf)[i] /= 4;
		}
		return CMPACK_ERR_OK;
	}
	return CMPACK_ERR_READ_ERROR;
}

int konv_cr3_getsize(tHandle handle, int* width, int* height)
{
	konv_cr3_t* idata = (konv_cr3_t*)handle;

	if (!idata->unpacked) {
		if (cr3_unpack(&idata->raw) == CR3_SUCCESS)
			idata->unpacked = 1;
	}
	if (idata->unpacked) {
		cr3_t* raw = &idata->raw;
		int nx = (raw->imgdata.width + 1) >> 1, ny = (raw->imgdata.height + 1) >> 1;
		if (nx <= 0 || nx >= 0x4000 || ny <= 0 || ny >= 0x4000) {
			/* Invalid image size */
			return CMPACK_ERR_INVALID_SIZE;
		}
		if ((!raw->imgdata.filters && raw->imgdata.colors != 1) || !raw->imgdata.raw_alloc) {
			/* Invalid image size */
			return CMPACK_ERR_UNKNOWN_FORMAT;
		}
		if (width)
			*width = nx;
		if (height)
			*height = ny;
		return CMPACK_ERR_OK;
	}
	return CMPACK_ERR_INVALID_SIZE;
}

int konv_cr3_copyheader(tHandle fs, CmpackImageHeader* hdr, CmpackChannel channel, CmpackConsole* con)
{
	int		avg_frames, sum_frames;
	char	datestr[64], timestr[64], * filter;
	CmpackDateTime dt;
	double  ccd_temp;
	konv_cr3_t* src = (konv_cr3_t*)fs;
	fitsfile* dst = (fitsfile*)hdr->fits;

	if (!src->unpacked) {
		if (cr3_unpack(&src->raw) == CR3_SUCCESS)
			src->unpacked = 1;
	}
	if (src->unpacked) {
		/* Set date and time of observation */
		memset(&dt, 0, sizeof(CmpackDateTime));
		if (sscanf(src->raw.imgdata.timestamp, "%4d:%2d:%2d %2d:%2d:%2d", &dt.date.year, &dt.date.month,
			&dt.date.day, &dt.time.hour, &dt.time.minute, &dt.time.second) == 6) {
			sprintf(datestr, "%04d-%02d-%02d", dt.date.year, dt.date.month, dt.date.day);
			ffpkys(dst, "DATE-OBS", (char*)datestr, "UT DATE OF START", &hdr->status);
			sprintf(timestr, "%02d:%02d:%02d", dt.time.hour, dt.time.minute, dt.time.second);
			ffpkys(dst, "TIME-OBS", (char*)timestr, "UT TIME OF START", &hdr->status);
		}

		/* Other parameters */
		if (src->raw.imgdata.shutter[0] > 0 && src->raw.imgdata.shutter[1] > 0)
			ffpkyg(dst, "EXPTIME", (double)src->raw.imgdata.shutter[0] / src->raw.imgdata.shutter[1], 3, "EXPOSURE IN SECONDS", &hdr->status);
		filter = konv_cr3_getfilter(fs, channel);
		if (filter) {
			ffpkys(dst, "FILTER", filter, "COLOR CHANNEL", &hdr->status);
			cmpack_free(filter);
		}
		ccd_temp = INVALID_CAMERA_TEMPERATURE;
		if (konv_cr3_getccdtemp(fs, &ccd_temp) == 0)
			ffukyg(dst, "CCD-TEMP", ccd_temp, 2, "AVERAGE CCD TEMPERATURE", &hdr->status);
		avg_frames = sum_frames = 1;
		konv_cr3_getframes(fs, channel, &avg_frames, &sum_frames);
		if (avg_frames > 1)
			ffpkyj(dst, "FR_AVG", avg_frames, "No. of subframes averaged", &hdr->status);
		if (sum_frames > 1)
			ffpkyj(dst, "FR_SUM", sum_frames, "No. of subframes summed", &hdr->status);

		return (hdr->status != 0 ? CMPACK_ERR_WRITE_ERROR : 0);
	}
	return CMPACK_ERR_READ_ERROR;
}

/* Get magic string */
char* konv_cr3_getmagic(tHandle handle)
{
	konv_cr3_t* idata = (konv_cr3_t*)handle;

	if (!idata->unpacked) {
		if (cr3_unpack(&idata->raw) == CR3_SUCCESS)
			idata->unpacked = 1;
	}
	if (idata->unpacked)
		return cmpack_strdup(idata->raw.imgdata.model);
	return NULL;
}

/* Get date and time of observation */
int konv_cr3_getdatetime(tHandle handle, CmpackDateTime* dt)
{
	konv_cr3_t* idata = (konv_cr3_t*)handle;

	memset(dt, 0, sizeof(CmpackDateTime));

	if (!idata->unpacked) {
		if (cr3_unpack(&idata->raw) == CR3_SUCCESS)
			idata->unpacked = 1;
	}
	if (idata->unpacked) {
		if (sscanf(idata->raw.imgdata.timestamp, "%4d:%2d:%2d %2d:%2d:%2d", &dt->date.year, &dt->date.month,
			&dt->date.day, &dt->time.hour, &dt->time.minute, &dt->time.second) == 6) {
			return CMPACK_ERR_OK;
		}
		return CMPACK_ERR_KEY_NOT_FOUND;
	}
	return CMPACK_ERR_READ_ERROR;
}

/* Get exposure duration */
int konv_cr3_getexptime(tHandle handle, double* val)
{
	konv_cr3_t* idata = (konv_cr3_t*)handle;

	if (!idata->unpacked) {
		if (cr3_unpack(&idata->raw) == CR3_SUCCESS)
			idata->unpacked = 1;
	}
	if (idata->unpacked) {
		if (idata->raw.imgdata.shutter[0] > 0 && idata->raw.imgdata.shutter[1] > 0) {
			*val = (double)idata->raw.imgdata.shutter[0] / idata->raw.imgdata.shutter[1];
			return CMPACK_ERR_OK;
		}
		else {
			*val = 0;
			return CMPACK_ERR_KEY_NOT_FOUND;
		}
	}
	else {
		*val = 0;
		return CMPACK_ERR_READ_ERROR;
	}
}

/* Get sensor temperature */
int konv_cr3_getccdtemp(tHandle handle, double* val)
{
	*val = INVALID_TEMP;
	return CMPACK_ERR_KEY_NOT_FOUND;
}

/* Get filter name */
char* konv_cr3_getfilter(tHandle handle, CmpackChannel channel)
{
	switch (channel)
	{
	case CMPACK_CHANNEL_RED:
		return cmpack_strdup("Red");
	case CMPACK_CHANNEL_GREEN:
		return cmpack_strdup("Green");
	case CMPACK_CHANNEL_BLUE:
		return cmpack_strdup("Blue");
	default:
		return NULL;
	}
}

/* Set number of frames averaged */
void konv_cr3_getframes(tHandle handle, CmpackChannel channel, int* avg_frames, int* sum_frames)
{
	if (channel == CMPACK_CHANNEL_GREEN) {
		if (avg_frames)	*avg_frames = 2;
	}
	if (channel == CMPACK_CHANNEL_DEFAULT) {
		if (avg_frames) *avg_frames = 4;
	}
}
