/* Define built-in VIPS types.
 *
 * J. Cupitt, 8/4/93.
 *
 * Modified:
 * 21/5/07
 *	- any length vector (Tom)
 * 23/8/10
 * 	- add IM_TYPE_RW flag for im__rw_image
 */

/*

	This file is part of VIPS.

	VIPS 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 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 Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
	02110-1301  USA

 */

/*

	These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk

 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <glib/gi18n-lib.h>

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

#include <vips/vips.h>
#include <vips/vips7compat.h>
#include <vips/internal.h>

/* String containing each of the characters which can be used within a
 * single command line argument to separate the elements of a vector.
 */
#define VEC_SEPS " "

/* Init function for input displays. Just a stub nowadays.
 */
static int
input_display_init(im_object *obj, char *str)
{
	*obj = NULL;

	return 0;
}

/* Input display type.
 */
im_type_desc im__input_display = {
	IM_TYPE_DISPLAY,	/* It's a display */
	0,					/* No storage needed */
	IM_TYPE_ARG,		/* It requires a command-line arg */
	input_display_init, /* Init function */
	NULL				/* Destroy function */
};

/* Output display type.
 */
im_type_desc im__output_display = {
	IM_TYPE_DISPLAY, /* It's a display */
	sizeof(void *),	 /* Memory to allocate */
	IM_TYPE_OUTPUT,	 /* Output object */
	NULL,			 /* Init function */
	NULL			 /* Destroy function */
};

/* Init function for input images.
 */
static int
input_image_init(im_object *obj, char *str)
{
	IMAGE **im = (IMAGE **) obj;

	return !(*im = vips__deprecated_open_read(str, FALSE));
}

/* Input image type.
 */
im_type_desc im__input_image = {
	IM_TYPE_IMAGE,					   /* It's an image */
	0,								   /* No storage needed */
	IM_TYPE_ARG,					   /* It requires a command-line arg */
	(im_init_obj_fn) input_image_init, /* Init function */
	(im_dest_obj_fn) im_close		   /* Destroy function */
};

/* Init function for output images.
 */
static int
output_image_init(im_object *obj, char *str)
{
	IMAGE **im = (IMAGE **) obj;

	return !(*im = vips__deprecated_open_write(str));
}

/* Output image type.
 */
im_type_desc im__output_image = {
	IM_TYPE_IMAGE,						/* It's an image */
	0,									/* No storage to be allocated */
	IM_TYPE_OUTPUT | IM_TYPE_ARG,		/* Flags! */
	(im_init_obj_fn) output_image_init, /* Init function */
	(im_dest_obj_fn) im_close			/* Destroy function */
};

/* Init function for RW images.
 */
static int
rw_image_init(im_object *obj, char *str)
{
	IMAGE **im = (IMAGE **) obj;

	return !(*im = im_open(str, "rw"));
}

/* RW image type.
 */
im_type_desc im__rw_image = {
	IM_TYPE_IMAGE,					/* It's an image */
	0,								/* No storage to be allocated */
	IM_TYPE_ARG | IM_TYPE_RW,		/* Read-write object, needs an arg */
	(im_init_obj_fn) rw_image_init, /* Init function */
	(im_dest_obj_fn) im_close		/* Destroy function */
};

/* im_imagevec_object destroy function.
 */
static int
imagevec_dest(im_object obj)
{
	im_imagevec_object *iv = obj;

	if (iv->vec) {
		int i;

		for (i = 0; i < iv->n; i++)
			if (iv->vec[i]) {
				im_close(iv->vec[i]);
				iv->vec[i] = NULL;
			}

		g_free(iv->vec);
		iv->vec = NULL;
		iv->n = 0;
	}

	return 0;
}

/* Init function for imagevec input.
 */
static int
input_imagevec_init(im_object *obj, char *str)
{
	im_imagevec_object *iv = *obj;
	char **strv;
	int nargs;
	int i;

	strv = g_strsplit(str, VEC_SEPS, -1);
	nargs = g_strv_length(strv);

	if (!(iv->vec = VIPS_ARRAY(NULL, nargs, IMAGE *))) {
		g_strfreev(strv);
		return -1;
	}
	iv->n = nargs;

	/* Must NULL them out in case we fail halfway though opening them all.
	 */
	for (i = 0; i < nargs; i++)
		iv->vec[i] = NULL;

	for (i = 0; i < nargs; i++)
		if (!(iv->vec[i] = im_open(strv[i], "rd"))) {
			g_strfreev(strv);
			return -1;
		}

	g_strfreev(strv);

	return 0;
}

/* Input image vector type.
 */
im_type_desc im__input_imagevec = {
	IM_TYPE_IMAGEVEC,			/* It's an array of IMAGE */
	sizeof(im_imagevec_object), /* Memory to allocate in vec build */
	IM_TYPE_ARG,				/* It requires a command-line arg */
	input_imagevec_init,		/* Init function */
	imagevec_dest				/* Destroy function */
};

/* Init function for masks. "str" can be NULL for output masks.
 */
static int
mask_init(im_object *obj, char *str)
{
	im_mask_object *mo = *obj;

	/* Install string, clear mask.
	 */
	if (str && !(mo->name = im_strdup(NULL, str)))
		return -1;
	mo->mask = NULL;

	return 0;
}

/* Init function for input dmasks. As above, but read in the mask.
 */
static int
dmask_init(im_object *obj, char *str)
{
	im_mask_object *mo = *obj;

	if (mask_init(obj, str))
		return -1;
	if (!(mo->mask = im_read_dmask(str)))
		return -1;

	return 0;
}

/* Init function for input imasks.
 */
static int
imask_init(im_object *obj, char *str)
{
	im_mask_object *mo = *obj;

	if (mask_init(obj, str))
		return -1;
	if (!(mo->mask = im_read_imask(str)))
		return -1;

	return 0;
}

/* DOUBLEMASK destroy function.
 */
static int
dmask_dest(im_object obj)
{
	im_mask_object *mo = obj;

	VIPS_FREE(mo->name);
	VIPS_FREEF(im_free_dmask, mo->mask);

	return 0;
}

/* INTMASK destroy function.
 */
static int
imask_dest(im_object obj)
{
	im_mask_object *mo = obj;

	VIPS_FREE(mo->name);
	VIPS_FREEF(im_free_imask, mo->mask);

	return 0;
}

/* As above, but save the mask first.
 */
static int
save_dmask_dest(im_object obj)
{
	im_mask_object *mo = obj;

	if (mo->mask && im_write_dmask(mo->mask))
		return -1;
	return dmask_dest(obj);
}

/* As above, but save the mask first.
 */
static int
save_imask_dest(im_object obj)
{
	im_mask_object *mo = obj;

	if (mo->mask && im_write_imask(mo->mask))
		return -1;
	return imask_dest(obj);
}

/* Output dmask type.
 */
im_type_desc im__output_dmask = {
	IM_TYPE_DMASK,				  /* It's a mask */
	sizeof(im_mask_object),		  /* Storage for mask object */
	IM_TYPE_OUTPUT | IM_TYPE_ARG, /* Flags */
	mask_init,					  /* Init function */
	save_dmask_dest				  /* Save and destroy function */
};

/* Input dmask type.
 */
im_type_desc im__input_dmask = {
	IM_TYPE_DMASK,			/* It's a mask */
	sizeof(im_mask_object), /* Storage for mask object */
	IM_TYPE_ARG,			/* It requires a command-line arg */
	dmask_init,				/* Init function */
	dmask_dest				/* Destroy function */
};

/* Output imask type.
 */
im_type_desc im__output_imask = {
	IM_TYPE_IMASK,				  /* It's a mask */
	sizeof(im_mask_object),		  /* Storage for mask object */
	IM_TYPE_OUTPUT | IM_TYPE_ARG, /* Flags */
	mask_init,					  /* Init function */
	save_imask_dest				  /* Save and destroy function */
};

/* Input imask type.
 */
im_type_desc im__input_imask = {
	IM_TYPE_IMASK,			/* It's a mask */
	sizeof(im_mask_object), /* Storage for mask object */
	IM_TYPE_ARG,			/* It requires a command-line arg */
	imask_init,				/* Init function */
	imask_dest				/* Destroy function */
};

/* Output dmask to screen type. Set a `print' function to get actual output.
 * Used for things like "stats".
 */
im_type_desc im__output_dmask_screen = {
	IM_TYPE_DMASK,			/* It's a mask */
	sizeof(im_mask_object), /* Storage for mask object */
	IM_TYPE_OUTPUT,			/* It's an output argument */
	mask_init,				/* Init function */
	dmask_dest				/* Destroy function */
};

/* Init function for double input.
 */
static int
input_double_init(im_object *obj, char *str)
{
	double *d = (double *) *obj;

	*d = g_ascii_strtod(str, NULL);

	return 0;
}

/* Input double type.
 */
im_type_desc im__input_double = {
	IM_TYPE_DOUBLE,	   /* It's a double */
	sizeof(double),	   /* Memory to allocate */
	IM_TYPE_ARG,	   /* It requires a command-line arg */
	input_double_init, /* Init function */
	NULL			   /* Destroy function */
};

/* im_doublevec_object destroy function.
 */
static int
doublevec_dest(im_object obj)
{
	im_doublevec_object *dv = obj;

	if (dv->vec) {
		g_free(dv->vec);
		dv->vec = NULL;
		dv->n = 0;
	}

	return 0;
}

/* Init function for doublevec input.
 */
static int
input_doublevec_init(im_object *obj, char *str)
{
	im_doublevec_object *dv = *obj;
	char **strv;
	int nargs;
	int i;

	strv = g_strsplit(str, VEC_SEPS, -1);
	nargs = g_strv_length(strv);

	if (!(dv->vec = VIPS_ARRAY(NULL, nargs, double))) {
		g_strfreev(strv);
		return -1;
	}
	dv->n = nargs;

	for (i = 0; i < nargs; i++) {
		dv->vec[i] = g_ascii_strtod(strv[i], NULL);
		if (errno) {
			vips_error_system(errno, "input_doublevec_init",
				_("bad double \"%s\""), strv[i]);
			g_strfreev(strv);
			return -1;
		}
	}

	g_strfreev(strv);

	return 0;
}

/* Input double vector type.
 */
im_type_desc im__input_doublevec = {
	IM_TYPE_DOUBLEVEC,			 /* It's an array of double */
	sizeof(im_doublevec_object), /* Memory to allocate in vec build */
	IM_TYPE_ARG,				 /* It requires a command-line arg */
	input_doublevec_init,		 /* Init function */
	doublevec_dest				 /* Destroy function */
};

/* Print function for doublevec output.
 */
int
im__dvprint(im_object obj)
{
	im_doublevec_object *dv = obj;
	int i;

	for (i = 0; i < dv->n; i++)
		printf("%G ", dv->vec[i]);
	printf("\n");

	return 0;
}

/* Output double vector type.
 */
im_type_desc im__output_doublevec = {
	IM_TYPE_DOUBLEVEC,			 /* It's an array of double */
	sizeof(im_doublevec_object), /* Memory to allocate in vec build */
	IM_TYPE_OUTPUT,				 /* Output type */
	NULL,						 /* Init function */
	doublevec_dest				 /* Destroy function */
};

/* im_intvec_object destroy function.
 */
static int
intvec_dest(im_object obj)
{
	im_intvec_object *iv = obj;

	if (iv->vec) {
		g_free(iv->vec);
		iv->vec = NULL;
		iv->n = 0;
	}

	return 0;
}

/* Init function for intvec input.
 */
static int
input_intvec_init(im_object *obj, char *str)
{
	im_intvec_object *iv = *obj;
	char **strv;
	int nargs;
	int i;

	strv = g_strsplit(str, VEC_SEPS, -1);
	nargs = g_strv_length(strv);

	if (!(iv->vec = VIPS_ARRAY(NULL, nargs, int))) {
		g_strfreev(strv);
		return -1;
	}
	iv->n = nargs;

	for (i = 0; i < nargs; i++) {
		long int val = strtol(strv[i], NULL, 10);

		if (errno) {
			vips_error_system(errno, "input_intvec_init",
				_("bad integer \"%s\""), strv[i]);
			g_strfreev(strv);
			return -1;
		}
		if (INT_MAX < val || INT_MIN > val) {
			vips_error("input_intvec_init",
				"%ld overflows integer type", val);
		}
		iv->vec[i] = (int) val;
	}

	g_strfreev(strv);

	return 0;
}

/* Input int vector type.
 */
im_type_desc im__input_intvec = {
	IM_TYPE_INTVEC,			  /* It's an array of int */
	sizeof(im_intvec_object), /* Memory to allocate in vec build */
	IM_TYPE_ARG,			  /* It requires a command-line arg */
	input_intvec_init,		  /* Init function */
	intvec_dest				  /* Destroy function */
};

/* Print function for intvec output.
 */
int
im__ivprint(im_object obj)
{
	im_intvec_object *iv = obj;
	int i;

	for (i = 0; i < iv->n; i++)
		printf("%d ", iv->vec[i]);
	printf("\n");

	return 0;
}

/* Output int vector type.
 */
im_type_desc im__output_intvec = {
	IM_TYPE_INTVEC,				 /* It's an array of int */
	sizeof(im_intvec_object),	 /* Memory to allocate in vec build */
	IM_TYPE_OUTPUT,				 /* Output arg */
	(im_init_obj_fn) NULL,		 /* Init function */
	(im_dest_obj_fn) intvec_dest /* Destroy function */
};

/* Init function for int input.
 */
static int
input_int_init(im_object *obj, char *str)
{
	int *i = (int *) *obj;

	if (sscanf(str, "%d", i) != 1) {
		vips_error("input_int", "%s", _("bad format"));
		return -1;
	}

	return 0;
}

/* Input int type.
 */
im_type_desc im__input_int = {
	IM_TYPE_INT,	/* It's an int */
	sizeof(int),	/* Memory to allocate */
	IM_TYPE_ARG,	/* It requires a command-line arg */
	input_int_init, /* Init function */
	NULL			/* Destroy function */
};

/* Init function for string input.
 */
static int
input_string_init(im_object *obj, char *str)
{
	if (!(*obj = (im_object) im_strdup(NULL, str)))
		return -1;

	return 0;
}

/* Input string type.
 */
im_type_desc im__input_string = {
	IM_TYPE_STRING,	   /* It's a string */
	0,				   /* Memory to allocate */
	IM_TYPE_ARG,	   /* It requires a command-line arg */
	input_string_init, /* Init function */
	vips_free		   /* Destroy function */
};

/* Output string type.
 */
im_type_desc im__output_string = {
	IM_TYPE_STRING, /* It's a string */
	0,				/* Memory to allocate */
	IM_TYPE_OUTPUT, /* It's an output argument */
	NULL,			/* Init function */
	vips_free		/* Destroy function */
};

/* Output double type.
 */
im_type_desc im__output_double = {
	IM_TYPE_DOUBLE, /* It's a double */
	sizeof(double), /* Memory to allocate */
	IM_TYPE_OUTPUT, /* It's an output argument */
	NULL,			/* Init function */
	NULL			/* Destroy function */
};

/* Output complex type.
 */
im_type_desc im__output_complex = {
	IM_TYPE_COMPLEX,	/* It's a complex */
	2 * sizeof(double), /* Memory to allocate */
	IM_TYPE_OUTPUT,		/* It's an output argument */
	NULL,				/* Init function */
	NULL				/* Destroy function */
};

/* Output int type.
 */
im_type_desc im__output_int = {
	IM_TYPE_INT,	/* It's an int */
	sizeof(int),	/* Memory to allocate */
	IM_TYPE_OUTPUT, /* It's an output argument */
	NULL,			/* Init function */
	NULL			/* Destroy function */
};

/* Print function for int output.
 */
int
im__iprint(im_object obj)
{
	int *i = (int *) obj;

	printf("%d\n", *i);

	return 0;
}

/* Print function for string output.
 */
int
im__sprint(im_object obj)
{
	char *s = (char *) obj;

	printf("%s\n", s);

	return 0;
}

/* Print function for double output.
 */
int
im__dprint(im_object obj)
{
	double *d = (double *) obj;

	printf("%G\n", *d);

	return 0;
}

/* Print function for complex output.
 */
int
im__cprint(im_object obj)
{
	double *d = (double *) obj;

	printf("%G %G\n", d[0], d[1]);

	return 0;
}

/* Statistics to stdout.
 */
int
im__dmsprint(im_object obj)
{
	DOUBLEMASK *mask = ((im_mask_object *) obj)->mask;
	double *row;
	int i, j;

	/* Print statistics band stats eg: 2 bands:b 0,1
	 */
	printf("band    minimum     maximum         sum       "
		   "sum^2        mean   deviation\n");
	for (j = 0; j < mask->ysize; j++) {
		row = mask->coeff + j * mask->xsize;
		if (j == 0)
			printf("all");
		else
			printf("%2d ", j);

		for (i = 0; i < 6; i++)
			printf("%12g", row[i]);
		printf("\n");
	}

	return 0;
}

/* GValue
 */

/* Init function for input gvalue. Just make a string ... will get cast to
 * whatever later.
 */
static int
input_gvalue_init(im_object *obj, char *str)
{
	GValue *value = *obj;

	g_value_init(value, G_TYPE_STRING);
	g_value_set_string(value, str);

	return 0;
}

static int
gvalue_free(im_object obj)
{
	GValue *value = obj;

	/* If it's just zeros (built but not used) we'll get an error if we
	 * unset().
	 */
	if (G_IS_VALUE(value))
		g_value_unset(value);

	return 0;
}

/* Input GValue type.
 */
im_type_desc im__input_gvalue = {
	IM_TYPE_GVALUE,
	sizeof(GValue),						/* Need some storage */
	IM_TYPE_ARG,						/* It requires a command-line arg */
	(im_init_obj_fn) input_gvalue_init, /* Init function */
	(im_dest_obj_fn) gvalue_free		/* Destroy function */
};

int
im__gprint(im_object obj)
{
	GValue *value = obj;
	char *str_value;

	str_value = g_strdup_value_contents(value);
	printf("%s\n", str_value);
	g_free(str_value);

	return 0;
}

/* Init function for output gvalue. Just init to zero.
 */
static int
output_gvalue_init(im_object *obj)
{
	GValue *value = *obj;

	memset(value, 0, sizeof(GValue));

	return 0;
}

im_type_desc im__output_gvalue = {
	IM_TYPE_GVALUE,
	sizeof(GValue),						 /* Need some storage */
	IM_TYPE_OUTPUT,						 /* No arg needed (just print) */
	(im_init_obj_fn) output_gvalue_init, /* Init function */
	(im_dest_obj_fn) gvalue_free		 /* Destroy function */
};

/* Init function for input interpolate.
 *
 * This is used as a helper function by the C++ interface, so amke it public.
 */
int
vips__input_interpolate_init(im_object *obj, char *str)
{
	GType type = g_type_from_name("VipsInterpolate");
	VipsObjectClass *class = VIPS_OBJECT_CLASS(g_type_class_ref(type));
	VipsObject *object;

	g_assert(class);

	if (!(object = vips_object_new_from_string(class, str)))
		return -1;
	if (vips_object_build(object)) {
		g_object_unref(object);
		return -1;
	}
	*obj = object;

	return 0;
}

static int
input_interpolate_dest(im_object obj)
{
	g_object_unref((GObject *) obj);

	return 0;
}

im_type_desc im__input_interpolate = {
	IM_TYPE_INTERPOLATE,
	0,							  /* No storage required */
	IM_TYPE_ARG,				  /* It requires a command-line arg */
	vips__input_interpolate_init, /* Init function */
	input_interpolate_dest		  /* Destroy function */
};
