#include "read_parse.h"

struct orcad_header
{
	orcad_uint8_t  type;
	orcad_uint32_t size;
	orcad_uint32_t unknown;
};

long orcad_read_header(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_header* const hdr);

/* Parse header or headers. Returns file offset after the header(s), and */
/* returns the type and the remaining length in 'out_hdr'. */
long orcad_parse_header(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_header* const out_hdr,
	struct orcad_namemapping_info* const nmi, int allow_old_style);

long orcad_parse_old_header(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_header* const out_hdr,
	struct orcad_namemapping_info* const nmi);

long orcad_peek_field_u8(io_orcad_rctx_t* const rctx, long offs,
	orcad_uint8_t* const out);

long orcad_read_field_u8(io_orcad_rctx_t* const rctx, long offs,
	orcad_uint8_t* const out);

long orcad_read_field_i16(io_orcad_rctx_t* const rctx, long offs,
	orcad_int16_t* const out);
long orcad_read_field_u16(io_orcad_rctx_t* const rctx, long offs,
	orcad_uint16_t* const out);

long orcad_read_field_i32(io_orcad_rctx_t* const rctx, long offs,
	orcad_int32_t* const out);
long orcad_read_field_u32(io_orcad_rctx_t* const rctx, long offs,
	orcad_uint32_t* const out);

long orcad_skip_field_8(io_orcad_rctx_t* const rctx, long offs,
	const orcad_uint8_t expected);

long orcad_skip_field_16(io_orcad_rctx_t* const rctx, long offs,
	const orcad_uint16_t expected);

long orcad_skip_field_32(io_orcad_rctx_t* const rctx, long offs,
	const orcad_uint32_t expected);

/* read zero-terminated string with the specified length */
long orcad_read_string(io_orcad_rctx_t* const rctx, long offs, char** buf,
	orcad_uint16_t len);

/* read zero-terminated string which is prefixed with a 16-bit length field */
long orcad_read_string2(io_orcad_rctx_t* const rctx, long offs, char** buf);

const char* orcad_type2str(const enum orcad_type type);
const char* orcad_primtype2str(const enum orcad_primtype primtype);

struct orcad_node* orcad_create_node__(io_orcad_rctx_t* const rctx,
	long* const p_offs, const size_t struct_size, const enum orcad_type type,
	struct orcad_node* const parent);

struct orcad_node* orcad_create_node_from__(io_orcad_rctx_t* const rctx,
	const long offs, const size_t struct_size, const enum orcad_type type,
	const struct orcad_header* const p_hdr, struct orcad_node* const parent,
	struct orcad_namemapping_info* const nmi);

void orcad_error_backtrace__(struct orcad_node* node, const char* const msg);

void orcad_create_prim_callback__(struct orcad_prim* const prim);

#define orcad_create_node(_type_name_, _type_) \
	_type_name_* const node = (_type_name_*)orcad_create_node__( \
		rctx, &offs, sizeof(_type_name_), _type_, parent); \
	if(NULL==node) \
	{ \
		return -1; \
	} \
	else \
	{ \
		*out_node = &node->node; \
	}

#define orcad_create_node_from(_type_name_, _type_, _hdr_, _nmi_) \
	_type_name_* const node = (_type_name_*)orcad_create_node_from__( \
		rctx, offs, sizeof(_type_name_), _type_, _hdr_, parent, _nmi_); \
	if(NULL==node) \
	{ \
		return -1; \
	} \
	else \
	{ \
		*out_node = &node->node; \
	}

#define orcad_create_xnode(_typename_, _type_) \
	_typename_* const node = (_typename_*)calloc(1, sizeof(_typename_)); \
	\
	if(NULL==node) \
	{ \
		fprintf(stderr, "Error: Could not allocate memory for %s\n", \
			orcad_type2str(_type_)); \
		return -1; \
	} \
	\
	do \
	{ \
		node->node.type = (_type_); \
		node->node.offs = offs; \
		node->node.size = 0; /* dunno, fill later? */ \
		node->node.parent = parent; \
		\
		*out_node = &node->node; \
	} \
	while(0)

#define read_u8(field) \
	if(0>(offs=orcad_read_field_u8(rctx, offs, &node->field))) \
	{ \
		orcad_error_backtrace__(&node->node, "read '" #field "'"); \
		return -1; \
	}

#define read_i16(field) \
	if(0>(offs=orcad_read_field_i16(rctx, offs, &node->field))) \
	{ \
		orcad_error_backtrace__(&node->node, "read '" #field "'"); \
		return -1; \
	}

#define read_u16(field) \
	if(0>(offs=orcad_read_field_u16(rctx, offs, &node->field))) \
	{ \
		orcad_error_backtrace__(&node->node, "read '" #field "'"); \
		return -1; \
	}

#define read_i32(field) \
	if(0>(offs=orcad_read_field_i32(rctx, offs, &node->field))) \
	{ \
		orcad_error_backtrace__(&node->node, "read '" #field "'"); \
		return -1; \
	}

#define read_u32(field) \
	if(0>(offs=orcad_read_field_u32(rctx, offs, &node->field))) \
	{ \
		orcad_error_backtrace__(&node->node, "read '" #field "'"); \
		return -1; \
	}

#define vread_u8(var) \
	if(0>(offs=orcad_read_field_u8(rctx, offs, &(var)))) \
	{ \
		fprintf(stderr, "Error: Could not read '%s'\n", #var); \
		return -1; \
	}

#define vread_i16(var) \
	if(0>(offs=orcad_read_field_i16(rctx, offs, &(var)))) \
	{ \
		fprintf(stderr, "Error: Could not read '%s'\n", #var); \
		return -1; \
	}

#define vread_u16(var) \
	if(0>(offs=orcad_read_field_u16(rctx, offs, &(var)))) \
	{ \
		fprintf(stderr, "Error: Could not read '%s'\n", #var); \
		return -1; \
	}

#define vread_i32(var) \
	if(0>(offs=orcad_read_field_i32(rctx, offs, &(var)))) \
	{ \
		fprintf(stderr, "Error: Could not read '%s'\n", #var); \
		return -1; \
	}

#define vread_u32(var) \
	if(0>(offs=orcad_read_field_u32(rctx, offs, &(var)))) \
	{ \
		fprintf(stderr, "Error: Could not read '%s'\n", #var); \
		return -1; \
	}

long orcad_read_nodes__(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node*** const p_array,
	size_t count, long (*const read_object)(io_orcad_rctx_t* const rctx,
		long offs, struct orcad_node* const parent,
		struct orcad_node** const out_node));

#define read_node_array(child_name, child_read_func) \
	do \
	{ \
		read_u16(num_ ## child_name ## s); \
		\
		if(0>(offs=orcad_read_nodes__(rctx, offs, &node->node, \
			(struct orcad_node***)&node->child_name ## s, \
			node->num_ ## child_name ## s, child_read_func))) \
		{ \
			orcad_error_backtrace__(&node->node, "read '" #child_name "s'"); \
			return -1; \
		} \
	} \
	while(0)

long orcad_skip_object(io_orcad_rctx_t* const rctx, long offs);
long orcad_skip_objects(io_orcad_rctx_t* const rctx, long offs, int count);

long orcad_read_primitive(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_prim** const out_prim);
void orcad_free_primitive(struct orcad_prim* const prim);

long orcad_read_inlinepageobject(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node);
long orcad_read_symboldisplayprop(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node);

long orcad_read_pagesettings(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_pagesettings* const settings);

long orcad_skip_magic(io_orcad_rctx_t* const rctx, long offs);

/* tests whether we reached the expected end; or we are at the magic number */
/* (file position should be at 'offs', and it will be restored) */
int orcad_is_end_or_magic(io_orcad_rctx_t* const rctx, long offs, long end);
