#ifdef ORCAD_TESTER
#	include "tester/read_fio.h"
#else
#	include "read_fio.h"
#	include "read_high.h"
#endif

#include "read_common.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

long orcad_read_page_settings(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_page_node* const node)
{
	read_32(settings.ctime);
	read_32(settings.mtime);
	read_32(settings.unknown_0);
	read_32(settings.unknown_1);
	read_32(settings.unknown_2);
	read_32(settings.unknown_3);
	read_32(settings.width);
	read_32(settings.height);
	read_32(settings.pin_to_pin);
	read_16(settings.unknown_4);
	read_16(settings.horiz_count);
	read_16(settings.vert_count);
	read_16(settings.unknown_5);
	read_32(settings.horiz_width);
	read_32(settings.vert_width);
	read_32(settings.unknown_6);
	read_32(settings.unknown_7);
	read_32(settings.unknown_8);
	read_32(settings.unknown_9);
	read_32(settings.unknown_10);
	read_32(settings.unknown_11);
	read_32(settings.unknown_12);
	read_32(settings.unknown_13);
	read_32(settings.unknown_14);
	read_32(settings.unknown_15);
	read_32(settings.unknown_16);
	read_32(settings.unknown_17);
	read_32(settings.horiz_char);
	read_32(settings.unknown_18);
	read_32(settings.horiz_ascending);
	read_32(settings.vert_char);
	read_32(settings.unknown_19);
	read_32(settings.vert_ascending);
	read_32(settings.is_metric);
	read_32(settings.border_displayed);
	read_32(settings.border_printed);
	read_32(settings.gridref_displayed);
	read_32(settings.gridref_printed);
	read_32(settings.titleblock_displayed);
	read_32(settings.titleblock_printed);
	read_32(settings.ansi_grid_refs);

	return offs;
}

long orcad_dump_titleblocks(io_orcad_rctx_t* const rctx, long offs,
	int indent, int count, char* buf, const size_t bufsz)
{
	/* for now we just skip the title blocks, they have only a little */
	/* added value, while title block is a quite complex structures */

	return orcad_skip_objects(rctx, offs, count);
}

long orcad_read_netalias(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_netalias** const out)
{
	struct orcad_netalias* const alias = (struct orcad_netalias*)malloc(
		sizeof(struct orcad_netalias));

	if(NULL==alias)
	{
		return -1;
	}

	memset(alias, 0, sizeof(*alias));

	if(0>(offs=orcad_read_string2(rctx, offs, alias->alias,
		sizeof(alias->alias))))
	{
		fprintf(stderr, "Error: Could not read net name\n");
		free(alias);
		return -1;
	}

	if(0>(offs=orcad_read_field_32(rctx, offs, &alias->net_id)))
	{
		fprintf(stderr, "Error: Could not read net_id\n");
		free(alias);
		return -1;
	}

	*out = alias;

	return offs;
}

long orcad_read_wire(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node)
{
	/*
		14 3d 00 00 00 00 00 00 00          header_1
		14 0b 00 00 00 00 00 00 00          header_2
		14 00 00 ff e4 5c 39 00 00 00 00    header_3
		11 00 00 00   unk[0]
		45 00 00 00   net_id
		30 00 00 00   color
		5a 00 00 00   start_x
		32 00 00 00   start_y
		82 00 00 00   end_x
		32 00 00 00   end_y
		00            unk[1]
		00 00         num of Alias
		00 00         num of SymbolDisplayProps
		03 00 00 00   linewidth
		05 00 00 00   linestyle
	*/

	orcad_create_node(struct orcad_wire_node, ORCAD_TYPE_WIRE);

	read_32(wire_id);
	read_32(net_id);
	read_32(color);
	read_32(start_x);
	read_32(start_y);
	read_32(end_x);
	read_32(end_y);
	read_8(unknown_0);

	read_16(num_alias);

	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_alias)))
	{
		fprintf(stderr, "Error: Could not skip alias objects\n");
		return -1;
	}

	read_16(num_displayprops);

	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_displayprops)))
	{
		fprintf(stderr, "Error: Could not skip displayprops\n");
		return -1;
	}

	read_32(line_width);
	read_32(line_style);

	return offs;
}

long orcad_read_netprop(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node)
{
	/*
		34 17 00 00 00 00 00 00 00
		45 00 00 00           net_id
		00 00 00 00 00 00 00  unk[0..6]
		30 00 00 00           color
		05 00 00 00           linewidth
		03 00 00 00           linestyle
	*/

	orcad_create_node(struct orcad_netprop_node, ORCAD_TYPE_NETPROP);

	read_32(net_id);
	read_8(unknown[0]);
	read_8(unknown[1]);
	read_8(unknown[2]);
	read_8(unknown[3]);
	read_8(unknown[4]);
	read_8(unknown[5]);
	read_8(unknown[6]);
	read_32(color);
	read_32(line_width);
	read_32(line_style);

	return offs;
}

static long orcad_read_graphicinst_inline(io_orcad_rctx_t* const rctx,
	long offs, struct orcad_graphicinst_node* const node,
	const my_uint32_t size)
{
	my_uint8_t type;

	read_32(graphic.unknown_0);
	read_32(graphic.unknown_1);

	if(0>(offs=orcad_read_string2(rctx, offs, node->graphic.name,
		sizeof(node->graphic.name))))
	{
		fprintf(stderr, "Error: Could not read name\n");
		return -1;
	}

	read_32(graphic.db_id);
	read_16(graphic.y);
	read_16(graphic.x);
	read_16(graphic.y2);
	read_16(graphic.x2);
	read_16(graphic.x1);
	read_16(graphic.y1);
	read_8(graphic.color);
	read_8(graphic.rotation);
	read_8(graphic.unknown_2);
	read_8(graphic.unknown_3);

	read_16(graphic.num_displayprops);
	if(0>(offs=orcad_skip_objects(rctx, offs, node->graphic.num_displayprops)))
	{
		fprintf(stderr, "Error: Could not skip displayprops\n");
		return -1;
	}

	if(sizeof(type)!=fio_fread(rctx, (char*)&type, sizeof(type)))
	{
		fprintf(stderr, "Error: Could not read type field\n");
		return -1;
	}

	offs += sizeof(type);

	switch(type)
	{
	case ORCAD_TYPE_INLINEPAGEOBJECT:
		if(0>(offs=orcad_read_inlinepageobject(rctx, offs, &node->node,
			(struct orcad_node**)&node->graphic.obj)))
		{
			return -1;
		}
		break;

	case ORCAD_TYPE_GLOBALSYMBOL:
	case ORCAD_TYPE_OFFPAGECONNSYMBOL:
	case ORCAD_TYPE_PORTSYMBOL:
		break;

	default:
		fprintf(stderr, "Error: Unexpected graphic object type: 0x%x\n",
			(unsigned int)type);
		return -1;
	}

	node->graphic.type = type;

	return offs;
}

long orcad_read_global(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node)
{
	/*
		25 39 00 00 00 00 00 00 00        header-1
		25 0b 00 00 00 00 00 00 00        header-2
		25 00 00 ff e4 5c 39 00 00 00 00  header-3
		19 00 00 00          unk-0
		0e 00 00 00          unk-1
		03 00 47 4e 44 00    name
		9c 00 00 00          db_id
		46 00                y
		50 00                x
		50 00                y2
		64 00                x2
		50 00                x1
		46 00                y1
		30                   color
		00                   rotation
		25                   unk-2 (seems to be always 0x25)
		2d                   unk-3
		00 00                num_displayprops
		21                   unk-4 (seems to be always 0x21)

		extra 5 bytes, after the object
		a2 00 00 00          wire_id
		00                   x-unk-0
	*/

	orcad_create_node(struct orcad_global_node, ORCAD_TYPE_GLOBAL);

	if(0>(offs=orcad_read_graphicinst_inline(rctx, offs,
		(struct orcad_graphicinst_node*)node, node->node.size-5)))
	{
		return -1;
	}

	/* this is a nasty object, it has 5 extra bytes after the object */
	/* (why it is not part of the object then?) */

	read_32(wire_id);
	read_8(unknown_0);

	return offs;
}

long orcad_read_offpageconn(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node)
{
	orcad_create_node(struct orcad_offpageconn_node, ORCAD_TYPE_OFFPAGECONN);

	if(0>(offs=orcad_read_graphicinst_inline(rctx, offs,
		(struct orcad_graphicinst_node*)node, node->node.size-5)))
	{
		return -1;
	}

	/* this is a nasty object, it has 5 extra bytes after the object */
	/* (why it is not part of the object then?) */

	read_32(wire_id);
	read_8(unknown_0);

	return offs;
}

long orcad_read_graphicinst(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node)
{
	struct orcad_header hdr;

	if(0>(offs=orcad_parse_header(rctx, offs, &hdr)))
	{
		fprintf(stderr, "Error: Could not read offpageconn header\n");
		return -1;
	}

	switch(hdr.type)
	{
	case ORCAD_TYPE_GRAPHICBOXINST:
	case ORCAD_TYPE_GRAPHICLINEINST:
	case ORCAD_TYPE_GRAPHICARCINST:
	case ORCAD_TYPE_GRAPHICELLIPSEINST:
	case ORCAD_TYPE_GRAPHICPOLYGONINST:
	case ORCAD_TYPE_GRAPHICTEXTINST:
	case ORCAD_TYPE_GRAPHICBEZIERINST:
		break;

	default:
		fprintf(stderr, "Error: Unhandled graphic instance type: 0x%x\n",
			(unsigned int)hdr.type);
		return -1;
	}

	{
		orcad_create_node_from(struct orcad_graphicinst_node, hdr.type, &hdr);

		if(0>(offs=orcad_read_graphicinst_inline(rctx, offs,
			(struct orcad_graphicinst_node*)node, node->node.size)))
		{
			return -1;
		}
	}

	return offs;
}

long orcad_read_T0x10(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node)
{
	my_uint16_t idx;

	orcad_create_node(struct orcad_T0x10_node, ORCAD_TYPE_0x10);

	if(0>(offs=orcad_read_field_16(rctx, offs, (my_uint16_t*)&idx)))
	{
		fprintf(stderr, "Error: Could not read pin_idx field\n");
		return -1;
	}

	if(0<=idx)
	{
		node->nc  = 0;
		node->idx = idx;
	}
	else
	{
		node->nc  = 1;
		node->idx = -idx;
	}

	read_16(x);
	read_16(y);
	read_32(wire_id);
	read_32(unknown_0);

	read_16(num_displayprops);
	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_displayprops)))
	{
		fprintf(stderr, "Error: Could not read displayprops objects\n");
		return -1;
	}

	return offs;
}

long orcad_read_partinst(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node)
{
	orcad_create_node(struct orcad_partinst_node, ORCAD_TYPE_PARTINST);

	read_32(unknown_0);
	read_32(unknown_1);

	if(0>(offs=orcad_read_string2(rctx, offs, node->name,
		sizeof(node->name))))
	{
		fprintf(stderr, "Error: Could not read name\n");
		return -1;
	}

	read_32(db_id);
	read_32(unknown_2);
	read_32(unknown_3);
	read_16(x);
	read_16(y);
	read_32(unknown_4);

	read_16(num_displayprops);
	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_displayprops)))
	{
		fprintf(stderr, "Error: Could not read displayprops objects\n");
		return -1;
	}

	read_8(unknown_5);

	if(0>(offs=orcad_read_string2(rctx, offs, node->refdes,
		sizeof(node->refdes))))
	{
		fprintf(stderr, "Error: Could not read refdes\n");
		return -1;
	}

	read_32(unknown_6);
	read_32(unknown_7);
	read_32(unknown_8);
	read_16(unknown_9);

	read_node_array(T0x10, orcad_read_T0x10);

	if(0>(offs=orcad_read_string2(rctx, offs, node->symname,
		sizeof(node->symname))))
	{
		fprintf(stderr, "Error: Could not read symname\n");
		return -1;
	}

	read_16(unknown_11);

	return offs;
}

long orcad_read_port(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node* const parent, struct orcad_node** const out_node)
{
	orcad_create_node(struct orcad_port_node, ORCAD_TYPE_PORT);

	if(0>(offs=orcad_read_graphicinst_inline(rctx, offs,
		(struct orcad_graphicinst_node*)node, node->node.size-9)))
	{
		return -1;
	}

	read_32(wire_id);
	read_8(unknown_0);
	read_32(unknown_1);

	return offs;
}

static int orcad_cmp_netalias(const void* p_lhs, const void* p_rhs)
{
	const struct orcad_netalias* const lhs =
		*(const struct orcad_netalias**)p_lhs;

	const struct orcad_netalias* const rhs =
		*(const struct orcad_netalias**)p_rhs;

	return strcmp(lhs->alias, rhs->alias);
}

long orcad_read_page(io_orcad_rctx_t* const rctx, long offs,
	struct orcad_node** const out_node, const struct orcad_header* const p_hdr)
{
	my_uint16_t i;

	struct orcad_node* const parent = NULL;

	orcad_create_node_from(struct orcad_page_node, ORCAD_TYPE_PAGE, p_hdr);

	/* ---- page name and size */

	if(0>(offs=orcad_read_string2(rctx, offs, node->page_name,
		sizeof(node->page_name))))
	{
		fprintf(stderr, "Error: Could not read page name\n");
		return -1;
	}

	if(0>(offs=orcad_read_string2(rctx, offs, node->page_size,
		sizeof(node->page_size))))
	{
		fprintf(stderr, "Error: Could not read page size\n");
		return -1;
	}

	/* ---- page settings */

	if(0>(offs=orcad_read_page_settings(rctx, offs, node)))
	{
		fprintf(stderr, "Error: Could not read page settings\n");
		return -1;
	}

	/* ---- objects */

	/* title blocks */

	read_16(num_titleblocks);
	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_titleblocks)))
	{
		fprintf(stderr, "Error: Could not skip titleblocks\n");
		return -1;
	}

	/* net properties */

	read_node_array(netprop, orcad_read_netprop);

	/* unknown T0x35 objects */

	read_16(num_T0x35s);
	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_T0x35s)))
	{
		fprintf(stderr, "Error: Could not skip T0x35 objects\n");
		return -1;
	}

	/* netalias-ID pairs */

	read_16(num_netaliases);
	if(NULL==(node->netaliases=(struct orcad_netalias**)malloc(
		sizeof(struct orcad_netalias*)*node->num_netaliases)))
	{
		fprintf(stderr, "Error: Could not allocate memory for netaliases\n");
		return -1;
	}
	for(i=0;i<node->num_netaliases;++i)
	{
		if(0>(offs=orcad_read_netalias(rctx, offs, &node->netaliases[i])))
		{
			return -1;
		}
	}
	/* netaliases are placed into the file in random order, and the order */
	/* can change from save to save, so better to sort them */
	qsort(node->netaliases, node->num_netaliases, sizeof(node->netaliases[0]),
		orcad_cmp_netalias);

	/* wires */

	read_node_array(wire, orcad_read_wire);

	/* part instances */

	read_node_array(partinst, orcad_read_partinst);

	/* ports */

	read_node_array(port, orcad_read_port);

	/* globals */

	read_node_array(global, orcad_read_global);

	/* off-page connectors */

	read_node_array(offpageconn, orcad_read_offpageconn);

	/* ERC symbols instances */

	read_16(num_ercsymbolinsts);
	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_ercsymbolinsts)))
	{
		fprintf(stderr, "Error: Could not read ercsymbolinsts\n");
		return -1;
	}

	/* busentries */

	read_16(num_busentries);
	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_busentries)))
	{
		fprintf(stderr, "Error: Could not read busentries\n");
		return -1;
	}

	/* graphic instances */

	read_node_array(graphicinst, orcad_read_graphicinst);

	/* unknown #10 */

	read_16(num_unk10);
	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_unk10)))
	{
		fprintf(stderr, "Error: Could not skip unk10 objects\n");
		return -1;
	}

	/* unknown #11 */

	read_16(num_unk11);
	if(0>(offs=orcad_skip_objects(rctx, offs, node->num_unk11)))
	{
		fprintf(stderr, "Error: Could not skip unk11 objects\n");
		return -1;
	}

	return offs;
}

struct orcad_node* orcad_read(io_orcad_rctx_t* const rctx)
{
	struct orcad_node* res;
	struct orcad_header hdr;
	long offs = orcad_parse_header(rctx, 0, &hdr);

	if(0>offs)
	{
		fprintf(stderr, "Error: Could not parse the initial header of '%s'\n",
			rctx->fn);
		return NULL;
	}

	res = NULL;

	switch(hdr.type)
	{
	case ORCAD_TYPE_PAGE:
		offs = orcad_read_page(rctx, offs, &res, &hdr);
		break;

	default:
		fprintf(stderr, "Error: '%s' has an unknown root header type: 0x%x\n",
			rctx->fn, (unsigned int)hdr.type);
		return NULL;
	}

	if(0>offs)
	{
		fprintf(stderr, "Error: Reading '%s' failed\n", rctx->fn);

		if(NULL!=res)
		{
			orcad_free(res);
		}

		return NULL;
	}

	{
		char c;

		if(0<fio_fread(rctx, &c, 1))
		{
			fprintf(stderr, "Error: File was not interpreted correctly!\n");
			fprintf(stderr, "Ending offs: %li (0x%lx)\n", offs, offs);

			if(NULL!=res)
			{
				orcad_free(res);
			}

			return NULL;
		}
	}

	return res;
}
