/* This header should be included in one C file only! */

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

#ifdef __cplusplus
  extern "C" {
#endif

/* CAT2:
 *      This macro catenates 2 tokens together.
 */
/* STRINGIFY:
 *      This macro surrounds its token with double quotes.
 */
#ifndef CAT2
# if 42 == 1
#  define CAT2(a,b)a/**/b
#  define CAT3(a,b,c)a/**/b/**/c
#  define CAT4(a,b,c,d)a/**/b/**/c/**/d
#  define CAT5(a,b,c,d,e)a/**/b/**/c/**/d/**/e
#  define STRINGIFY(a)"a"
                /* If you can get stringification with catify, tell me how! */
# endif
# if 42 == 42
#  define CAT2(a,b)a ## b
#  define CAT3(a,b,c)a ## b ## c
#  define CAT4(a,b,c,d)a ## b ## c ## d
#  define CAT5(a,b,c,d,e)a ## b ## c ## d ## e
#  define StGiFy(a)# a
#  define STRINGIFY(a)StGiFy(a)
#  define SCAT2(a,b)StGiFy(a) StGiFy(b)
#  define SCAT3(a,b,c)StGiFy(a) StGiFy(b) StGiFy(c)
#  define SCAT4(a,b,c,d)StGiFy(a) StGiFy(b) StGiFy(c) StGiFy(d)
#  define SCAT5(a,b,c,d,e)StGiFy(a) StGiFy(b) StGiFy(c) StGiFy(d) StGiFy(e)
# endif
# ifndef CAT2
#   include "Bletch: How does this C preprocessor catenate tokens?"
# endif
#endif /* CAT2 */


#define TERM_CAN_MULTIPLOT    1  /* tested if stdout not redirected */
#define TERM_CANNOT_MULTIPLOT 2  /* tested if stdout is redirected  */
#define TERM_BINARY           4  /* open output file with "b"       */

#ifndef NO_JUNK_SMALL

/* Compatibility with the old gnuplot: */
extern  FILE *outfile;
FILE *outfile = NULL;

extern  FILE *gpoutfile;
FILE *gpoutfile = NULL;

static outfile_set;
static void
set_gpoutfile(void)
{
  outfile = stdout;
  gpoutfile = stdout;
}

#define SET_OUTFILE (outfile_set++ ? 1 : (set_gpoutfile(), 1))

extern int encoding;
int        encoding = 0;
extern float                   xoffset;  /* x origin */
extern float                   yoffset;  /* y origin */
float                   xoffset = 0.0;  /* x origin */
float                   yoffset = 0.0;  /* y origin */
extern int		multiplot;
int		multiplot		= 0;

extern char *outstr;
#define MAX_ID_LEN 50
/* char        outstr[MAX_ID_LEN+1] = "STDOUT"; */
char        *outstr = NULL;
extern double ticscale; /* scale factor for tic marks (was (0..1])*/
double        ticscale = 1.0; /* scale factor for tic mark */

char *input_line = NULL;
int inline_num;          /* from command.c */

float xsize=1.0, ysize=1.0;
double pointsize=1.0;		/* During test! */

int interactive;    /* from plot.c */
char *infile_name;       /* from plot.c */
extern char     default_font[];
char            default_font[MAX_ID_LEN+1] = "\0"; /* Entry added by DJL */

typedef int TBOOLEAN;

enum DATA_TYPES {
	INTGR, CMPLX
};

#if !(defined(ATARI)&&defined(__GNUC__)&&defined(_MATH_H)) &&  !(defined(MTOS)&&defined(__GNUC__)&&defined(_MATH_H)) /* FF's math.h has the type already */
struct cmplx {
	double real, imag;
};
#endif

struct value {
	enum DATA_TYPES type;
	union {
		int int_val;
		struct cmplx cmplx_val;
	} v;
};

struct lexical_unit {	/* produced by scanner */
	TBOOLEAN is_token;	/* true if token, false if a value */
	struct value l_val;
	int start_index;	/* index of first char in token */
	int length;			/* length of token in chars */
};

/* char *token; */
#define MAX_TOKENS 20
extern struct lexical_unit *token;
struct lexical_unit tokens[MAX_TOKENS];	/* We only process options,
					   there should not be many */
struct lexical_unit *token = tokens;
long c_token = 0, num_tokens = 0;
char term_options[200] = "";

/* New with 3.7.1: */

#define FIRST_Z_AXIS 0
#define FIRST_Y_AXIS 1
#define FIRST_X_AXIS 2
#define SECOND_Z_AXIS 4 /* for future expansion ;-) */
#define SECOND_Y_AXIS 5
#define SECOND_X_AXIS 6
/* extend list for datatype[] for t,u,v,r though IMHO
 * they are not relevant to time data [being parametric dummies]
 */
#define T_AXIS 3  /* fill gap */
#define R_AXIS 7  /* never used ? */
#define U_AXIS 8
#define V_AXIS 9

#define AXIS_ARRAY_SIZE 10
#define DATATYPE_ARRAY_SIZE 10

extern double min_array[], max_array[], base_array[], log_base_array[];
extern TBOOLEAN log_array[];
/* graphics.c */
extern int xleft, xright, ybot, ytop;
extern TBOOLEAN is_3d_plot;

double min_array[AXIS_ARRAY_SIZE], max_array[AXIS_ARRAY_SIZE], base_array[AXIS_ARRAY_SIZE], log_base_array[AXIS_ARRAY_SIZE];
TBOOLEAN log_array[AXIS_ARRAY_SIZE];
int xleft, xright, ybot, ytop;
TBOOLEAN is_3d_plot;

/* End of 3.7.1 additions */

/* 3.7.0-devel additions */

extern float surface_rot_z;
extern TBOOLEAN polar;
extern double			base_log_x, base_log_y, base_log_z;
extern TBOOLEAN			is_log_x, is_log_y, is_log_z;
extern double			log_base_log_x2, log_base_log_y2;
extern double base_z;
extern TBOOLEAN screen_ok;

float surface_rot_z = 30.0;
TBOOLEAN polar = 0;
TBOOLEAN is_log_x = 0;
TBOOLEAN is_log_y = 0;
TBOOLEAN is_log_z = 0;
double base_log_x = 0.0;
double base_log_y = 0.0;
double base_log_z = 0.0;
double log_base_log_x = 0.0;
double log_base_log_y = 0.0;
double log_base_log_z = 0.0;
double base_z = 0.0;
TBOOLEAN screen_ok;

void map3d_xy (double x, double y, double z, unsigned int *xt, unsigned int *yt)
{
    croak("Unsupported function map3d_xy called");
}

/* End of 3.7.0-devel additions */

/* Here are the only missing functions: */

struct value*
const_express(struct value*v)
{
    if (token[c_token].is_token)
	croak("Expect a number, got a string");
    *v = token[c_token++].l_val;
    return v;
}

void*
gp_alloc(unsigned long size, char *usage)
{
  return malloc(size);
}

void*
gp_realloc(void *old, unsigned long size, char *usage)
{
  return realloc(old,size);
}

void
bail_to_command_line()
{
  croak("panic: gnuplot");
}

#endif	/* NO_JUNK_SMALL */ 

/* Cannot pull the whole plot.h, too many contradictions. */

#ifdef __ZTC__
typedef int (*FUNC_PTR)(...);
#else
typedef int (*FUNC_PTR)();
#endif

struct TERMENTRY {
        char *name;
#if defined(_Windows) && !defined(WIN32)
        char GPFAR description[80];     /* to make text go in FAR segment */
#else
        char *description;
#endif
        unsigned int xmax,ymax,v_char,h_char,v_tic,h_tic;
        FUNC_PTR options,init,reset,text,scale,graphics,move,vector,linetype,
                put_text,text_angle,justify_text,point,arrow,set_font,
		pointsize;
	int flags;
        FUNC_PTR suspend,resume,fillbox,linewidth;
};

#ifdef _Windows
#  define termentry TERMENTRY far
#else
#  define termentry TERMENTRY
#endif

extern struct termentry *term;
struct termentry *term;

#define RETVOID
#define RETINT , 1

#define F_0 void(*)()
#define F_1 void(*)(int)
#define F_1I int(*)(int)
#define F_1D void(*)(double)
#define F_1IP int(*)(char*)
#define F_2 void(*)(unsigned int,unsigned int)
#define F_2D int(*)(double,double)
#define F_3 void(*)(unsigned int,unsigned int,int)
#define F_3T void(*)(int,int,char*)
#define F_4 void(*)(int,int,int,int)
#define F_5 void(*)(int,int,int,int,int)

#define CALL_G_METH0(method) CALL_G_METH(method,0,(),RETVOID)
#define CALL_G_METH1(method,arg1) CALL_G_METH(method,1,(arg1),RETVOID)
#define CALL_G_METH1I(method,arg1) CALL_G_METH(method,1I,(arg1),RETINT)
#define CALL_G_METH1D(method,arg1) CALL_G_METH(method,1D,(arg1),RETVOID)
#define CALL_G_METH1IP(method,arg1) CALL_G_METH(method,1IP,(arg1),RETINT)
#define CALL_G_METH2(method,arg1,arg2) \
		CALL_G_METH(method,2,((arg1),(arg2)),RETVOID)
#define CALL_G_METH2D(method,arg1,arg2) \
		CALL_G_METH(method,2D,((arg1),(arg2)),RETINT)
#define CALL_G_METH3(method,arg1,arg2,arg3) \
		CALL_G_METH(method,3,((arg1),(arg2),(arg3)),RETVOID)
#define CALL_G_METH3T(method,arg1,arg2,arg3) \
		CALL_G_METH(method,3T,((arg1),(arg2),(arg3)),RETVOID)
#define CALL_G_METH4(method,arg1,arg2,arg3,arg4) \
		CALL_G_METH(method,4,((arg1),(arg2),(arg3),(arg4)),RETVOID)
#define CALL_G_METH5(method,arg1,arg2,arg3,arg4,arg5) \
		CALL_G_METH(method,5,((arg1),(arg2),(arg3),(arg4),(arg5)),RETVOID)

#define CALL_G_METH(method,mult,args,returnval)    (		\
       (term==0) ? (						\
	 croak("No terminal specified") returnval		\
       ) :							\
       (*(CAT2(F_,mult))term->method)args		\
     )

#define GET_G_FLAG(mask)    (		\
       (term==0) ? (						\
	 croak("No terminal specified") RETINT		\
       ) :							\
       (term->flags & (mask)))

#ifdef DONT_POLLUTE_INIT
#  define gptable_init()	CALL_G_METH0(init)
#else
#  define init()		CALL_G_METH0(init)
#  define gptable_init		init
#endif
#define reset()		CALL_G_METH0(reset)
#define text()		CALL_G_METH0(text)
#define options()	CALL_G_METH0(options)
#define graphics()	CALL_G_METH0(graphics)
#define linetype(lt)	CALL_G_METH1(linetype,lt)
#define justify_text(mode)	CALL_G_METH1I(justify_text,mode)
#define text_angle(ang)	CALL_G_METH1I(text_angle,ang)
#define scale(xs,ys)	CALL_G_METH2D(scale,xs,ys)
#define move(x,y)	CALL_G_METH2(move,x,y)
#define vector(x,y)	CALL_G_METH2(vector,x,y)
#define put_text(x,y,str)	CALL_G_METH3T(put_text,x,y,str)
#define point(x,y,p)	CALL_G_METH3(point,x,y,p)
#define arrow(sx,sy,ex,ey,head)	CALL_G_METH5(arrow,sx,sy,ex,ey,head)
#define set_font(font)	CALL_G_METH1IP(set_font,font)
#define setpointsize(size)	CALL_G_METH1D(pointsize,size)
#define suspend()	CALL_G_METH0(suspend)
#define resume()	CALL_G_METH0(resume)
#define fillbox(sx,sy,ex,ey,head)	CALL_G_METH5(fillbox,sx,sy,ex,ey,head)
#define linewidth(size)	CALL_G_METH1D(linewidth,size)
#define can_multiplot()	GET_G_FLAG(TERM_CAN_MULTIPLOT)
#define cannot_multiplot()	GET_G_FLAG(TERM_CANNOT_MULTIPLOT)
#define is_binary()	GET_G_FLAG(TERM_BINARY)

#define termprop(prop) (term->prop)
#define termset(term) my_change_term(term,strlen(term))

struct termentry * change_term(char*,int);

#define TTABLE_STARTPLOT	0
#define TTABLE_ENDPLOT		1
#define TTABLE_STARTMPLOT	2
#define TTABLE_ENDMPLOT		3
#define TTABLE_INIT		4
#define TTABLE_LIST		5
#define TTABLE_COUNT		6

typedef void (*TSET_FP)(char *s);
typedef void (*TST_END_FP)(void);
typedef void (*SET_SIZES_t)(double x, double y);
typedef double (*GET_SIZES_t)(int flag);

struct t_ftable {
  int loaded;
  FUNC_PTR change_term_p;
  TSET_FP term_set_outputp;
  SET_SIZES_t set_sizesp;
  GET_SIZES_t get_sizesp;
  TST_END_FP term_funcs[TTABLE_COUNT];
};

#ifdef DYNAMIC_PLOTTING			/* Can load plotting DLL later */

UNKNOWN_null()
{
    croak("gnuplot-like plotting environment not loaded yet");
}

static void myterm_table_not_loaded_v(void);
static void myterm_table_not_loaded(char*);
static int myterm_table_not_loaded_u();
static void myterm_table_not_loaded_vdd(double x, double y);
static double myterm_table_not_loaded_di(int flag);

#if 0
static int ftable_warned;
static void
tmp_my_term_init
{
  if (!warned++)
     warn("This runtime link with gnuplot-shim does not implement midlevel start/end functions");
  shim_myinit();
}
#endif

static struct t_ftable my_term_ftable = 
{
	0, &myterm_table_not_loaded_u, &myterm_table_not_loaded,
	&myterm_table_not_loaded_vdd,
	&myterm_table_not_loaded_di,
	{&myterm_table_not_loaded_v, &myterm_table_not_loaded_v, 
	 &myterm_table_not_loaded_v, &myterm_table_not_loaded_v,
	 &myterm_table_not_loaded_v, &myterm_table_not_loaded_v}
};

static struct t_ftable *my_term_ftablep = &my_term_ftable;

static void
myterm_table_not_loaded_v(void)
{
    if (!my_term_ftablep->loaded) {
        UNKNOWN_null();
	return;
    }
    croak("This runtime link with gnuplot-shim does not implement midlevel start/end functions");
}

static void
myterm_table_not_loaded(char *s)
{
  myterm_table_not_loaded_v();
}

static void
myterm_table_not_loaded_vdd(double x, double y)
{
  myterm_table_not_loaded_v();
}

static double
myterm_table_not_loaded_di(int flag)
{
  myterm_table_not_loaded_v();
}

static int
myterm_table_not_loaded_u()
{
  myterm_table_not_loaded_v();
  return 0;
}

#  define change_term		(*my_term_ftablep->change_term_p)
#  define term_set_output	(*my_term_ftablep->term_set_outputp)
#  define term_start_plot	(*my_term_ftablep->term_funcs[TTABLE_STARTPLOT])
#  define term_end_plot	(*my_term_ftablep->term_funcs[TTABLE_ENDPLOT])
#  define term_start_multiplot	(*my_term_ftablep->term_funcs[TTABLE_STARTMPLOT])
#  define term_end_multiplot	(*my_term_ftablep->term_funcs[TTABLE_ENDMPLOT])
#  define term_init		(*my_term_ftablep->term_funcs[TTABLE_INIT])
#  define list_terms		(*my_term_ftablep->term_funcs[TTABLE_LIST])
#  define plotsizes_scale	(*my_term_ftablep->set_sizesp)
#  define plotsizes_scale_get	(*my_term_ftablep->get_sizesp)

#  define scaled_xmax()	((int)termprop(xmax)*plotsizes_scale_get(0))
#  define scaled_ymax()	((int)termprop(ymax)*plotsizes_scale_get(1))

#define USE_FUNCTION_FROM_TABLE

static struct termentry *
my_change_term(char*s,int l)
{
    SET_OUTFILE;
    if (!my_term_ftablep->change_term_p)
	UNKNOWN_null();
    return term = (struct termentry *)(*my_term_ftablep->change_term_p)(s,l);
}

static struct termentry dummy_term_tbl[] = {
    {"unknown", "Unknown terminal type - not a plotting device",
	  100, 100, 1, 1,
	  1, 1, UNKNOWN_null, UNKNOWN_null, UNKNOWN_null,
	  UNKNOWN_null, UNKNOWN_null, UNKNOWN_null, UNKNOWN_null, UNKNOWN_null,
	  UNKNOWN_null, UNKNOWN_null, UNKNOWN_null,
     UNKNOWN_null, UNKNOWN_null, UNKNOWN_null, UNKNOWN_null, UNKNOWN_null, 0,
	  UNKNOWN_null, UNKNOWN_null, UNKNOWN_null, UNKNOWN_null},
};

#define set_term_funcp(change_p, term_p) set_term_funcp2((change_p), 0)
/* #define set_term_funcp3(change_p, term_p, tchange) \
			set_term_funcp2((change_p), (tchange)) */

/* This function should be called before any graphic code can be used... */
void
set_term_funcp2(FUNC_PTR change_p, TSET_FP tchange)
{
    SET_OUTFILE;
    my_term_ftable.change_term_p = change_p;
    my_term_ftable.loaded = 1;
    if (tchange) {
	my_term_ftable.term_set_outputp = tchange;
    }
}

/* Used from Math::Pari */
void
set_term_funcp3(FUNC_PTR change_p, void *term_p, TSET_FP tchange)
{
  set_term_funcp2(change_p, tchange);
}

void
set_term_ftable(struct t_ftable *p)
{
  SET_OUTFILE;
  my_term_ftablep = p;
}

#else /* !DYNAMIC_PLOTTING */

extern struct termentry term_tbl[];

#  define my_change_term	change_term
#  define my_term_tbl		term_tbl

extern void term_set_output(char *s);
extern void term_start_plot(void);
extern void term_end_plot(void);
extern void term_start_multiplot(void);
extern void term_end_multiplot(void);
extern void term_init(void);
extern void list_terms(void);

static void
plotsizes_scale(double x, double y)	{ xsize=x; ysize=y; }

static double
plotsizes_get(int flag)	{ return (flag ? ysize : xsize); }

struct t_ftable my_term_ftable =
{
	1, (FUNC_PTR)&change_term, &term_set_output,
	&plotsizes_scale, &plotsizes_get,
	{&term_start_plot, &term_end_plot, 
	 &term_start_multiplot, &term_end_multiplot, &term_init, &list_terms}
};

struct t_ftable *get_term_ftable()	{ SET_OUTFILE; return &my_term_ftable; }
void set_term_ftable()	{ SET_OUTFILE; }


void
set_term_funcp3(FUNC_PTR change_p, void *term_p, TSET_FP tchange)
{
    SET_OUTFILE;
    my_term_ftable.change_term_p = change_p;
    my_term_ftable.loaded = 1;
    if (tchange) {
	my_term_ftable.term_set_outputp = tchange;
    }
}

#define scaled_xmax()	((int)termprop(xmax)*xsize)
#define scaled_ymax()	((int)termprop(ymax)*ysize)

#endif /* !DYNAMIC_PLOTTING */

#define int_get_term_ftable()	((IV)get_term_ftable())
#define int_set_term_ftable(a) (v_set_term_ftable((void*)a))

void
v_set_term_ftable(void *a) { set_term_ftable((struct t_ftable*)a); }

void
setup_gpshim(void) { SET_OUTFILE; }

#ifdef SET_OPTIONS_FROM_STRING
/* This sets the tokens for the options */
void
set_tokens_string(char *start)
{
    char *s = start;
    char *tstart;
    int is_real, is_integer, has_exp;
    
    num_tokens = 0;
    while (num_tokens < MAX_TOKENS) {
	while (*s == ' ' || *s == '\t' || *s == '\n')
	    s++;
	if (!*s)
	    return;
	tstart = s;
	if (*s == ',') {
	    s++;
	    is_integer = is_real = 0;
	    goto process;
	}
	is_integer = is_real = ((*s) != 0);
	if (*s == '+' || *s == '-')
	    s++;
	has_exp = 0;
	while (*s && !(*s == ' ' || *s == '\t' || *s == '\n')) {
	    if (!(*s <= '9' && *s >= '0')) {
		if (*s == '.') {		
		    if (!is_integer)
			is_real = 0;
		    else if (is_integer == 1 && !(s[1] <= '9' && s[1] >= '0'))
			is_real = 0;
		} else if (*s == 'e' || *s == 'E') {
		    if (has_exp)
			is_real = 0;
		    has_exp = 1;
		    if (s[1] == '+' || s[1] == '-')
			s++;
		} else if (*s == ',' && (is_integer || is_real))
		    break;
		else
		    is_real = 0;
		is_integer = 0;
	    } else if (is_integer)
		is_integer++;
	    s++;	    
	}
      process:
	token[num_tokens].start_index = tstart - input_line;
	token[num_tokens].length = s - tstart;
	if (is_integer) {
	    token[num_tokens].is_token = 0;
	    token[num_tokens].l_val.type = INTGR;
	    token[num_tokens].l_val.v.int_val = atoi(tstart);
	} else if (is_real) {
	    token[num_tokens].is_token = 0;
	    token[num_tokens].l_val.type = CMPLX;
	    token[num_tokens].l_val.v.cmplx_val.real = atof(tstart);
	    token[num_tokens].l_val.v.cmplx_val.imag = 0;
	} else {
	    token[num_tokens].is_token = 1;
	}
	num_tokens++;
    }
    if (num_tokens >= MAX_TOKENS) {
	char buf[80];
	sprintf(buf, "panic: more than %d tokens for options", MAX_TOKENS);
	croak(buf);    
    }
}

void
set_options_from(char *s)
{
    char *o = input_line;

    input_line = s;		/* for error reports */
    set_tokens_string(s);
    options();
    input_line = o;
    c_token = num_tokens = 0;
}
#endif

#ifdef GNUPLOT_OUTLINE_STDOUT
int
StartOutput() {}

int
EndOutput() {}

int
OutLine(char *s)
{
   return fprintf(stdout, "%s", s);
}
#endif

#ifdef __cplusplus
  }
#endif
