/****************************************************************/
/* jamcam.c - Gphoto2 library for the KBGear JamCam v2 and v3   */
/*                                                              */
/* Copyright 2001 Chris Pinkham                                 */
/*                                                              */
/* Author: Chris Pinkham <cpinkham@infi.net>                    */
/*                                                              */
/* This library is free software; you can redistribute it       */
/* and/or modify it under the terms of the GNU Library General  */
/* Public License as published by the Free Software Foundation; */
/* either version 2 of the License, or (at your option) any     */
/* later version.                                               */
/*                                                              */
/* This library 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 Library General Public License for     */
/* more details.                                                */
/*                                                              */
/* You should have received a copy of the GNU Library General   */
/* Public License along with this library; if not, write to the */
/* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,*/
/* Boston, MA  02110-1301  USA					*/
/****************************************************************/

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <gphoto2/gphoto2.h>
#include <libgphoto2/bayer.h>
#include <libgphoto2/gamma.h>

#include "libgphoto2/i18n.h"

#include "library.h"


#define GP_MODULE "jamcam"
#define TIMEOUT	      2000

#define JAMCAM_VERSION "0.6"
#define JAMCAM_LAST_MOD "11/28/2001 14:51 EST"

/* define what cameras we support */
static const struct {
	char *model;
	int usb_vendor;
	int usb_product;
} models[] = {
	{ "KBGear:JamCam", 0x084E, 0x0001 },
	{ NULL, 0, 0 }
};


int camera_id (CameraText *id)
{
	/* GP_DEBUG ("* camera_id"); */

	strcpy (id->text, "jamcam");

	return (GP_OK);
}

int camera_abilities (CameraAbilitiesList *list)
{
	int x = 0;
	char *ptr;
	CameraAbilities a;

	/* GP_DEBUG ("* camera_abilities"); */

	ptr = models[x].model;
	while (ptr) {
		memset(&a, 0, sizeof(a));
		strcpy (a.model, ptr );
		a.status = GP_DRIVER_STATUS_PRODUCTION;
		a.port     = GP_PORT_SERIAL | GP_PORT_USB;
		a.speed[0] = 57600;
		a.speed[1] = 0;

		/* fixme, need to set these operations lists to correct values */
		a.operations        = 	GP_OPERATION_NONE;
		a.file_operations   = 	GP_FILE_OPERATION_PREVIEW;

		a.folder_operations = 	GP_FOLDER_OPERATION_NONE;

		a.usb_vendor = models[x].usb_vendor;
		a.usb_product = models[x].usb_product;

		gp_abilities_list_append (list, a);

		ptr = models[++x].model;
	}

	return (GP_OK);
}

static int file_list_func (CameraFilesystem *fs, const char *folder,
			   CameraList *list, void *data, GPContext *context)
{
	Camera *camera = data;
	int count;

	GP_DEBUG ("* file_list_func");
	GP_DEBUG ("*** folder: %s", folder);

	CHECK (count = jamcam_file_count (camera));
	CHECK (gp_list_populate (list, "pic_%04i.ppm", count));

	return (GP_OK);
}

static int get_info_func (CameraFilesystem *fs, const char *folder,
		const char *filename, CameraFileInfo *info, void *data,
		GPContext *context)
{
	Camera *camera = data;
	int n;
	struct jamcam_file *jc_file;

	GP_DEBUG ("* get_info_func");
	GP_DEBUG ("*** folder: %s", folder);
	GP_DEBUG ("*** filename: %s",filename);

	/* Get the file number from the CameraFileSystem */
	CHECK (n = gp_filesystem_number (camera->fs, folder,
					 filename, context));

	jc_file = jamcam_file_info( camera, n );

	/* fixme, get file size also */
	info->file.fields = GP_FILE_INFO_TYPE;
	strcpy (info->file.type, GP_MIME_PPM);
	info->file.width = jc_file->width;
	info->file.height = jc_file->height;

	info->preview.fields = GP_FILE_INFO_TYPE;
	strcpy (info->preview.type, GP_MIME_PPM);
	info->preview.width = 80;
	info->preview.height = 60;

	return (GP_OK);
}

static int camera_exit (Camera *camera, GPContext *context)
{
	GP_DEBUG ("* camera_exit");

	jamcam_file_count (camera);
	return (GP_OK);
}

#define CHECK_free(result) {		\
	int res;			\
	res = result; 			\
	if (res < 0)  { 		\
		free(raw); free(ppm);	\
		return (res);		\
	}				\
}

static int get_file_func (CameraFilesystem *fs, const char *folder,
			  const char *filename, CameraFileType type,
			  CameraFile *file, void *user_data, GPContext *context)
{
	Camera *camera = user_data;
	char *raw, *ppm;
	unsigned char gtable[256];
	char *ptr;
	unsigned int size = 0;
	int n = 0;
	int width, height;
	struct jamcam_file *jc_file;

	GP_DEBUG ("* camera_file_get");
	GP_DEBUG ("*** folder: %s", folder);
	GP_DEBUG ("*** filename: %s",filename);
	GP_DEBUG ("*** type: %d", type);

	CHECK (n = gp_filesystem_number (camera->fs, folder,
					 filename, context));

	if (gp_context_cancel (context) == GP_CONTEXT_FEEDBACK_CANCEL)
		return (GP_ERROR_CANCEL);

	raw = malloc(640*480 * 3);
	ppm = malloc(640*480 * 3 + 200);

	switch (type) {
	case GP_FILE_TYPE_PREVIEW:

		CHECK_free (jamcam_request_thumbnail (camera, file, raw, &size, n, context));


		width = 80;
		height = 60;

		sprintf( ppm,
			"P6\n"
			"# CREATOR: gphoto2, jamcam library\n"
			"%d %d\n"
			"255\n", width, height );

		ptr = ppm + strlen( ppm );

		size = strlen( ppm ) + ( height * width * 3 );

		gp_bayer_decode((unsigned char *)raw, width, height,
				(unsigned char *)ptr, BAYER_TILE_GBRG );
		gp_gamma_fill_table( gtable, 0.5 );
		gp_gamma_correct_single( gtable, (unsigned char *)ptr, height * width );

		CHECK_free (gp_file_set_mime_type (file, GP_MIME_PPM));
		CHECK_free (gp_file_append (file, ppm, size));
		break;

	case GP_FILE_TYPE_NORMAL:
		CHECK_free (jamcam_request_image (camera, file, raw, &size, n, context));

		jc_file = jamcam_file_info (camera, n);

		sprintf( ppm,
			"P6\n"
			"# CREATOR: gphoto2, jamcam library\n"
			"%d %d\n"
			"255\n", jc_file->width, jc_file->height );

		if ((jc_file->width > 640) || (jc_file->height > 480)) {
			free (raw);
			free (ppm);
			return GP_ERROR;
		}

		ptr = ppm + strlen( ppm );

		size = strlen( ppm ) + ( jc_file->width * jc_file->height * 3 );

		gp_bayer_decode((unsigned char *)raw, jc_file->width, jc_file->height,
				(unsigned char *)ptr, BAYER_TILE_GBRG );
		gp_gamma_fill_table( gtable, 0.5 );
		gp_gamma_correct_single( gtable, (unsigned char *)ptr, jc_file->width * jc_file->height );

		CHECK_free (gp_file_set_mime_type (file, GP_MIME_PPM));
		CHECK_free (gp_file_append (file, ppm, size));
		break;

	case GP_FILE_TYPE_RAW:
		CHECK_free (jamcam_request_image (camera, file, raw, &size, n, context));
		CHECK_free (gp_file_set_mime_type (file, GP_MIME_RAW));
		CHECK_free (gp_file_append (file, raw, size));
		break;
	default:
		free(raw); free(ppm);
		return (GP_ERROR_NOT_SUPPORTED);
	}
	free(raw); free(ppm);
	return (GP_OK);
}

static int camera_summary (Camera *camera, CameraText *summary, GPContext *context)
{
	int count;
	char tmp[1024];

	GP_DEBUG ("* camera_summary");

	/* possibly get # pics, mem free, etc. */
	count = jamcam_file_count (camera);

	sprintf (tmp, _("Frames Taken     : %4d\n"), count);
	strcat (summary->text, tmp );

	return (GP_OK);
}

static int camera_about (Camera *camera, CameraText *about, GPContext *context)
{
	GP_DEBUG ("* camera_about");

	strcpy (about->text,
		_("jamcam library v" JAMCAM_VERSION
		" " JAMCAM_LAST_MOD "\n"
		"Chris Pinkham <cpinkham@infi.net>\n"
		"Support for KBGear JamCam v2.0 & v3.0 digital cameras\n"
		"based on reverse engineering serial protocol.\n"
		"\n"));

	return (GP_OK);
}

static CameraFilesystemFuncs fsfuncs = {
	.file_list_func = file_list_func,
	.get_file_func = get_file_func,
	.get_info_func = get_info_func,
};

int camera_init (Camera *camera, GPContext *context)
{
	int count;
	GPPortSettings settings;

	GP_DEBUG ("* camera_init");
	GP_DEBUG ("   * jamcam library for Gphoto2 by Chris Pinkham <cpinkham@infi.net>");
	GP_DEBUG ("   * jamcam library v%s, %s", JAMCAM_VERSION, JAMCAM_LAST_MOD );

	/* First, set up all the function pointers */
	camera->functions->exit 	= camera_exit;
	camera->functions->summary	= camera_summary;
	camera->functions->about 	= camera_about;

	CHECK (gp_port_get_settings (camera->port, &settings));
	switch( camera->port->type ) {
	case GP_PORT_SERIAL:
		settings.serial.speed 	 = 57600;
		settings.serial.bits 	 = 8;
		settings.serial.parity 	 = 0;
		settings.serial.stopbits = 1;
		break;
	case GP_PORT_USB:
		/* Use the defaults the core parsed */
		break;
	default:
		fprintf( stderr, "Unknown port type: %d\n", camera->port->type );
		return ( GP_ERROR );
		break;
	}
	CHECK (gp_port_set_settings (camera->port, settings));
	CHECK (gp_port_set_timeout (camera->port, TIMEOUT));

	/* check to see if camera is really there */
	CHECK (jamcam_enq (camera));

	/* get number of images */
	CHECK (count = jamcam_file_count (camera));

	/* Set up the CameraFilesystem */
	return gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
}

