/* sierra-desc.c:
 *
 * Copyright 2002 Patrick Mansfield <patman@aracnet.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser 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 <_stdint.h>
#include <stdio.h>
#include <string.h>

#include <math.h>

#include <gphoto2/gphoto2-library.h>
#include <gphoto2/gphoto2-port-log.h>

#include "sierra.h"
#include "sierra-desc.h"
#include "library.h"

#ifdef ENABLE_NLS
#  include <libintl.h>
#  undef _
#  define _(String) dgettext (GETTEXT_PACKAGE, String)
#  ifdef gettext_noop
#    define N_(String) gettext_noop (String)
#  else
#    define N_(String) (String)
#  endif
#else
#  define textdomain(String) (String)
#  define gettext(String) (String)
#  define dgettext(Domain,Message) (Message)
#  define dcgettext(Domain,Message,Type) (Message)
#  define bindtextdomain(Domain,Directory) (Domain)
#  define _(String) (String)
#  define N_(String) (String)
#endif

#define GP_MODULE "sierra"

/*
 * For some reason, round() does not get a prototype from math.h.
 */
extern double round(double x);

static int 
camera_cam_desc_get_value (ValueNameType *val_name_p, CameraWidgetType widge, 
			   uint32_t reg_len, void *buff, int mask, 
			   CameraWidget *child)
{
	if ((widge == GP_WIDGET_RADIO) || (widge == GP_WIDGET_MENU)) {
		gp_widget_add_choice (child, _(val_name_p->name));
		GP_DEBUG ("get value %15s:\t%lld (0x%04llx)", 
			  val_name_p->name, (long long)val_name_p->u.value,
			  (long long unsigned) val_name_p->u.value);
		/* XXX where is endian handled? */
		if ((mask & *(int*) buff) == val_name_p->u.value) {
			gp_widget_set_value (child, _(val_name_p->name));
		}
	} else if (widge == GP_WIDGET_DATE) {
		GP_DEBUG ("get value date/time %s", ctime((time_t *) buff));
		gp_widget_set_value (child, (int*) buff);
	} else if (widge == GP_WIDGET_RANGE) {
		float float_val, increment;

		increment = val_name_p->u.range[2];
		if (increment == 0.0) {
			/* 
			 * Use a default value.
			 */
			increment = 1.0;
		}
		GP_DEBUG ("get value range:\t%08g:%08g increment %g (via %g)", 
		       val_name_p->u.range[0], val_name_p->u.range[1],
		       increment, val_name_p->u.range[2]);
		gp_widget_set_range (child, val_name_p->u.range[0], 
				     val_name_p->u.range[1], increment);
		/*
		 * Kluge: store the value in buff as a 4 byte int, even
		 * though we might have read 8 bytes or more.
		 */
		float_val = (*(int*) buff) * increment;
		gp_widget_set_value (child, &float_val);
	} else if (widge == GP_WIDGET_BUTTON) {
		GP_DEBUG ("get button");
		gp_widget_set_value (child, val_name_p->u.callback);
	} else {
		GP_DEBUG ("get value bad widget type %d", widge);
		return (GP_ERROR);
	}

	return (GP_OK);
}

static int
camera_cam_desc_get_widget (Camera *camera, CameraRegisterType *reg_p, 
			    CameraWidget *section, GPContext *context)
{
	int ind, vind, ret, value;
	int mask;
	char buff[1024];
	CameraWidget *child;
	RegisterDescriptorType *reg_desc_p;

	GP_DEBUG ("register %d", reg_p->reg_number);
	if (reg_p->reg_len == 0) {
		/*
		 * This is 0 for GP_WIDGET_BUTTON (callbacks), since is no
		 * register for call backs. Frontends "get" the value, and
		 * call the function directly.
		 */
		ret = GP_OK;
	} else if (reg_p->reg_len == 4) {
		int rval;
		ret = sierra_get_int_register (camera, reg_p->reg_number,
					       &rval,
					       context);
		reg_p->reg_value = rval;
	} else if (reg_p->reg_len == 8) {
		/*
		 * reg_value is 8 bytes maximum. If you need a bigger
		 * value, change the reg_value size, or allocate space on
		 * the fly and make a union with reg_value and a void*.
		 */
		ret = sierra_get_string_register (camera, reg_p->reg_number, 
					  -1, NULL, (unsigned char *)buff, (unsigned int *)&value, context);
		if ((ret == GP_OK) && value != reg_p->reg_len) {
			GP_DEBUG ("Bad length result %d", value);
			return (GP_ERROR);
		}
		memcpy (&reg_p->reg_value, buff, reg_p->reg_len);
	} else {
		GP_DEBUG ("Bad register length %d", reg_p->reg_number);
		return (GP_ERROR);
	}
	GP_DEBUG ("... '%s'.", gp_result_as_string (ret));
        if (ret < 0) {
		return (ret);
	}

	for (ind = 0; ind < reg_p->reg_desc_cnt; ind++) {
		reg_desc_p = &reg_p->reg_desc[ind];
		mask = reg_desc_p->regs_mask;
		GP_DEBUG ("window name is %s", reg_desc_p->regs_long_name);
		gp_widget_new (reg_desc_p->reg_widget_type, 
			       _(reg_desc_p->regs_long_name), &child);
		gp_widget_set_name (child, reg_desc_p->regs_short_name);
		/*
		 * Setting the info for the preference settings does not
		 * make sense like it does for an icon button. This is
		 * used as the tool-tip field (mouse over hint that pops
		 * up after a second in gtkam).  We don't want this used
		 * at all; setting it to space doesn't work well, so just
		 * set it to the same as regs_long_name.
		 */
		gp_widget_set_info (child,  _(reg_desc_p->regs_long_name));
		GP_DEBUG ("reg_value 0x%016llx", (long long unsigned)reg_p->reg_value);
		for (vind = 0; vind < reg_desc_p->reg_val_name_cnt; vind++) {
			camera_cam_desc_get_value (&reg_desc_p->regs_value_names[vind],
				reg_desc_p->reg_widget_type, reg_p->reg_len,
				(char*) &reg_p->reg_value, mask, child);
		}
		/*
		 * For radio and menu values: if there has been no change, it
		 * means the value was not set, and so it is unknown.
		 */
		if (((reg_desc_p->reg_widget_type == GP_WIDGET_RADIO) || 
		     (reg_desc_p->reg_widget_type == GP_WIDGET_MENU)) && 
		      !gp_widget_changed (child)) {
	                gp_widget_set_changed (child, FALSE);
			sprintf (buff, _("%lld (unknown)"), (long long)reg_p->reg_value);
			gp_widget_add_choice (child, buff);
			gp_widget_set_value (child, buff);
		}
		gp_widget_append (section, child);
	}
	return (GP_OK);
}

int
camera_get_config_cam_desc (Camera *camera, CameraWidget **window,
			    GPContext *context)
{
	CameraWidget *section;
	int indw, indr;
	const CameraDescType *cam_desc = NULL;

	GP_DEBUG ("*** camera_get_config_cam_desc");
	CHECK (camera_start (camera, context));
	gp_widget_new (GP_WIDGET_WINDOW, _("Camera Configuration"), window);

	cam_desc = camera->pl->cam_desc;
	for (indw = 0; indw < 2 /* XXX sizeof () */; indw++) {
		GP_DEBUG ("%s registers", cam_desc->regset[indw].window_name);
		gp_widget_new (GP_WIDGET_SECTION,
			       _(cam_desc->regset[indw].window_name), &section);
		gp_widget_append (*window, section);
		for (indr = 0; indr < cam_desc->regset[indw].reg_cnt;  indr++) {
			camera_cam_desc_get_widget (camera,
			      &cam_desc->regset[indw].regs[indr], section,
			      context);
		}
	}
	return (GP_OK);
}

static int
cam_desc_set_register (Camera *camera, CameraRegisterType *reg_p,
		       void *value, GPContext *context)
{
	if (reg_p->reg_get_set.method == CAM_DESC_DEFAULT) {
		if (reg_p->reg_len == 4) {
			/* XXX endian problem or not? */
			CHECK_STOP (camera, sierra_set_int_register
				(camera, reg_p->reg_number,
				*(int*)value, context));
		} else if (reg_p->reg_len <= 8) {
			CHECK_STOP (camera, sierra_set_string_register (camera,
					reg_p->reg_number, (char*) value,
					reg_p->reg_len, context));
		} else {
			/*
			 * Note that sizeof u.value is 8, so if you have a
			 * larger value to store, somehow change u.value
			 * size, or add a union.
			 */
			GP_DEBUG ("set value BAD LENGTH %d", reg_p->reg_len);
			return (GP_ERROR);
		}
	} else if (reg_p->reg_get_set.method == CAM_DESC_SUBACTION) {
		/*
		 * Right now, the only supported sub-action is for
		 * changing the LCD mode, the only other possible
		 * sub-actions are for testing, calibration, and
		 * protection mode. Just make sure the LCD mode setting
		 * works.
		 */
		CHECK_STOP (camera, sierra_sub_action (camera, 
			reg_p->reg_get_set.action, *(int*) value,
			context));
	} else {
		GP_DEBUG ("Unsupported register setting action %d",
			reg_p->reg_get_set.method);
		return (GP_ERROR);
	}
	return (GP_OK);
}

static int 
camera_cam_desc_set_value (Camera *camera, CameraRegisterType *reg_p,
			   RegisterDescriptorType *reg_desc_p,
			   ValueNameType *val_name_p, void *data,
			   GPContext *context)
{
	/*
	 * Return GP_OK if match and OK, 1 if no match, or < 0 on error.
	 */
	if ((reg_desc_p->reg_widget_type == GP_WIDGET_RADIO) || 
	    (reg_desc_p->reg_widget_type == GP_WIDGET_MENU)) {
		uint32_t new_value;

		GP_DEBUG ("set value comparing data '%s' with name '%s'", *(char**)data,
			  val_name_p->name);
		if (strcmp (*(char**)data, val_name_p->name) != 0) {
			return (1);
		}
		/*
		 * For masking, we might actually set the same register
		 * twice - once for each change in the mask, so we have to
		 * reset reg_value to the new value.
		 * 
		 * If we must to mask u.value, that is really a bug - the
		 * u.value should already be masked.
		 *
		 * For registers longer than 4 bytes, we are masking
		 * in whatever values were already in the upper 4 bytes -
		 * none of the current settings (new values) are longer
		 * than 4 bytes.
		 */
		new_value = (reg_p->reg_value & ~reg_desc_p->regs_mask) |
			(val_name_p->u.value & reg_desc_p->regs_mask);
		reg_p->reg_value = new_value;
		GP_DEBUG ("set new val 0x%x; reg val 0x%x; msk 0x%x; val 0x%x ",
			  new_value, (int)reg_p->reg_value,
			  reg_desc_p->regs_mask, (int)val_name_p->u.value);
		CHECK_STOP (camera, cam_desc_set_register (camera, reg_p,
				&new_value, context));
	} else if (reg_desc_p->reg_widget_type == GP_WIDGET_DATE) {
		GP_DEBUG ("set new date/time %s", ctime((time_t*)data));
		CHECK_STOP (camera, cam_desc_set_register (camera, reg_p,
				(int*)data, context));
	} else if (reg_desc_p->reg_widget_type == GP_WIDGET_RANGE) {
		int val[2];
		float increment;

		if (reg_p->reg_get_set.method != CAM_DESC_DEFAULT) {
			GP_DEBUG ("Setting range values using the non-default"
				  " register functions is not supported");
			return (GP_ERROR);
		}
		/*
		 * The value in *data is a float, convert it to an int.
		 *
		 * Currently no need to support any subaction here.
		 *
		 * Similiar to the masking above, if we have a register
		 * larger than 4 bytes, put the four bytes in the int to
		 * into the low four bytes of the value we want to set;
		 * fill the rest with whatever was read from the camera.
		 */
		increment = val_name_p->u.range[2];
		if (increment == 0.0) {
			increment = 1.0; /* default value */
		}
		GP_DEBUG ("set value range from %g inc %g", *(float*)data,
			  increment);
		val[0] = round ((*(float*) data) / increment);
		if (reg_p->reg_len == 4) {
			/* 
			 * not used but set it anyway 
			 */
			val[1] = 0;
		} else if (reg_p->reg_len == 8) {
			memcpy(&val[1], ((char*)&reg_p->reg_value)+sizeof(val[0]), sizeof(val[0]));
			/* val[1] = ((int *) &reg_p->reg_value)[1]; */
		} else if (reg_p->reg_len != 4 ) {
			GP_DEBUG ("Unsupported range with register length %d",
				  reg_p->reg_len);
			return (GP_ERROR);
		}
		GP_DEBUG ("set value range to %d (0x%x and 0x%x)", val[0],
			  val[0], val[1]);
		CHECK_STOP (camera, cam_desc_set_register (camera, reg_p, 
							   &val, context));
	} else {
		GP_DEBUG ("bad reg_widget_type type %d", 
			reg_desc_p->reg_widget_type);
		return (GP_ERROR);
	}
	return (GP_OK);
}

static int
camera_cam_desc_set_widget (Camera *camera, CameraRegisterType *reg_p,
			    CameraWidget *window, GPContext *context)
{
	int ind, vind, ret;
	union value_in {
		char *charp;
		int val;
		float flt;
	} value_in;
	CameraWidget *child;
	RegisterDescriptorType *reg_desc_p;

	GP_DEBUG ("register %d", reg_p->reg_number);

	for (ind = 0; ind < reg_p->reg_desc_cnt; ind++) {
		reg_desc_p = &reg_p->reg_desc[ind];
		GP_DEBUG ("window name is %s", reg_desc_p->regs_long_name);

		if ((gp_widget_get_child_by_label (window,
		     _(reg_desc_p->regs_long_name), &child) >= 0) &&
		     gp_widget_changed (child)) {
	                gp_widget_set_changed (child, FALSE);
			gp_widget_get_value (child, &value_in);
			for (vind = 0; vind < reg_desc_p->reg_val_name_cnt;
			     vind++) {
				ret = camera_cam_desc_set_value (camera, reg_p,
					reg_desc_p,
					&reg_desc_p->regs_value_names[vind],
					&value_in, context);
				if (ret == GP_OK) {
					/*
					 * Something got set, mark the
					 * widget as changed, so the user
					 * can repeat any actions  - like
					 * going to the "next" picture by
					 * repeatedly hitting apply while
					 * in operation play mode.
					 *
					 * This has the side affect of
					 * changing whatever was just set
					 * twice when using the apply in
					 * gtkam - once when applied and
					 * once when OK.
					 */
					gp_widget_set_changed (child, 1);

				}
				if (ret <= 0) {
					/*
					 * Value was set (GP_OK is 0), or
					 * some an error occurred (< 0),
					 * don't bother checking any other
					 * value/name pairs.
					 */
					break;
				}
			}
		}
	}
	return (GP_OK);
}

int 
camera_set_config_cam_desc (Camera *camera, CameraWidget *window,
			    GPContext *context)
{
	int wind, rind;
	const CameraDescType *cam_desc = NULL;

	GP_DEBUG ("*** camera_set_config_cam_desc");
	CHECK (camera_start (camera, context));

	cam_desc = camera->pl->cam_desc;
	for (wind = 0; wind < 2 /* XXX sizeof () */; wind++) {
		GP_DEBUG ("%s registers", cam_desc->regset[wind].window_name);
		for (rind = 0; rind < cam_desc->regset[wind].reg_cnt;  rind++) {
			camera_cam_desc_set_widget (camera,
			      &cam_desc->regset[wind].regs[rind], window,
			      context);
		}
	}
	return (GP_OK);
}
