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

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

/* TODO: remove when the indent_printf() is removed from read_common.h */
#undef indent_printf

#define indent_printf \
	indent_impl(stdout, indent), printf

#define indent_u(field) \
	indent_printf("%s: %u\n", #field, node->field)

#define indent_i(field) \
	indent_printf("%s: %i\n", #field, node->field)

#define indent_x(field) \
	indent_printf("%s: 0x%x\n", #field, node->field)

#define indent_s(field) \
	indent_printf("%s: \"%s\"\n", #field, node->field)

#define dump_node_array(tag) \
	do \
	{ \
		indent_u(num_##tag##s); \
		orcad_dump_node_array((struct orcad_node**)node->tag##s, \
			node->num_##tag##s, indent); \
	} \
	while(0)

static void orcad_dump_node(struct orcad_node* const node, int indent);
static void orcad_dump_prim(struct orcad_prim* const prim, int indent);

static void orcad_dump_node_array(struct orcad_node** array, size_t count,
	int indent)
{
	while(0<(count--))
	{
		orcad_dump_node(*array++, indent);
	}
}

static void orcad_dump_text_prim(struct orcad_text_prim* const node,
	int indent)
{
	indent_printf("begin text\n");
	++indent;

	indent_i(x);
	indent_i(y);
	indent_i(x1);
	indent_i(y1);
	indent_i(x2);
	indent_i(y2);
	indent_u(font_id);
	indent_u(unknown_0);
	indent_s(text);

	--indent;
	indent_printf("end text\n");
}

static void orcad_dump_line_prim(struct orcad_line_prim* const node,
	int indent)
{
	indent_printf("begin line\n");
	++indent;

	indent_i(x1);
	indent_i(y1);
	indent_i(x2);
	indent_i(y2);

	if(node->have_line_style)
	{
		indent_u(line_style);
		indent_u(line_width);
	}

	--indent;
	indent_printf("end line\n");
}

static void orcad_dump_rect_prim(struct orcad_rect_prim* const node,
	int indent)
{
	indent_printf("begin rect\n");
	++indent;

	indent_i(x1);
	indent_i(y1);
	indent_i(x2);
	indent_i(y2);

	if(node->have_line_style)
	{
		indent_u(line_style);
		indent_u(line_width);
	}

	if(node->have_fill_style)
	{
		indent_u(fill_style);
		indent_u(hatch_style);
	}

	--indent;
	indent_printf("end rect\n");
}

static void orcad_dump_ellipse_prim(struct orcad_ellipse_prim* const node,
	int indent)
{
	indent_printf("begin ellipse\n");
	++indent;

	indent_i(x1);
	indent_i(y1);
	indent_i(x2);
	indent_i(y2);

	if(node->have_line_style)
	{
		indent_u(line_style);
		indent_u(line_width);
	}

	if(node->have_fill_style)
	{
		indent_u(fill_style);
		indent_u(hatch_style);
	}

	--indent;
	indent_printf("end ellipse\n");
}

static void orcad_dump_arc_prim(struct orcad_arc_prim* const node, int indent)
{
	indent_printf("begin arc\n");
	++indent;

	indent_i(x1);
	indent_i(y1);
	indent_i(x2);
	indent_i(y2);
	indent_i(start_x);
	indent_i(start_y);
	indent_i(end_x);
	indent_i(end_y);

	if(node->have_line_style)
	{
		indent_u(line_style);
		indent_u(line_width);
	}

	--indent;
	indent_printf("end arc\n");
}

static void orcad_dump_polygon_prim(struct orcad_polygon_prim* const node,
	int indent)
{
	my_uint32_t i;

	indent_printf("begin polygon\n");
	++indent;

	if(node->have_line_style)
	{
		indent_u(line_style);
		indent_u(line_width);
	}

	if(node->have_fill_style)
	{
		indent_u(fill_style);
		indent_u(hatch_style);
	}

	indent_u(num_points);

	for(i=0;i<node->num_points;++i)
	{
		indent_printf("point[%u]: {%u, %u}\n", i, node->points[i].x,
			node->points[i].y);
	}

	--indent;
	indent_printf("end polygon\n");
}

static void orcad_dump_bezier_prim(struct orcad_bezier_prim* const node,
	int indent)
{
	my_uint32_t i;

	indent_printf("begin bezier\n");
	++indent;

	if(node->have_line_style)
	{
		indent_u(line_style);
		indent_u(line_width);
	}

	indent_u(num_segments);

	for(i=0;i<node->num_segments;++i)
	{
		struct orcad_bsegment* const seg = &node->segments[i];

		indent_printf("segment[%u]: {%u, %u}, {%u, %u}, {%u, %u}, {%u, %u}\n",
			i,
			seg->p1.x, seg->p1.y,
			seg->p2.x, seg->p2.y,
			seg->p3.x, seg->p3.y,
			seg->p4.x, seg->p4.y);
	}

	--indent;
	indent_printf("end bezier\n");
}

static void orcad_dump_prim(struct orcad_prim* const prim, int indent)
{
	switch(prim->type)
	{
	case ORCAD_PRIMITIVE_RECT:
		orcad_dump_rect_prim((struct orcad_rect_prim*)prim, indent);
		break;

	case ORCAD_PRIMITIVE_LINE:
		orcad_dump_line_prim((struct orcad_line_prim*)prim, indent);
		break;

	case ORCAD_PRIMITIVE_ARC:
		orcad_dump_arc_prim((struct orcad_arc_prim*)prim, indent);
		break;

	case ORCAD_PRIMITIVE_ELLIPSE:
		orcad_dump_ellipse_prim((struct orcad_ellipse_prim*)prim, indent);
		break;

	case ORCAD_PRIMITIVE_POLYGON:
		orcad_dump_polygon_prim((struct orcad_polygon_prim*)prim, indent);
		break;

	/*
	case ORCAD_PRIMITIVE_POLYLINE:
	*/

	case ORCAD_PRIMITIVE_TEXT:
		orcad_dump_text_prim((struct orcad_text_prim*)prim, indent);
		break;

	case ORCAD_PRIMITIVE_BEZIER:
		orcad_dump_bezier_prim((struct orcad_bezier_prim*)prim, indent);
		break;

	default:
		fprintf(stderr, "Error: Primitive 0x%x is not handled!\n",
			(unsigned int)prim->type);
		break;
	}
}

static void orcad_dump_netalias(struct orcad_netalias* const alias, int indent)
{
	indent_printf("begin netalias\n");
	++indent;

	indent_printf("alias: \"%s\"\n", alias->alias);
	indent_printf("net_id: %u\n", alias->net_id);

	--indent;
	indent_printf("end netalias\n");
}

static void orcad_dump_netprop(struct orcad_netprop_node* const node,
	int indent)
{
	indent_printf("begin netprop\n");
	++indent;

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

	--indent;
	indent_printf("end netprop\n");
}

static void orcad_dump_wire(struct orcad_wire_node* const node, int indent)
{
	indent_printf("begin wire\n");
	++indent;

	indent_u(wire_id);
	indent_u(net_id);
	indent_u(color);
	indent_u(start_x);
	indent_u(start_y);
	indent_u(end_x);
	indent_u(end_y);
	indent_x(unknown_0);

	indent_u(num_alias);
	indent_u(num_displayprops);

	indent_u(line_width);
	indent_u(line_style);

	--indent;
	indent_printf("end wire\n");
}

static void orcad_dump_T0x10(struct orcad_T0x10_node* const node, int indent)
{
	indent_printf("begin T0x10\n");
	++indent;

	indent_i(nc);
	indent_i(idx);
	indent_u(x);
	indent_u(y);
	indent_u(wire_id);
	indent_x(unknown_0);

	--indent;
	indent_printf("end T0x10\n");
}

static void orcad_dump_symbolpin(struct orcad_symbolpin_node* const node,
	int indent)
{
	indent_printf("begin symbolpin\n");
	++indent;

	indent_s(pin_name);
	indent_i(start_x);
	indent_i(start_y);
	indent_i(hotpt_x);
	indent_i(hotpt_y);
	indent_u(pin_shape);
	indent_x(unknown_0);
	indent_u(port_type);
	indent_x(unknown_1);
	indent_x(unknown_2);

	--indent;
	indent_printf("end symbolpin\n");
}

static void orcad_dump_pinidxmapping(
	struct orcad_pinidxmapping_node* const node, int indent)
{
	my_uint16_t i;

	indent_printf("begin pinidxmapping\n");
	++indent;

	indent_s(unit_ref);
	indent_s(symname);
	indent_u(num_pins);

	for(i=0;i<node->num_pins;++i)
	{
		struct orcad_pin* const pin = node->pins[i];

		indent_printf("begin pin[%i]\n", i);
		++indent;

		if(NULL==pin)
		{
			indent_printf("empty/missing\n");
		}
		else
		{
			indent_printf("pin_name: \"%s\"\n", pin->pin_name);
			indent_printf("pin_ignore: %u\n", pin->pin_ignore);
			indent_printf("pin_group: %u\n", pin->pin_group);
		}

		--indent;
		indent_printf("end pin[%i]\n", i);
	}

	--indent;
	indent_printf("end pinidxmapping\n");
}

static void orcad_dump_page(struct orcad_page_node* const node, int indent)
{
	time_t tim;
	my_uint16_t i;

	indent_printf("begin page\n");
	++indent;

	indent_s(page_name);
	indent_s(page_size);

	tim = node->settings.ctime;
	indent_printf("ctime: %s", ctime(&tim));

	tim = node->settings.mtime;
	indent_printf("mtime: %s", ctime(&tim));

	indent_printf("unknown_0: 0x%x\n", node->settings.unknown_0);
	indent_printf("unknown_1: 0x%x\n", node->settings.unknown_1);
	indent_printf("unknown_2: 0x%x\n", node->settings.unknown_2);
	indent_printf("unknown_3: 0x%x\n", node->settings.unknown_3);
	indent_printf("width: %u\n", node->settings.width);
	indent_printf("height: %u\n", node->settings.height);
	indent_printf("pin_to_pin: %u\n", node->settings.pin_to_pin);
	indent_printf("unknown_4: 0x%x\n", node->settings.unknown_4);
	indent_printf("horiz_count: %u\n", node->settings.horiz_count);
	indent_printf("vert_count: %u\n", node->settings.vert_count);
	indent_printf("unknown_5: 0x%x\n", node->settings.unknown_5);
	indent_printf("horiz_width: %u\n", node->settings.horiz_width);
	indent_printf("vert_width: %u\n", node->settings.vert_width);
	indent_printf("unknown_6: 0x%x\n", node->settings.unknown_6);
	indent_printf("unknown_7: 0x%x\n", node->settings.unknown_7);
	indent_printf("unknown_8: 0x%x\n", node->settings.unknown_8);
	indent_printf("unknown_9: 0x%x\n", node->settings.unknown_9);
	indent_printf("unknown_10: 0x%x\n", node->settings.unknown_10);
	indent_printf("unknown_11: 0x%x\n", node->settings.unknown_11);
	indent_printf("unknown_12: 0x%x\n", node->settings.unknown_12);
	indent_printf("unknown_13: 0x%x\n", node->settings.unknown_13);
	indent_printf("unknown_14: 0x%x\n", node->settings.unknown_14);
	indent_printf("unknown_15: 0x%x\n", node->settings.unknown_15);
	indent_printf("unknown_16: 0x%x\n", node->settings.unknown_16);
	indent_printf("unknown_17: 0x%x\n", node->settings.unknown_17);
	indent_printf("horiz_char: %u\n", node->settings.horiz_char);
	indent_printf("unknown_18: 0x%x\n", node->settings.unknown_18);
	indent_printf("horiz_ascending: %u\n", node->settings.horiz_ascending);
	indent_printf("vert_char: %u\n", node->settings.vert_char);
	indent_printf("unknown_19: 0x%x\n", node->settings.unknown_19);
	indent_printf("vert_ascending: %u\n", node->settings.vert_ascending);
	indent_printf("is_metric: %u\n", node->settings.is_metric);
	indent_printf("border_displayed: %u\n", node->settings.border_displayed);
	indent_printf("border_printed: %u\n", node->settings.border_printed);
	indent_printf("gridref_displayed: %u\n", node->settings.gridref_displayed);
	indent_printf("gridref_printed: %u\n", node->settings.gridref_printed);
	indent_printf("titleblock_displayed: %u\n", node->settings.titleblock_displayed);
	indent_printf("titleblock_printed: %u\n", node->settings.titleblock_printed);
	indent_printf("ansi_grid_refs: %u\n", node->settings.ansi_grid_refs);

	indent_u(num_titleblocks);
	dump_node_array(netprop);
	indent_u(num_T0x35s);

	indent_u(num_netaliases);
	for(i=0;i<node->num_netaliases;++i)
	{
		orcad_dump_netalias(node->netaliases[i], indent);
	}

	dump_node_array(wire);
	dump_node_array(partinst);
	dump_node_array(port);
	dump_node_array(global);
	dump_node_array(offpageconn);
	indent_u(num_ercsymbolinsts);
	indent_u(num_busentries);
	dump_node_array(graphicinst);
	indent_u(num_unk10);
	indent_u(num_unk11);

	--indent;
	indent_printf("end page\n");
}

static void orcad_dump__graphic_inline(
	struct orcad_graphic_inline* const node, int indent)
{
	indent_printf("begin graphic\n");
	++indent;

	indent_x(unknown_0);
	indent_x(unknown_1);
	indent_s(name);
	indent_u(db_id);
	indent_u(x);
	indent_u(y);
	indent_u(x1);
	indent_u(y1);
	indent_u(x2);
	indent_u(y2);
	indent_u(color);
	indent_u(rotation);
	indent_x(unknown_2);
	indent_x(unknown_3);

	indent_u(num_displayprops);

	indent_printf("type: %s\n", orcad_type2str(node->type));

	if(NULL!=node->obj)
	{
		orcad_dump_node((struct orcad_node*)node->obj, indent);
	}

	--indent;
	indent_printf("end graphic\n");
}

static void orcad_dump_global(struct orcad_global_node* const node, int indent)
{
	indent_printf("begin global\n");
	++indent;

	orcad_dump__graphic_inline(&node->graphic, indent);

	indent_u(wire_id);
	indent_x(unknown_0);

	--indent;
	indent_printf("end global\n");
}

static void orcad_dump_offpageconn(struct orcad_offpageconn_node* const node,
	int indent)
{
	indent_printf("begin offpageconn\n");
	++indent;

	orcad_dump__graphic_inline(&node->graphic, indent);

	indent_u(wire_id);
	indent_x(unknown_0);

	--indent;
	indent_printf("end offpageconn\n");
}

static void orcad_dump_port(struct orcad_port_node* const node, int indent)
{
	indent_printf("begin port\n");
	++indent;

	orcad_dump__graphic_inline(&node->graphic, indent);

	indent_u(wire_id);
	indent_x(unknown_0);
	indent_x(unknown_1);

	--indent;
	indent_printf("end port\n");
}

static void orcad_dump_partinst(struct orcad_partinst_node* const node,
	int indent)
{
	indent_printf("begin partinst\n");
	++indent;

	indent_x(unknown_0);
	indent_x(unknown_1);
	indent_s(name);
	indent_u(db_id);
	indent_x(unknown_2);
	indent_x(unknown_3);
	indent_u(x);
	indent_u(y);
	indent_x(unknown_4);

	indent_u(num_displayprops);

	indent_x(unknown_5);
	indent_s(refdes);
	indent_x(unknown_6);
	indent_x(unknown_7);
	indent_x(unknown_8);
	indent_x(unknown_9);
	dump_node_array(T0x10);
	indent_s(symname);
	indent_x(unknown_11);

	--indent;
	indent_printf("end partinst\n");
}

static void orcad_dump_graphicinst(struct orcad_graphicinst_node* const node,
	int indent)
{
	indent_printf("begin graphicinst\n");
	++indent;

	orcad_dump__graphic_inline(&node->graphic, indent);

	--indent;
	indent_printf("end graphicinst\n");
}

static void orcad_dump_inlinepageobject(
	struct orcad_inlinepageobject_node* const node, int indent)
{
	my_uint16_t i;

	indent_printf("begin inlinepageobject\n");
	++indent;

	indent_s(name);
	indent_s(unk_str);
	indent_u(color);
	indent_u(num_primitives);

	for(i=0;i<node->num_primitives;++i)
	{
		orcad_dump_prim(node->primitives[i], indent);
	}

	--indent;
	indent_printf("end inlinepageobject\n");
}

static void orcad_dump_node(struct orcad_node* const node, int indent)
{
	switch(node->type)
	{
	case ORCAD_TYPE_INLINEPAGEOBJECT:
		orcad_dump_inlinepageobject((struct orcad_inlinepageobject_node*)node,
			indent);
		break;

	/*
	case ORCAD_TYPE_PROPERTIES:
	*/

	case ORCAD_TYPE_PAGE:
		orcad_dump_page((struct orcad_page_node*)node, indent);
		break;

	case ORCAD_TYPE_PARTINST:
		orcad_dump_partinst((struct orcad_partinst_node*)node, indent);
		break;

	case ORCAD_TYPE_0x10:
		orcad_dump_T0x10((struct orcad_T0x10_node*)node, indent);
		break;

	case ORCAD_TYPE_WIRE:
		orcad_dump_wire((struct orcad_wire_node*)node, indent);
		break;

	case ORCAD_TYPE_PORT:
		orcad_dump_port((struct orcad_port_node*)node, indent);
		break;

	/*
	case ORCAD_TYPE_SYMBOLGRAPHIC:
	*/

	case ORCAD_TYPE_SYMBOLPIN:
		orcad_dump_symbolpin((struct orcad_symbolpin_node*)node, indent);
		break;

	/*
	case ORCAD_TYPE_SYMBOLPINMAPPING:
	*/

	case ORCAD_TYPE_PINIDXMAPPING:
		orcad_dump_pinidxmapping((struct orcad_pinidxmapping_node*)node,
			indent);
		break;

	/*
	case ORCAD_TYPE_GLOBALSYMBOL:
	case ORCAD_TYPE_PORTSYMBOL:
	case ORCAD_TYPE_OFFPAGECONNSYMBOL:
	*/

	case ORCAD_TYPE_GLOBAL:
		orcad_dump_global((struct orcad_global_node*)node, indent);
		break;

	case ORCAD_TYPE_OFFPAGECONN:
		orcad_dump_offpageconn((struct orcad_offpageconn_node*)node, indent);
		break;

	/*
	case ORCAD_TYPE_SYMBOLDISPLAYPROP:
	*/

	case ORCAD_TYPE_NETPROP:
		orcad_dump_netprop((struct orcad_netprop_node*)node, indent);
		break;

	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:
		orcad_dump_graphicinst((struct orcad_graphicinst_node*)node, indent);
		break;

	/*
	case ORCAD_TYPE_TITLEBLOCKSYMBOL:
	case ORCAD_TYPE_TITLEBLOCK:
	case ORCAD_TYPE_X_CACHE:
	*/

	default:
		fprintf(stderr, "Error: Type 0x%x (%s) is not handled!\n", node->type,
			orcad_type2str(node->type));
		break;
	}
}

void orcad_dump(struct orcad_node* const root)
{
	orcad_dump_node(root, 0);
}
