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

static long orcad_read_xlibraryfont__(struct io_orcad_rctx_s* const rctx,
	long offs, struct orcad_xlibraryfont* const font)
{
	vread_u32(font->height);
	vread_u32(font->width);
	vread_u32(font->escapement);
	vread_u32(font->orientation);
	vread_u32(font->weight);
	vread_u8(font->italic);
	vread_u8(font->underline);
	vread_u8(font->strikeout);
	vread_u8(font->charset);
	vread_u8(font->outprecision);
	vread_u8(font->clipprecision);
	vread_u8(font->quality);
	vread_u8(font->pitchandfamily);

	if(sizeof(font->facename)!=fio_fread(rctx, font->facename,
		sizeof(font->facename)))
	{
		fprintf(stderr, "Error: Could not read font facename\n");
		return -1;
	}

	offs += sizeof(font->facename);

	return offs;
}

static long orcad_read_xlibrary_num_names__(
	struct io_orcad_rctx_s* const rctx, long offs,
	struct orcad_xlibrary_node* const node)
{
	if(3==node->ver_major && 2==node->ver_minor)
	{
		orcad_uint32_t n;
		vread_u32(n);
		node->num_names = n;
	}
	else
	{
		fprintf(stderr, "Error: Could not read 'num_names': Unhandled "
			"version: v%u.%u\n",
			(unsigned int)node->ver_major, (unsigned int)node->ver_minor);
		return -1;
	}

	return offs;
}

static long orcad_read_library__(struct io_orcad_rctx_s* const rctx,
	long offs, struct orcad_node** const out_node)
{
	orcad_uint16_t i;

	struct orcad_node* const parent = NULL;

	orcad_create_xnode(struct orcad_xlibrary_node, ORCAD_TYPE_X_LIBRARY);

	if(sizeof(node->introduction)!=fio_fread(rctx, node->introduction,
		sizeof(node->introduction)))
	{
		fprintf(stderr, "Error: Could not read introduction\n");
		return -1;
	}

	offs += sizeof(node->introduction);

	read_u16(ver_major);
	read_u16(ver_minor);
	read_u32(ctime);
	read_u32(mtime);

	if(0>(offs=orcad_skip_field_32(rctx, offs, 0x00000000)))
	{
		fprintf(stderr, "Error: Unknown 32-bit field is not 0x00000000!\n");
		return -1;
	}

	read_u16(num_fonts);

	if(0==node->num_fonts)
	{
		fprintf(stderr, "Error: Value of 'num_fonts' cannot be zero!\n");
		return -1;
	}

	/* number of fonts stored in the file is off-by-one!!! */
	--node->num_fonts;

	if(NULL==(node->fonts=(struct orcad_xlibraryfont*)calloc(node->num_fonts,
		sizeof(struct orcad_xlibraryfont))))
	{
		fprintf(stderr, "Error: Could not allocate fonts\n");
		return -1;
	}

	for(i=0;i<node->num_fonts;++i)
	{
		if(0>(offs=orcad_read_xlibraryfont__(rctx, offs, &node->fonts[i])))
		{
			fprintf(stderr, "Error: Could not read fonts[%u]\n",
				(unsigned int)i);
			return -1;
		}
	}

	read_u16(num_unks);

	if(NULL==(node->unks=(orcad_uint16_t*)calloc(node->num_unks,
		sizeof(orcad_uint16_t))))
	{
		fprintf(stderr, "Error: Could not allocate 'unks'\n");
		return -1;
	}

	for(i=0;i<node->num_unks;++i)
	{
		if(0>(offs=orcad_read_field_u16(rctx, offs, &node->unks[i])))
		{
			fprintf(stderr, "Error: Could not read unks[%u]\n",
				(unsigned int)i);
			return -1;
		}
	}

	read_u32(unknown_0);
	read_u32(unknown_1);

	for(i=0;i<(sizeof(node->partfields)/sizeof(node->partfields[0]));++i)
	{
		if(0>(offs=orcad_read_string2(rctx, offs, &node->partfields[i])))
		{
			fprintf(stderr, "Error: Could not read partfields[%u]\n",
				(unsigned int)i);
			return -1;
		}
	}

	if(0>(offs=orcad_read_pagesettings(rctx, offs, &node->settings)))
	{
		orcad_error_backtrace__(&node->node, "read 'pagesettings'");
		return -1;
	}

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

	if(NULL==(node->names=(char**)calloc(node->num_names, sizeof(char*))))
	{
		fprintf(stderr, "Error: Could not allocate 'names'\n");
		return -1;
	}

	for(i=0;i<node->num_names;++i)
	{
		if(0>(offs=orcad_read_string2(rctx, offs, &node->names[i])))
		{
			fprintf(stderr, "Error: Could not read names[%u]\n",
				(unsigned int)i);
			return -1;
		}
	}

	read_u16(num_aliases);

	if(NULL==(node->aliases=(struct orcad_xlibraryalias*)calloc(
		node->num_aliases, sizeof(struct orcad_xlibraryalias))))
	{
		fprintf(stderr, "Error: Could not allocate 'aliases'\n");
		return -1;
	}

	for(i=0;i<node->num_aliases;++i)
	{
		if(0>(offs=orcad_read_string2(rctx, offs, &node->aliases[i].alias)))
		{
			fprintf(stderr, "Error: Could not read aliases[%u].alias\n",
				(unsigned int)i);
			return -1;
		}

		if(0>(offs=orcad_read_string2(rctx, offs, &node->aliases[i].package)))
		{
			fprintf(stderr, "Error: Could not read aliases[%u].package\n",
				(unsigned int)i);
			return -1;
		}
	}

	read_u32(unknown_2);
	read_u32(unknown_3);

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

	return offs;
}

struct orcad_node* orcad_read_library(struct io_orcad_rctx_s* const rctx)
{
	struct orcad_node* res = NULL;

	long offs = orcad_read_library__(rctx, 0, &res);

	if(0>offs)
	{
		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;
}
