/*
 * new_template.c.in
 *
 * MathMap
 *
 * Copyright (C) 2002-2009 Mark Probst
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * $$g -> GIMP ? 1 : 0
 * $$m -> mathmap code
 * $$xy_decls         -> declarations for xy-constant variables
 * $$xy_code          -> code for xy-constant variables
 * $$x_decls          -> declarations for x-constant variables
 * $$x_code           -> code for x-constant variables
 * $$y_decls          -> declarations for y-constant variables
 * $$y_code           -> code for y-constant variables
 * $$opmacros_h       -> full name of opmacros.h file
 */

#include <stdlib.h>
#include <math.h>
#include <complex.h>

#if !$g
#define OPENSTEP
#endif

#define IN_COMPILED_CODE

#include "$include/opmacros.h"
#include "$include/pools.h"


typedef struct _mathmap_pools_chunk_t {
    struct _mathmap_pools_chunk_t *next;
    double data[];		/* double for alignment */
} mathmap_pools_chunk_t;

typedef struct {
    int is_global;
    pools_t pools;			 /* only for local pools */
    mathmap_pools_chunk_t *chunks; /* only for global pools */
} mathmap_pools_t;

void mathmap_pools_init_global (mathmap_pools_t *pools);
void mathmap_pools_init_local (mathmap_pools_t *pools);

void mathmap_pools_reset (mathmap_pools_t *pools);

void mathmap_pools_free (mathmap_pools_t *pools);

void* _mathmap_pools_alloc (mathmap_pools_t *pools, size_t size);

static inline void*
mathmap_pools_alloc (mathmap_pools_t *pools, size_t size)
{
    if (pools->is_global)
	return _mathmap_pools_alloc(pools, size);
    return pools_alloc(&pools->pools, size);
}



#ifndef MIN
#define MIN(a,b)         (((a)<(b))?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b)         (((a)<(b))?(b):(a))
#endif

#define M_PI		3.14159265358979323846	/* pi */

#define EDGE_BEHAVIOUR_COLOR          1
#define EDGE_BEHAVIOUR_WRAP           2
#define EDGE_BEHAVIOUR_REFLECT        3
#define EDGE_BEHAVIOUR_ROTATE         4


#define USERVAL_IMAGE       7


#define NUM_FLOATMAP_CHANNELS	4


#define USER_CURVE_POINTS       1024
#define USER_GRADIENT_POINTS    1024


#define MAX_DEBUG_TUPLES              8


typedef struct
{
    int number;
    int length;
    float data[];
} tuple_t;


typedef unsigned int color_t;

#define MAKE_RGBA_COLOR(r,g,b,a)            ((((color_t)(r))<<24)|(((color_t)(g))<<16)|(((color_t)(b))<<8)|((color_t)(a)))
#define RED(c)                              ((c)>>24)
#define GREEN(c)                            (((c)>>16)&0xff)
#define BLUE(c)                             (((c)>>8)&0xff)
#define ALPHA(c)                            ((c)&0xff)

#define IMAGE_DRAWABLE		1
#define IMAGE_CLOSURE		2
#define IMAGE_FLOATMAP		3
#define IMAGE_RESIZE		4


struct _mathmap_invocation_t;
struct _mathmap_frame_t;
struct _mathmap_slice_t;
struct _userval_t;
struct _image_t;

typedef void (*init_frame_func_t) (struct _mathmap_frame_t*, struct _image_t*);
typedef void (*init_slice_func_t) (struct _mathmap_slice_t*, struct _image_t*);
typedef void (*calc_lines_func_t) (struct _mathmap_slice_t*, struct _image_t*, int, int, void*, int);

typedef float* (*filter_func_t) (struct _mathmap_invocation_t*,
				 struct _image_t*,
				 float, float, float,
				 mathmap_pools_t*);

/* FIXME: just for LLVM - remove eventually */
typedef void* (*llvm_init_frame_func_t) (struct _mathmap_invocation_t*,
					 struct _image_t*,
					 float,
					 mathmap_pools_t*);
typedef float* (*llvm_filter_func_t) (struct _mathmap_slice_t*,
				      struct _image_t*,
				      void*, void*,
				      float, float, float,
				      mathmap_pools_t*);
typedef void* (*init_x_or_y_func_t) (struct _mathmap_slice_t*,
				     struct _image_t*,
				     float, float);


typedef struct
{
    double r, g, b, a;
} GimpRGB;

typedef struct _GtkObject GtkObject;

typedef struct
{
    float *values;
} curve_t;

typedef struct
{
    color_t *values;
} gradient_t;

typedef struct _userval_t
{
    union
    {
	int int_const;
	float float_const;
	int bool_const;
	struct _image_t *image;
	curve_t *curve;
	gradient_t *gradient;

	struct
	{
#ifndef OPENSTEP
	    GimpRGB button_value;
#endif
	    color_t value;
	} color;
    } v;

#ifndef OPENSTEP
    GtkObject *widget_object;
#endif
} userval_t;


struct _input_drawable_t;
struct _mathfuncs_t;

typedef struct _image_t
{
    int type;
    int id;			/* globally unique */
    int pixel_width;
    int pixel_height;
    union
    {
	struct _input_drawable_t *drawable;
	struct {
	    float ax;
	    float bx;
	    float ay;
	    float by;
	    float *data;
	} floatmap;
	struct {
	    struct _image_t *original;
	    float x_factor;
	    float y_factor;
	} resize;
	struct {
	    /* for rendering */
	    struct _mathfuncs_t *funcs;
	    /* for getting single pixels - never called for the root closure */
	    filter_func_t func;
	    mathmap_pools_t *pools;
	    void *xy_vars;
	    int num_args;
	    userval_t args[];
	} closure;
    } v;
} image_t;


typedef struct _filter_t filter_t;
typedef struct _interpreter_insn_t interpreter_insn_t;
typedef struct _GArray GArray;
typedef void* initfunc_t;

typedef struct _mathmap_t
{
    filter_t *filters;
    filter_t *current_filter;	/* only valid during parsing */
    filter_t *main_filter;

    unsigned int flags;

    /* for CC */
    initfunc_t initfunc;
    /* FIXME: for LLVM - remove eventually */
    struct _mathfuncs_t *mathfuncs;

    void *module_info;

    struct _mathmap_t *next;
} mathmap_t;


/* All the functions required to render an image efficiently */
typedef struct _mathfuncs_t
{
    init_frame_func_t init_frame;
    init_slice_func_t init_slice;
    calc_lines_func_t calc_lines;

    /* FIXME: only used for LLVM - remove eventually */
    llvm_init_frame_func_t llvm_init_frame_func;
    llvm_filter_func_t main_filter_func;
    init_x_or_y_func_t init_x_func;
    init_x_or_y_func_t init_y_func;
} mathfuncs_t;


typedef color_t (*orig_val_pixel_func_t) (struct _mathmap_invocation_t*, float, float, image_t*, int);


/* dummy declarations - we never need those here */
typedef void* GMutex;
typedef void* GCond;
typedef void* native_filter_cache_entry_t;

typedef struct _mathmap_invocation_t
{
    mathmap_t *mathmap;

    userval_t *uservals;

    /* FIXME: These should eventually go into image_t */
    int antialiasing;
    orig_val_pixel_func_t orig_val_func;

    int supersampling;

    int output_bpp;

    int edge_behaviour_x, edge_behaviour_y;
    color_t edge_color_x, edge_color_y;

    /* These are in pixel coordinates: */
    int img_width, img_height;
    int render_width, render_height;

    /* These are in virtual coordinates: */
    float image_R;		/* FIXME: remove and calculate in
				   filter code */

    int row_stride;

    unsigned char * volatile rows_finished;

    mathmap_pools_t pools;	/* used exclusively for the native filter cache */
    GMutex *native_filter_cache_mutex;
    GCond *native_filter_cache_cond;
    native_filter_cache_entry_t *native_filter_cache;

    /* FIXME: remove - it's in the closure */
    mathfuncs_t mathfuncs;

    int do_debug;
    int num_debug_tuples;
    tuple_t *debug_tuples[MAX_DEBUG_TUPLES];
} mathmap_invocation_t;

typedef struct _mathmap_frame_t
{
    mathmap_invocation_t *invocation;

    int frame_render_width, frame_render_height;

    int current_frame;
    float current_t;

    void *xy_vars;
    mathmap_pools_t pools;
} mathmap_frame_t;

typedef struct _mathmap_slice_t
{
    mathmap_frame_t *frame;

    float sampling_offset_x, sampling_offset_y;
    int region_x, region_y, region_width, region_height;

    void *y_vars;
    mathmap_pools_t pools;
} mathmap_slice_t;


color_t get_orig_val_pixel (struct _mathmap_invocation_t *invocation, float x, float y, struct _image_t *image, int frame);
color_t get_orig_val_intersample_pixel (struct _mathmap_invocation_t *invocation, float x, float y, struct _image_t *image, int frame);

float* get_floatmap_pixel (struct _mathmap_invocation_t *invocation, struct _image_t *image, float x, float y, float frame);

struct _image_t* render_image (struct _mathmap_invocation_t *invocation, struct _image_t *image,
			       int width, int height, mathmap_pools_t *pools, int force);


int image_new_id (void);


image_t* make_resize_image (image_t *image, float x_factor, float y_factor, mathmap_pools_t *pools);


extern float libnoise_perlin (int num_octaves, float persistence, float lacunarity,
			      float x, float y, float z);
extern float libnoise_billow (int num_octaves, float persistence, float lacunarity,
			      float x, float y, float z);
extern float libnoise_ridged_multi (int num_octaves, float lacunarity,
				    float x, float y, float z);
extern float libnoise_voronoi (float displacement, float x, float y, float z);


typedef struct _tree_vector_t tree_vector_t;

extern tree_vector_t* new_tree_vector (mathmap_pools_t *pools, int length, float *data);
extern float tree_vector_get (tree_vector_t *tv, int index);
extern tree_vector_t* tree_vector_set (mathmap_pools_t *pools, tree_vector_t *tv, int index, float value);


double g_random_double_range (double min, double max);

struct _gsl_vector;
typedef struct _gsl_vector gsl_vector;
struct _gsl_matrix;
typedef struct _gsl_matrix gsl_matrix;

gsl_matrix * gsl_matrix_alloc (const size_t n1, const size_t n2);
void gsl_matrix_free (gsl_matrix * m);
void gsl_matrix_set(gsl_matrix * m, const size_t i, const size_t j, const double x);

gsl_vector *gsl_vector_alloc (const size_t n);
void gsl_vector_free (gsl_vector * v);
double gsl_vector_get (const gsl_vector * v, const size_t i);
void gsl_vector_set (gsl_vector * v, const size_t i, double x);

int gsl_linalg_HH_solve (gsl_matrix * A, const gsl_vector * b, gsl_vector * x);

#define GSL_PREC_SINGLE		1

double gsl_sf_ellint_Kcomp (double k, unsigned int mode);
double gsl_sf_ellint_Ecomp (double k, unsigned int mode);

double gsl_sf_ellint_F (double phi, double k, unsigned int mode);
double gsl_sf_ellint_E (double phi, double k, unsigned int mode);
double gsl_sf_ellint_P (double phi, double k, double n, unsigned int mode);
double gsl_sf_ellint_D (double phi, double k, double n, unsigned int mode);

double gsl_sf_ellint_RC (double x, double y, unsigned int mode);
double gsl_sf_ellint_RD (double x, double y, double z, unsigned int mode);
double gsl_sf_ellint_RF (double x, double y, double z, unsigned int mode);
double gsl_sf_ellint_RJ (double x, double y, double z, double p, unsigned int mode);

int gsl_sf_elljac_e (double u, double m, double *sn, double *cn, double *dn);

complex float cgamma (complex float z);

double gsl_sf_beta (double a, double b);
double gsl_sf_gamma (double x);

extern void save_debug_tuples (mathmap_invocation_t *invocation, int row, int col);

#define DECLARE_NATIVE_FILTER(name)	extern image_t* name (mathmap_invocation_t*, userval_t*, mathmap_pools_t*)
$native_filter_decls

$filter_begin
static void
calc_lines_$name (mathmap_slice_t *slice, image_t *closure, int first_row, int last_row, void *q, int floatmap);

static void
init_frame_$name (mathmap_frame_t *mmframe, image_t *closure);

static void
init_slice_$name (mathmap_slice_t *slice, image_t *closure);

static float*
filter_$name (mathmap_invocation_t *invocation, image_t *closure, float x, float y, float t, mathmap_pools_t *pools);

static mathfuncs_t mathfuncs_$name;
$filter_end

#undef ARG
#define ARG(i)			(arguments[(i)])

$filter_begin
typedef struct
{
    $xy_decls
} xy_const_vars_t_$name;

typedef struct
{
    $y_decls
} y_const_vars_t_$name;

static void
calc_lines_$name (mathmap_slice_t *slice, image_t *closure, int first_row, int last_row, void *q, int floatmap)
{
    mathmap_frame_t *mmframe = slice->frame;
    mathmap_invocation_t *invocation = mmframe->invocation;
    color_t (*get_orig_val_pixel_func) (mathmap_invocation_t*, float, float, image_t*, int);
    int row, col;
    float t = mmframe->current_t;
    float R = invocation->image_R;
    int __canvasPixelW = invocation->img_width;
    int __canvasPixelH = invocation->img_height;
    int __renderPixelW = invocation->render_width;
    int __renderPixelH = invocation->render_height;
    float sampling_offset_x = slice->sampling_offset_x, sampling_offset_y = slice->sampling_offset_y;
    int origin_x = slice->region_x, origin_y = slice->region_y;
    int frame = mmframe->current_frame;
    int output_bpp = invocation->output_bpp;
    int is_bw = output_bpp == 1 || output_bpp == 2;
    int need_alpha = output_bpp == 2 || output_bpp == 4;
    int alpha_index = output_bpp - 1;
    xy_const_vars_t_$name *xy_vars = mmframe->xy_vars;
    mathmap_pools_t pixel_pools;
    mathmap_pools_t *pools;
    int region_x = slice->region_x;
    int frame_render_width = mmframe->frame_render_width;
    int frame_render_height = mmframe->frame_render_height;
    userval_t *arguments = closure->v.closure.args;

    mathmap_pools_init_local(&pixel_pools);

    first_row = MAX(0, first_row);
    last_row = MIN(last_row, slice->region_y + slice->region_height);

    get_orig_val_pixel_func = invocation->orig_val_func;

    for (row = first_row - slice->region_y; row < last_row - slice->region_y; ++row)
    {
	float y = CALC_VIRTUAL_Y(row + slice->region_y, frame_render_height, sampling_offset_y);
	unsigned char *p = q;
	float *fp = q;

	pools = &slice->pools;

	$x_decls

	$x_code

	pools = &pixel_pools;

	for (col = 0; col < slice->region_width; ++col)
	{
	    y_const_vars_t_$name *y_vars = &((y_const_vars_t_$name*)slice->y_vars)[col];
	    float x = CALC_VIRTUAL_X(col + region_x, frame_render_width, sampling_offset_x);
	    float *return_tuple;

	    if (invocation->do_debug)
		invocation->num_debug_tuples = 0;

	    mathmap_pools_reset(pools);

	    {
		$m
	    }

	    if (floatmap)
	    {
		int i;

		for (i = 0; i < NUM_FLOATMAP_CHANNELS; ++i)
		    fp[i] = return_tuple[i];
	    }
	    else
	    {
		if (is_bw)
		    p[0] = (TUPLE_RED(return_tuple) * 0.299
			    + TUPLE_GREEN(return_tuple) * 0.587
			    + TUPLE_BLUE(return_tuple) * 0.114) * 255.0;
		else
		{
		    p[0] = TUPLE_RED(return_tuple) * 255.0;
		    p[1] = TUPLE_GREEN(return_tuple) * 255.0;
		    p[2] = TUPLE_BLUE(return_tuple) * 255.0;
		}
		if (need_alpha)
		    p[alpha_index] = TUPLE_ALPHA(return_tuple) * 255.0;
	    }

	    if (invocation->do_debug)
		save_debug_tuples(invocation, row, col);

	    p += output_bpp;
	    fp += NUM_FLOATMAP_CHANNELS;
	}

	if (floatmap)
	    q = (float*)q + frame_render_width * NUM_FLOATMAP_CHANNELS;
	else
	    q = (unsigned char*)q + invocation->row_stride;

	if (!invocation->supersampling)
	    invocation->rows_finished[row] = 1;
    }

    mathmap_pools_free(&pixel_pools);
}

static void
init_frame_$name (mathmap_frame_t *mmframe, image_t *closure)
{
    mathmap_invocation_t *invocation = mmframe->invocation;
    xy_const_vars_t_$name *xy_vars;
    color_t (*get_orig_val_pixel_func) (mathmap_invocation_t*, float, float, image_t*, int);
    int frame = mmframe->current_frame;
    float t = mmframe->current_t;
    int __canvasPixelW = invocation->img_width;
    int __canvasPixelH = invocation->img_height;
    int __renderPixelW = invocation->render_width;
    int __renderPixelH = invocation->render_height;
    float R = invocation->image_R;
    mathmap_pools_t *pools = &mmframe->pools;
    userval_t *arguments = closure->v.closure.args;

    get_orig_val_pixel_func = invocation->orig_val_func;

    mmframe->xy_vars = xy_vars = (xy_const_vars_t_$name*)mathmap_pools_alloc(pools, sizeof(xy_const_vars_t_$name));

    {
	$xy_code
    }
}

static void
init_slice_$name (mathmap_slice_t *slice, image_t *closure)
{
    mathmap_frame_t *mmframe = slice->frame;
    mathmap_invocation_t *invocation = mmframe->invocation;
    color_t (*get_orig_val_pixel_func) (mathmap_invocation_t*, float, float, image_t*, int);
    int frame = mmframe->current_frame;
    float t = mmframe->current_t;
    int __canvasPixelW = invocation->img_width;
    int __canvasPixelH = invocation->img_height;
    int __renderPixelW = invocation->render_width;
    int __renderPixelH = invocation->render_height;
    float R = invocation->image_R;
    mathmap_pools_t *pools = &slice->pools;
    userval_t *arguments = closure->v.closure.args;

    get_orig_val_pixel_func = invocation->orig_val_func;

    slice->y_vars = (y_const_vars_t_$name*)mathmap_pools_alloc(pools, sizeof(y_const_vars_t_$name) * slice->region_width);

    {
	xy_const_vars_t_$name *xy_vars = mmframe->xy_vars;
	int col;

	for (col = 0; col < slice->region_width; ++col)
	{
	    y_const_vars_t_$name *y_vars = &((y_const_vars_t_$name*)slice->y_vars)[col];
	    float x = CALC_VIRTUAL_X(col + slice->region_x, mmframe->frame_render_width, slice->sampling_offset_x);

	    {
		$y_code
	    }
	}
    }
}

static float*
filter_$name (mathmap_invocation_t *invocation, image_t *closure, float x, float y, float t, mathmap_pools_t *pools)
{
    color_t (*get_orig_val_pixel_func) (mathmap_invocation_t*, float, float, image_t*, int);
    int frame = 0;
    int __canvasPixelW = invocation->img_width;
    int __canvasPixelH = invocation->img_height;
    int __renderPixelW = invocation->render_width;
    int __renderPixelH = invocation->render_height;
    float R = invocation->image_R;
    float *return_tuple;
    xy_const_vars_t_$name *xy_vars;
    y_const_vars_t_$name _y_vars;
    y_const_vars_t_$name *y_vars = &_y_vars;
    userval_t *arguments = closure->v.closure.args;

    get_orig_val_pixel_func = invocation->orig_val_func;

    if (closure->v.closure.xy_vars == 0)
    {
	mathmap_pools_t *pools = closure->v.closure.pools;

	xy_vars = mathmap_pools_alloc(pools, sizeof(xy_const_vars_t_$name));

	{
	    $xy_code
	}

	closure->v.closure.xy_vars = xy_vars;
    }
    else
	xy_vars = closure->v.closure.xy_vars;

    {
	$y_code
    }
    {
	$x_decls

	$x_code

	{
	    $m
	}
    }

    return return_tuple;
}
$filter_end

mathfuncs_t
mathmapinit (mathmap_invocation_t *invocation)
{
$filter_begin
    mathfuncs_$name.init_frame = &init_frame_$name;
    mathfuncs_$name.init_slice = &init_slice_$name;
    mathfuncs_$name.calc_lines = &calc_lines_$name;
$filter_end

    return mathfuncs_$filter_name;
}
