#include "read_parse.h"

struct orcad_header
{
	my_uint8_t  type;
	my_uint32_t size;
	my_uint32_t unknown;
};

my_uint16_t little2host_16(my_uint16_t x);
my_uint32_t little2host_32(my_uint32_t x);

void indent_impl(FILE* const out, int indent);

#define indent_printf \
	printf

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);

long orcad_read_field_8(io_orcad_rctx_t* const rctx, long offs,
	my_uint8_t* const out);

long orcad_read_field_16(io_orcad_rctx_t* const rctx, long offs,
	my_uint16_t* const out);

long orcad_read_field_32(io_orcad_rctx_t* const rctx, long offs,
	my_uint32_t* const out);

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

long orcad_skip_field_32(io_orcad_rctx_t* const rctx, long offs,
	const my_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,
	const size_t bufsz, my_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 size_t bufsz);

/* TODO: REMOVE! */
void orcad_report_type_mismatch__(io_orcad_rctx_t* const rctx, long offs,
	const my_uint8_t expected, const my_uint8_t actual);
#define orcad_check_hdr_type(_hdr_, _type_) \
	do \
	{ \
		if((_type_)!=(_hdr_).type) \
		{ \
			orcad_report_type_mismatch__(rctx, offs, (_type_), (_hdr_).type); \
			return -1; \
		} \
	} \
	while(0)

const char* orcad_type2str(const enum orcad_type type);

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__(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);

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

#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_) \
	_type_name_* const node = (_type_name_*)orcad_create_node_from__( \
		offs, sizeof(_type_name_), _type_, _hdr_, parent); \
	if(NULL==node) \
	{ \
		return -1; \
	} \
	else \
	{ \
		*out_node = &node->node; \
	}

/* TODO: static assert for proper field sizes! */

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

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

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

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

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

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








/* TODO: REMOVE ALL OF THESE! */
#define dump_time32(tag) \
	do \
	{ \
		my_uint32_t tmp32; \
		time_t tt; \
		\
		if(0>(offs=orcad_read_field_32(rctx, offs, &tmp32))) \
		{ \
			fprintf(stderr, "Error: Could not read %s\n", "" tag); \
			return -1; \
		} \
		\
		tt = tmp32; \
		\
		indent_printf("" tag ": %s", ctime(&tt)); \
	} \
	while(0)

/* read (and dump) 8-bit field from current position */
#define read_8_fmt(var, fmt) \
	do \
	{ \
		if(sizeof(var)!=fio_fread(rctx, (char*)&(var), sizeof(var))) \
		{ \
			fprintf(stderr, "Error: Could not read %s\n", "" #var); \
			return -1; \
		} \
		\
		offs += sizeof(var); \
		\
		indent_printf("" #var ": " fmt "\n", var); \
	} \
	while(0)

/* dump 8-bit field from current position */
#define dump_8_fmt(tag, fmt) \
	do \
	{ \
		my_uint8_t tmp8; \
		\
		if(sizeof(tmp8)!=fio_fread(rctx, (char*)&tmp8, sizeof(tmp8))) \
		{ \
			fprintf(stderr, "Error: Could not read %s\n", "" tag); \
			return -1; \
		} \
		\
		offs += sizeof(tmp8); \
		\
		indent_printf("" tag ": " fmt "\n", tmp8); \
	} \
	while(0)

/* read (and dump) 16-bit field from current position */
#define read_16_fmt(var, fmt) \
	do \
	{ \
		if(0>(offs=orcad_read_field_16(rctx, offs, (uint16_t*)&(var)))) \
		{ \
			fprintf(stderr, "Error: Could not read %s\n", "" #var); \
			return -1; \
		} \
		\
		indent_printf("" #var ": " fmt "\n", var); \
	} \
	while(0)

/* dump 16-bit field from current position */
#define dump_16_fmt(tag, fmt) \
	do \
	{ \
		my_uint16_t tmp16; \
		\
		if(0>(offs=orcad_read_field_16(rctx, offs, &tmp16))) \
		{ \
			fprintf(stderr, "Error: Could not read %s\n", "" tag); \
			return -1; \
		} \
		\
		indent_printf("" tag ": " fmt "\n", tmp16); \
	} \
	while(0)

/* read (and dump) 32-bit field from current position */
#define read_32_fmt(var, fmt) \
	do \
	{ \
		if(0>(offs=orcad_read_field_32(rctx, offs, (uint32_t*)&(var)))) \
		{ \
			fprintf(stderr, "Error: Could not read %s\n", "" #var); \
			return -1; \
		} \
		\
		indent_printf("" #var ": " fmt "\n", var); \
	} \
	while(0)

/* dump 32-bit field from current position */
#define dump_32_fmt(tag, fmt) \
	do \
	{ \
		my_uint32_t tmp32; \
		\
		if(0>(offs=orcad_read_field_32(rctx, offs, &tmp32))) \
		{ \
			fprintf(stderr, "Error: Could not read %s\n", "" tag); \
			return -1; \
		} \
		\
		indent_printf("" tag ": " fmt "\n", tmp32); \
	} \
	while(0)

#define dump_hex8(tag) dump_8_fmt(tag, "0x%02x")
#define dump_uint8(tag) dump_8_fmt(tag, "%u")
#define dump_hex16(tag) dump_16_fmt(tag, "0x%04x")
#define dump_uint16(tag) dump_16_fmt(tag, "%u")
#define dump_hex32(tag) dump_32_fmt(tag, "0x%08x")
#define dump_uint32(tag) dump_32_fmt(tag, "%u")
#define read_uint8(var) read_8_fmt(var, "%u")
#define read_int16(var) read_16_fmt(var, "%i")
#define read_uint16(var) read_16_fmt(var, "%u")
#define read_int32(var) read_32_fmt(var, "%i")
#define read_uint32(var) read_32_fmt(var, "%u")
/* TODO: ^^^^ THESE ^^^^ */

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_16(num_ ## child_name ## s); \
		\
		if(0>(offs=orcad_read_nodes__(rctx, offs, parent, \
			(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)

/* TODO: REMOVE */
#define orcad_skip_objects(_fp_, _offs_, _count_) \
	orcad_process_objects(_fp_, _offs_, 0, _count_, orcad_skip_object)

/* TODO: REMOVE */
long orcad_process_objects(io_orcad_rctx_t* const rctx, long offs, int indent,
	int count, long (* const process_object)(io_orcad_rctx_t* const rctx,
	long offs, int ind));

/* TODO: a single function to skip objects: orcad_skip_object() */
long orcad_skip_object(io_orcad_rctx_t* const rctx, long offs, int indent);

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_skip_magic(io_orcad_rctx_t* const rctx, long offs);
