#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>

#define free_node_array(tag) \
	orcad_free_node_array((struct orcad_node**)node->tag##s, \
		node->num_##tag##s)

static void orcad_free_node(struct orcad_node* const node);
static void orcad_free_prim(struct orcad_prim* const prim);

static void orcad_free_node_array(struct orcad_node** array, size_t count)
{
	while(0<(count--))
	{
		orcad_free_node(array[count]);
	}

	free(array);
}

static void orcad_free_text_prim(struct orcad_text_prim* const node)
{
	free(node->text);
}

static void orcad_free_bitmap_prim(struct orcad_bitmap_prim* const node)
{
	free(node->bmp_data);
}

static void orcad_free_polygon_prim(struct orcad_polygon_prim* const node)
{
	free(node->points);
}

static void orcad_free_polyline_prim(struct orcad_polyline_prim* const node)
{
	free(node->points);
}

static void orcad_free_bezier_prim(struct orcad_bezier_prim* const node)
{
	free(node->segments);
}

static void orcad_free_symbolvector_prim(
	struct orcad_symbolvector_prim* const node)
{
	orcad_uint16_t i;

	if(NULL!=node->primitives)
	{
		for(i=0;i<node->num_primitives;++i)
		{
			orcad_free_prim(node->primitives[i]);
		}

		free(node->primitives);
	}

	free(node->name);
}

static void orcad_free_prim(struct orcad_prim* const prim)
{
	if(NULL==prim)
	{
		return;
	}

	switch(prim->type)
	{
	case ORCAD_PRIMITIVE_RECT:
	case ORCAD_PRIMITIVE_LINE:
	case ORCAD_PRIMITIVE_ARC:
	case ORCAD_PRIMITIVE_ELLIPSE:
		break;

	case ORCAD_PRIMITIVE_POLYGON:
		orcad_free_polygon_prim((struct orcad_polygon_prim*)prim);
		break;

	case ORCAD_PRIMITIVE_POLYLINE:
		orcad_free_polyline_prim((struct orcad_polyline_prim*)prim);
		break;

	case ORCAD_PRIMITIVE_TEXT:
		orcad_free_text_prim((struct orcad_text_prim*)prim);
		break;

	case ORCAD_PRIMITIVE_BITMAP:
		orcad_free_bitmap_prim((struct orcad_bitmap_prim*)prim);
		break;

	case ORCAD_PRIMITIVE_BEZIER:
		orcad_free_bezier_prim((struct orcad_bezier_prim*)prim);
		break;

	case ORCAD_PRIMITIVE_SYMBOLVECTOR:
		orcad_free_symbolvector_prim((struct orcad_symbolvector_prim*)prim);
		break;

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

	free(prim);
}

static void orcad_free_alias(struct orcad_alias_node* const node)
{
	free(node->name);
}

static void orcad_free_xnetalias(struct orcad_xnetalias_node* const node)
{
	free(node->alias);
}

static void orcad_free_symbolpin(struct orcad_symbolpin_node* const node)
{
	free(node->pin_name);
	free_node_array(displayprop);
}

static void orcad_free_pinidxmapping(
	struct orcad_pinidxmapping_node* const node)
{
	orcad_uint16_t i;

	free(node->unit_ref);
	free(node->symname);

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

		if(NULL!=pin)
		{
			free(pin->pin_name);
			free(pin);
		}
	}
	free(node->pins);
}

static void orcad_free_schematic(struct orcad_schematic_node* const node)
{
	orcad_uint16_t i;

	free(node->sch_name);

	for(i=0;i<node->num_pagenames;++i)
	{
		free(node->pagenames[i]);
	}
	free(node->pagenames);

	free(node->unk4s);
	free(node->unk5s);
}

static void orcad_free_page(struct orcad_page_node* const node)
{
	free(node->page_name);
	free(node->page_size);

	free_node_array(netprop);
	free_node_array(busprop);
	orcad_free_node_array((struct orcad_node**)node->netaliases,
		node->num_netaliases);
	free_node_array(wire);
	free_node_array(partinst);
	free_node_array(port);
	free_node_array(global);
	free_node_array(offpageconn);
	free_node_array(graphicinst);
}

static void orcad_free__graphic_inline(struct orcad_graphic_inline* const node)
{
	free(node->name);
	free_node_array(displayprop);

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

static void orcad_free_global(struct orcad_global_node* const node)
{
	orcad_free__graphic_inline(&node->graphic);
}

static void orcad_free_offpageconn(struct orcad_offpageconn_node* const node)
{
	orcad_free__graphic_inline(&node->graphic);
}

static void orcad_free_port(struct orcad_port_node* const node)
{
	orcad_free__graphic_inline(&node->graphic);
}

static void orcad_free_ercsymbolinst(
	struct orcad_ercsymbolinst_node* const node)
{
	orcad_free__graphic_inline(&node->graphic);
	free(node->msg1);
	free(node->msg2);
	free(node->msg3);
}

static void orcad_free_partinst(struct orcad_partinst_node* const node)
{
	free(node->name);
	free(node->refdes);
	free_node_array(pinconnection);
	free_node_array(displayprop);
	free(node->symname);
}

static void orcad_free_wire(struct orcad_wire_node* const node)
{
	free_node_array(aliase);
	free_node_array(displayprop);
}

static void orcad_free_pinconnection(
	struct orcad_pinconnection_node* const node)
{
	free_node_array(displayprop);
}

static void orcad_free_graphicinst(struct orcad_graphicinst_node* const node)
{
	orcad_free__graphic_inline(&node->graphic);
}

static void orcad_free_inlinepageobject(
	struct orcad_inlinepageobject_node* const node)
{
	orcad_uint16_t i;

	free(node->name);
	free(node->unk_str);

	for(i=0;i<node->num_primitives;++i)
	{
		orcad_free_prim(node->primitives[i]);
	}
	free(node->primitives);
}

static void orcad_free_symbol_common(
	const struct orcad_symbol_common* const common)
{
	free(common->name);
	free(common->source);
}

static void orcad_free_titleblocksymbol(
	struct orcad_titleblocksymbol_node* const node)
{
	orcad_uint16_t i;

	orcad_free_symbol_common(&node->sym_common);

	free_node_array(symbolpin);
	free_node_array(displayprop);

	for(i=0;i<node->num_primitives;++i)
	{
		orcad_free_prim(node->primitives[i]);
	}
	free(node->primitives);
}

static void orcad_free_titleblock(struct orcad_titleblock_node* const node)
{
	free(node->name);
	free_node_array(displayprop);
}

static void orcad_free_symbolgraphic(
	struct orcad_symbolgraphic_node* const node)
{
	orcad_uint16_t i;

	orcad_free_symbol_common(&node->sym_common);

	for(i=0;i<node->num_primitives;++i)
	{
		orcad_free_prim(node->primitives[i]);
	}
	free(node->primitives);

	free_node_array(symbolpin);
	free_node_array(displayprop);
	free(node->sym_prefix);
	free(node->unk_str0);
	free(node->unk_str1);
	free(node->unk_str2);
}

static void orcad_free_properties(
	struct orcad_properties_node* const node)
{
	orcad_uint16_t i;

	orcad_free_symbol_common(&node->sym_common);

	for(i=0;i<node->num_partnames;++i)
	{
		free(node->partnames[i]);
	}
	free(node->partnames);
}

static void orcad_free_symbolpinmapping(
	struct orcad_symbolpinmapping_node* const node)
{
	orcad_free_symbol_common(&node->sym_common);

	free(node->sym_prefix);
	free(node->unk_str0);
	free(node->unk_str1);
	free_node_array(pinidxmapping);
}

static void orcad_free_busprop(struct orcad_busprop_node* const node)
{
	free(node->busnetids);
}

static void orcad_free_globalsymbol(
	struct orcad_globalsymbol_node* const node)
{
	orcad_uint16_t i;

	orcad_free_symbol_common(&node->sym_common);

	for(i=0;i<node->num_primitives;++i)
	{
		orcad_free_prim(node->primitives[i]);
	}
	free(node->primitives);

	free_node_array(symbolpin);
	free_node_array(displayprop);
}

static void orcad_free_ercsymbol(struct orcad_ercsymbol_node* const node)
{
	orcad_uint16_t i;

	orcad_free_symbol_common(&node->sym_common);

	for(i=0;i<node->num_primitives;++i)
	{
		orcad_free_prim(node->primitives[i]);
	}
	free(node->primitives);
}

static void orcad_free_xcache(struct orcad_xcache_node* const node)
{
	orcad_free_node((struct orcad_node*)node->titleblocks);
	orcad_free_node((struct orcad_node*)node->symbolgraphics);
	orcad_free_node((struct orcad_node*)node->symbolproperties);
	orcad_free_node((struct orcad_node*)node->symbolpinmappings);
}

static void orcad_free_xsymbolgroup(
	struct orcad_xsymbolgroup_node* const node)
{
	free_node_array(symbol);
}

static void orcad_free_xcachesymbol(
	struct orcad_xcachesymbol_node* const node)
{
	free(node->symname);
	free_node_array(variant);
}

static void orcad_free_xcachesymvariant(
	struct orcad_xcachesymvariant_node* const node)
{
	free(node->lib_path);

	if(NULL!=node->obj)
	{
		orcad_free_node(node->obj);
	}
}

static void orcad_free_xlibrary(struct orcad_xlibrary_node* const node)
{
	size_t i;

	free(node->fonts);
	free(node->unks);

	for(i=0;i<(sizeof(node->partfields)/sizeof(node->partfields[0]));++i)
	{
		free(node->partfields[i]);
	}

	if(NULL!=node->names)
	{
		for(i=0;i<node->num_names;++i)
		{
			free(node->names[i]);
		}

		free(node->names);
	}

	if(NULL!=node->aliases)
	{
		for(i=0;i<node->num_aliases;++i)
		{
			free(node->aliases[i].alias);
			free(node->aliases[i].package);
		}

		free(node->aliases);
	}

	free(node->sch_name);
}

static void orcad_free_node(struct orcad_node* const node)
{
	if(NULL==node)
	{
		return;
	}

	switch(node->type)
	{
	case ORCAD_TYPE_INLINEPAGEOBJECT:
		orcad_free_inlinepageobject((struct orcad_inlinepageobject_node*)node);
		break;

	case ORCAD_TYPE_PROPERTIES:
		orcad_free_properties((struct orcad_properties_node*)node);
		break;

	case ORCAD_TYPE_SCHEMATIC:
		orcad_free_schematic((struct orcad_schematic_node*)node);
		break;

	case ORCAD_TYPE_PAGE:
		orcad_free_page((struct orcad_page_node*)node);
		break;

	case ORCAD_TYPE_PARTINST:
		orcad_free_partinst((struct orcad_partinst_node*)node);
		break;

	case ORCAD_TYPE_PINCONNECTION:
		orcad_free_pinconnection((struct orcad_pinconnection_node*)node);
		break;

	case ORCAD_TYPE_WIRE:
		orcad_free_wire((struct orcad_wire_node*)node);
		break;

	case ORCAD_TYPE_PORT:
		orcad_free_port((struct orcad_port_node*)node);
		break;

	case ORCAD_TYPE_SYMBOLGRAPHIC:
		orcad_free_symbolgraphic((struct orcad_symbolgraphic_node*)node);
		break;

	case ORCAD_TYPE_SYMBOLPIN:
		orcad_free_symbolpin((struct orcad_symbolpin_node*)node);
		break;

	case ORCAD_TYPE_SYMBOLPINMAPPING:
		orcad_free_symbolpinmapping((struct orcad_symbolpinmapping_node*)node);
		break;

	case ORCAD_TYPE_PINIDXMAPPING:
		orcad_free_pinidxmapping((struct orcad_pinidxmapping_node*)node);
		break;

	case ORCAD_TYPE_GLOBALSYMBOL:
	case ORCAD_TYPE_PORTSYMBOL:
	case ORCAD_TYPE_OFFPAGECONNSYMBOL:
		orcad_free_globalsymbol((struct orcad_globalsymbol_node*)node);
		break;

	case ORCAD_TYPE_GLOBAL:
		orcad_free_global((struct orcad_global_node*)node);
		break;

	case ORCAD_TYPE_OFFPAGECONN:
		orcad_free_offpageconn((struct orcad_offpageconn_node*)node);
		break;

	case ORCAD_TYPE_SYMBOLDISPLAYPROP:
	case ORCAD_TYPE_NETPROP:
		/* nothing */
		break;

	case ORCAD_TYPE_BUSPROP:
		orcad_free_busprop((struct orcad_busprop_node*)node);
		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:
	case ORCAD_TYPE_GRAPHICBITMAPINST:
		orcad_free_graphicinst((struct orcad_graphicinst_node*)node);
		break;

	case ORCAD_TYPE_TITLEBLOCKSYMBOL:
		orcad_free_titleblocksymbol((struct orcad_titleblocksymbol_node*)node);
		break;

	case ORCAD_TYPE_TITLEBLOCK:
		orcad_free_titleblock((struct orcad_titleblock_node*)node);
		break;

	case ORCAD_TYPE_ERCSYMBOL:
		orcad_free_ercsymbol((struct orcad_ercsymbol_node*)node);
		break;

	case ORCAD_TYPE_ERCSYMBOLINST:
		orcad_free_ercsymbolinst((struct orcad_ercsymbolinst_node*)node);
		break;

	case ORCAD_TYPE_ALIAS:
		orcad_free_alias((struct orcad_alias_node*)node);
		break;

	case ORCAD_TYPE_X_NETALIAS:
		orcad_free_xnetalias((struct orcad_xnetalias_node*)node);
		break;

	case ORCAD_TYPE_X_CACHE:
		orcad_free_xcache((struct orcad_xcache_node*)node);
		break;

	case ORCAD_TYPE_X_SYMBOLGROUP:
		orcad_free_xsymbolgroup((struct orcad_xsymbolgroup_node*)node);
		break;

	case ORCAD_TYPE_X_CACHESYMBOL:
		orcad_free_xcachesymbol((struct orcad_xcachesymbol_node*)node);
		break;

	case ORCAD_TYPE_X_CACHESYMVARIANT:
		orcad_free_xcachesymvariant((struct orcad_xcachesymvariant_node*)node);
		break;

	case ORCAD_TYPE_X_LIBRARY:
		orcad_free_xlibrary((struct orcad_xlibrary_node*)node);
		break;

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

	free(node->namemappings);
	free(node);
}

void orcad_free_primitive(struct orcad_prim* const prim)
{
	/* forward it to the internal function */
	orcad_free_prim(prim);
}

void orcad_free(struct orcad_node* const root)
{
	orcad_free_node(root);
}
