#ifndef SCH_RND_SIM_H
#define SCH_RND_SIM_H

#include <genvector/vts0.h>
#include <liblihata/dom.h>
#include <libcschem/abstract.h>

/* setup: sim implementation specific opaque data for simulator state */
typedef struct sch_sim_setup_s sch_sim_setup_t;

/*** Mod(ification)s ***/

typedef enum sch_sim_mod_type_s {
	SCH_SIMOD_ADD = 0,
	SCH_SIMOD_OMIT,
	SCH_SIMOD_EDIT_ATTR,
	SCH_SIMOD_DISCON,
	SCH_SIMOD_TEMP,

	SCH_SIMOD_invalid = -1
} sch_sim_mod_type_t;

extern const char *sch_simmod_type_names[];
sch_sim_mod_type_t sch_sim_str2mod_type(const char *mtype);

typedef enum sch_sim_mod_device_s {
	SCH_SIMDEV_V,
	SCH_SIMDEV_I,
	SCH_SIMDEV_R,
	SCH_SIMDEV_C,
	SCH_SIMDEV_L,

	SCH_SIMDEV_max,
	SCH_SIMDEV_invalid = -1
} sch_sim_mod_device_t;

extern const char *sch_simmod_dev_names[];
sch_sim_mod_device_t sch_sim_str2mod_device(const char *mdev);
extern const int sch_sim_device_has_ac[];  /* 0 or 1 for each device above; tells whether the device has AC component */
extern const int sch_sim_device_has_tdf[]; /* 0 or 1 for each device above; tells whether the device has TDF component */


typedef enum sch_sim_mod_target_type_s {
	SCH_SIMTT_COMP,
	SCH_SIMTT_PORT,
	SCH_SIMTT_NET,
	SCH_SIMTT_invalid = -1
} sch_sim_mod_target_type_t;

extern const char *sch_simmod_target_type_names[];
sch_sim_mod_target_type_t sch_sim_str2mod_target_type(const char *typ);


typedef enum sch_sim_mod_tdf_s {
	SCH_SIMTDF_NONE,
	SCH_SIMTDF_PULSE,
	SCH_SIMTDF_SIN,
	SCH_SIMTDF_EXP,
	SCH_SIMTDF_PWL,


	SCH_SIMTDF_max,
	SCH_SIMTDF_invalid = -1
} sch_sim_mod_tdf_t;

extern const char *sch_simmod_tdf_names[];
sch_sim_mod_tdf_t sch_sim_str2mod_tdf(const char *mtype);

typedef struct sch_sim_mod_tdf_param_s {
	const char *name;
	const char *desc;
	unsigned optional:1;
} sch_sim_mod_tdf_param_t;

extern const sch_sim_mod_tdf_param_t *sch_sim_mod_tdf_params[]; /* indexed with sch_sim_mod_tdf_t */


typedef struct sch_sim_mod_add_s {
	sch_sim_mod_device_t device;
	char *pos, *neg; /* terminal or network; if neg is not specified, gnd is assumed */
	char *val; /* main value; for V and I: dc component */

	sch_sim_mod_tdf_t tdf;  /* for V and I: name of the time dependant function */
	vts0_t *params;         /* for tdf */
} sch_sim_mod_add_t;

typedef struct sch_sim_mod_omit_s {
	sch_sim_mod_target_type_t type; /* should be comp or net */
	char *name;
} sch_sim_mod_omit_t;

typedef struct sch_sim_edit_attr_s {
	sch_sim_mod_target_type_t type;
	char *name;
	char *key;
	char *val;
} sch_sim_mod_edit_attr_t;

typedef struct sch_sim_mod_disconn_s {
	char *comp_name, *port_name;
} sch_sim_mod_disconn_t;

typedef struct sch_sim_mod_temp_s {
	double C; /* temperature in celsius */
} sch_sim_mod_temp_t;

typedef struct sch_sim_mod_s {
	sch_sim_mod_type_t type;
	union {
		sch_sim_mod_add_t add;
		sch_sim_mod_omit_t omit;
		sch_sim_mod_edit_attr_t edit_attr;
		sch_sim_mod_disconn_t disconn;
		sch_sim_mod_temp_t temp;
	} data;
} sch_sim_mod_t;

/*** Analysis ***/

typedef enum sch_sim_analysis_type_s {
	SCH_SIMAN_invalid = -1,

	/* order and value does matter, see:
	    afld[], sch_siman_names[], sch_siman_description[] */
	SCH_SIMAN_OP = 0, /* dc operating point; no params */

	/* transient; params: [start], stop, incr, [incr_max] */
	SCH_SIMAN_TRAN_LIN,

	/* frequency domain; params: numpt, start, stop */
	SCH_SIMAN_AC_DEC,
	SCH_SIMAN_AC_OCT,
	SCH_SIMAN_AC_LIN,

	/* dc transfer; params: src, start, stop, incr */
	SCH_SIMAN_DC_LIN,


	/* distortion; params: numpt, start, stop */
	SCH_SIMAN_DC_DISTO_DEC,
	SCH_SIMAN_DC_DISTO_OCT,
	SCH_SIMAN_DC_DISTO_LIN,

	SCH_SIMAN_DC_NOISE_DEC, /* noise; params: port1, port2, numpt, start, stop */

	SCH_SIMAN_PREVIOUS /* keep the results of the previous run but extract a different output */


	/* TODO: PZ (pole zero), sens, sp, tf */
} sch_sim_analysis_type_t;

extern const char *sch_siman_names[];
extern const char *sch_siman_description[];
extern const char *sch_siman_x_axis_name[];
extern const int sch_siman_x_axis_log[]; /* 0 for lin; 10 or 8 for logarithmic, -1 for non-graph data */

sch_sim_analysis_type_t sch_sim_str2analysis_type(const char *stype);


typedef struct sch_sim_analysis_s {
	sch_sim_analysis_type_t type;
	char *start, *stop, *incr, *incr_max; /* measurement range */
	int numpt; /* number of points, per decade, per octave or over the range in LIN */
	char *src;
	char *port1[2];
	char *port2[2];

	void *user_data;
} sch_sim_analysis_t;

/*** Presentation ***/

typedef enum sch_sim_presentation_type_s {
	SCH_SIMPRES_PRINT,
	SCH_SIMPRES_PLOT,

	SCH_SIMPRES_invalid = -1
} sch_sim_presentation_type_t;
extern const char *sch_simpres_names[];
sch_sim_presentation_type_t sch_sim_str2presentation_type(const char *ptype);

typedef struct sch_sim_presentation_s {
	sch_sim_presentation_type_t type;
	vts0_t props; /* property names to print or plot (e.g. nodes) */

	char *outfn; /* file anme of the output; used by the sim backend */

	void *user_data;
} sch_sim_presentation_t;

/*** Execution ***/

typedef struct sch_sim_exec_s {
	const char *name;

	/* allocater (or free) a simulation setup for the currently active project (sheet) */
	sch_sim_setup_t *(*alloc)(void);
	void (*free)(sch_sim_setup_t *setup);

	/* compile and export the current circuit; shall be called only once per setup */
	int (*set_global)(csch_abstract_t *abst, int eng_prio, const char *name, fgw_arg_t *val);

	/* compile and export the current circuit; shall be called only once per setup */
	int (*add_circuit)(sch_sim_setup_t *setup);

	/* add an output; can be called multiple times; an and pres are free'd by the
	   sim backend (immediately upon error or at ->free()) */
	int (*add_output)(sch_sim_setup_t *setup, sch_sim_analysis_t *an, sch_sim_presentation_t *pres);

	/* callback from the compiler: add mod 'add' value parameters to an abstract
	   component (should add attributes to comp) */
	void (*mod_add_params)(csch_project_t *prj, csch_acomp_t *comp, sch_sim_mod_device_t device, const char *value, const char *ac_value, sch_sim_mod_tdf_t tdf, lht_node_t *tdf_params, int eng_prio, lht_node_t *mod, long mod_idx);

	/* execute the setup as prepared */
	int (*exec)(csch_project_t *prj, sch_sim_setup_t *setup);


	/*** accessors for data and plots ***/

	/* open and close an opaque result stream; NULL means error */
	void *(*result_open)(csch_project_t *prj, sch_sim_setup_t *setup, int output_idx);
	void (*result_close)(sch_sim_setup_t *setup, void *stream);

	/* read the next item of data into dst; the first N items of data are
	   the presentation props values, one item per prop. An extra item is
	   added at the end for time, when available. The stream is acquired from a
	   result_open earlier. Returns 0 on success, -1 on error or eof. The strings
	   in dst are owned by the sim backend and shall not be freed; they are
	   valud until the next call to any sim backend function. If dst is NULL,
	   no string is parsed or stored, but lines are read (useful for counting
	   lines of input) */
	int (*result_read)(sch_sim_setup_t *setup, void *stream, vts0_t *dst);

	/* Next call to result_read() will return the first line */
	void (*result_rewind)(sch_sim_setup_t *setup, void *stream);

	/* TODO: SOA (warnings; safe operating area) */
} sch_sim_exec_t;

/* If viewid is -1, use current view. Return NULL on error */
sch_sim_exec_t *sch_sim_get_sim_exec(csch_project_t *proj, int viewid);


#endif
