/* AFM_parse.c */

#include <stdio.h>
#ifndef AIX
#include <stdlib.h>
#   endif
#include <string.h>
#include "defines.h"
#include "myopen.h"
#include "mymalloc.h"
#include "ps2mf.h"
#include "ps2mfutl.h"
#include "AFMparse.h"
#ifdef MSDOS
#include <float.h>
#endif

#ifdef __STDC__
int AFM_command (char * s)
#else
int AFM_command (s)
char * s;
#endif
{
	char ** p;
	int n;

	for (p = AFM_key_words, n = 0; * p; p ++, n ++)
	{
		if (strequ (s, * p)) return (n);
	}
	return (not_an_AFM_keyword);
}

#ifdef __STDC__
int find_TeX_num (char * p)
#else
int find_TeX_num (p)
char * p;
#endif
{
	AFM_info_tp * ai;

	for (ai = AFM_chars; ai; ai = ai -> next)
	{
		if (strequ (p, ai -> AFM_name)) return (ai -> TeX_num);
	}
	fprintf (stderr, "! No TeX coding found for character %s\n", p);
	error ("! Are you sure the configuration file is OK?\n");
}

kern_tp * new_kern ()
{
	kern_tp * nk;

	nk = (kern_tp *) my_malloc (sizeof (kern_tp));
	nk -> next = NULL;
	nk -> succ = NULL;
	nk -> delta = 0;
	return (nk);
}

AFM_info_tp * new_char ()
{
	AFM_info_tp * ai;

	ai = (AFM_info_tp *) my_malloc (sizeof (AFM_info_tp));
	ai -> AFM_num = -1;
	ai -> TeX_num = -1;
	ai -> width = -1;
	ai -> AFM_name = NULL;
	ai -> bbox_llx = -1;
	ai -> bbox_lly = -1;
	ai -> bbox_urx = -1;
	ai -> bbox_ury = -1;
	ai -> ligs = NULL;
	ai -> kerns = NULL;
	ai -> pccs = NULL;
	ai -> next = AFM_chars;
	ai -> in_MF_file = FALSE;
	AFM_chars = ai;
	return (ai);
}

lig_tp * new_lig ()
{
	lig_tp * nl;

	nl = (lig_tp *) my_malloc (sizeof (lig_tp));
	nl -> next = NULL;
	nl -> succ = NULL;
	nl -> sub = NULL;
	return (nl);
}

void process_char ()
{
	AFM_info_tp * ai;
	configuration_info_tp * ci;
	lig_tp * nl, * cilig;

	ai = new_char ();
	ai -> AFM_num = param_num ();
	expect (";");
	expect ("WX");
	ai -> width = transform (param_num (), 0);
	expect (";");
	expect ("N");
	ai -> AFM_name = param_new_string ();
	expect (";");
	expect ("B");
	ai -> bbox_llx = param_num ();
	ai -> bbox_lly = param_num ();
	ai -> bbox_urx = param_num ();
	ai -> bbox_ury = param_num ();
	ai -> bbox_llx = transform (ai -> bbox_llx, ai -> bbox_lly);
	ai -> bbox_urx = transform (ai -> bbox_urx, ai -> bbox_ury);
	if (ai -> bbox_lly > 0) ai -> bbox_lly = 0;
	if (ai -> bbox_ury < 0) ai -> bbox_ury = 0;
	if (strequ (ai -> AFM_name, "space")) font_space = ai -> width;
	if (strequ (ai -> AFM_name, "M")) font_quad = ai -> width;
	if (strequ (ai -> AFM_name, "x") && ! x_height_is_defined)
	{
		x_height = ai -> bbox_ury;
	}
	expect (";");
	while (* param == 'L')
	{
		expect ("L");
		nl = new_lig ();
		nl -> succ = param_new_string ();
		nl -> sub = param_new_string ();
		nl -> next = ai -> ligs;
		ai -> ligs = nl;
		expect (";");
	}
	for (ci = TeX_configuration; ci; ci = ci -> next)
	{
		if (strequ (ai -> AFM_name, ci -> AFM_name))
		{
			for (cilig = ci -> ligs; cilig; cilig = cilig -> next)
			{
				nl = new_lig ();
				nl -> succ = cilig -> succ;
				nl -> sub = cilig -> sub;
				nl -> next = ai -> ligs;
				ai -> ligs = nl;
			}
		}
	}
}

void process_kern ()
{
	AFM_info_tp * ai;
	char * p;
	kern_tp * nk;

	p = param_string ();
	if (strequ (p, "space")) return;
	ai = find_AFM_info_for (p);
	if (ai == NULL) error ("! kern char not found");
	if (ai -> AFM_num < '0' || ai -> AFM_num > '9')
	{
		nk = new_kern ();
		nk -> succ = param_new_string ();
		if (strequ (nk -> succ, "space")) return;
		nk -> delta = transform (param_num (), 0);
		nk -> next = ai -> kerns;
		ai -> kerns = nk;
	}
}

pcc_tp * new_pcc ()
{
	pcc_tp * np;

	np = (pcc_tp *) my_malloc (sizeof (pcc_tp));
	np -> next = NULL;
	np -> part_name = NULL;
	np -> x_offset = 0;
	np -> y_offset = 0;
	return (np);
}

void process_composite_char ()
{
	AFM_info_tp * ai;
	char * p;
	pcc_tp * np;
	int n;
	pcc_tp * npp = NULL;

	p = param_string ();
	ai = find_AFM_info_for (p);
	if (ai == NULL) error ("! composite character name not found");
	n = param_num ();
	expect (";");
	while (n --)
	{
		expect ("PCC"); /* see afm2tfm.c: handleconstruct*/
		np = new_pcc ();
		np -> part_name = param_new_string ();
		if (find_AFM_info_for (np -> part_name) == NULL) return;
		np -> x_offset = param_num ();
		np -> y_offset = param_num ();
		np -> x_offset = transform (np -> x_offset, np -> y_offset);
		if (npp) npp -> next = np;
		else ai -> pccs = np;
		npp = np;
		expect (";");
	}
}

#ifdef __STDC__
void append_liginfo (lig_tp * ail, lig_tp * cil)
#   else
void append_liginfo (ail, cil)
lig_tp * ail, * cil;
#	endif
{
	lig_tp * dummy_cil;

	if (ail == NULL)
	{
		ail = cil;
		return;
	}
	if (cil == NULL) return;
	dummy_cil = cil;
	while (dummy_cil -> next != NULL) dummy_cil = dummy_cil -> next;
	dummy_cil -> next = ail;
	ail = cil;
}

void assign_chars ()
{
	char ** p;
	int i;
	bool stop;
	AFM_info_tp * ai;
	configuration_info_tp * ci;
	int nextfree;

	for (ai = AFM_chars; ai; ai = ai -> next)
	{
		stop = FALSE; ci = TeX_configuration;
		while (! stop && ci)
		{
			if (strequ (ai -> AFM_name, ci -> AFM_name))
			{
				ai -> TeX_num = ci -> TeX_num;
				append_liginfo (ai -> ligs, ci -> ligs);
				stop = TRUE;
			}
			ci = ci -> next;
		}
	}
}

void delete_TeX_configuration ()
{
	configuration_info_tp * ci;

	while (TeX_configuration)
	{
		ci = TeX_configuration -> next;
		free (TeX_configuration);
		TeX_configuration = ci;
	}
}

void parse_AFM ()
{
	strcpy (font_name, "Unknown");
	italic_angle = 0.0;
	fixed_pitch = FALSE;
	x_height = 400;
	font_space = 250;
	font_quad = 800;
	x_height_is_defined = FALSE;
	

	while (get_line (afm_file))
	{
		switch (AFM_command (param_string ()))
		{
			case FontName:
			{
				strcpy (font_name, param_string ());
			}
			break;
			case EncodingScheme:
			{
				/*strcpy (coding_scheme, param_string ());*/
			}
			break;
			case ItalicAngle:
			{
				italic_angle = param_float ();
			}
			break;
			case IsFixedPitch:
			{
				if (* param == 't' || * param == 'T')
				{
					fixed_pitch = TRUE;
				}
				else fixed_pitch = FALSE;
			}
			break;
			case XHeight:
			{
				x_height = param_num ();
				x_height_is_defined = TRUE;
			}
			break;
			case C:
			{
				process_char ();
			}
			break;
			case KPX:
			{
				process_kern ();
			}
			break;
			case CC:
			{
				process_composite_char ();
			}
			break;
			default:
			break;
		}
	}
	assign_chars ();
	delete_TeX_configuration ();
}

#ifdef __STDC__
int config_command (char * s)
#else
int config_command (s)
char * s;
#endif
{
	char ** p;
	int n;

	for (p = config_key_words, n = 0; * p; p ++, n ++)
	{
		if (strequ (s, * p)) return (n);
	}
	return (not_a_config_keyword);
}

configuration_info_tp * new_conf_info ()
{
	configuration_info_tp * nsl;

	nsl = (configuration_info_tp *)
	      my_malloc (sizeof (configuration_info_tp));
	nsl -> TeX_num = 0;
	nsl -> AFM_name = NULL;
	nsl -> ligs = NULL;
	nsl -> next = TeX_configuration;
	TeX_configuration = nsl;
	return (nsl);
}

#ifdef __STDC__
void process_configuration (int base)
#   else
void process_configuration (base)
int base;
#	endif
{
	configuration_info_tp * ci;
	lig_tp * nl;

	ci = new_conf_info ();
	if (base == 8) ci -> TeX_num = param_oct ();
	else if (base == 10) ci -> TeX_num = param_num ();
	else if (base == 16) ci -> TeX_num = param_hex ();
	expect (";");
	expect ("N");
	ci -> AFM_name = param_new_string ();
	expect (";");
	while (* param == 'L')
	{
		expect ("L");
		nl = new_lig ();
		nl -> succ = param_new_string ();
		nl -> sub = param_new_string ();
		nl -> next = ci -> ligs;
		ci -> ligs = nl;
		expect (";");
	}
}

ignore_info_tp * new_ignore_info ()
{
	ignore_info_tp * nii;

	nii = (ignore_info_tp *)
		my_malloc (sizeof (ignore_info_tp));
	nii -> AFM_name = NULL;
	nii -> next = ignore_list;
	ignore_list = nii;
	return (nii);
}

void process_ignore ()
{
	ignore_info_tp * ii;

	ii = new_ignore_info ();
	ii -> AFM_name = param_new_string ();
	expect (";");
}

void process_encoding ()
{
	strcpy (coding_scheme, param_string ());
	while (* param != ';')
	{
		strcat (coding_scheme, " ");
		strcat (coding_scheme, param_string ());
	}
	expect (";");
}

void parse_config ()
{
	strcpy (coding_scheme, "Unspecified");
	while (get_line (conf_file))
	{
		switch (config_command (param_string ()))
		{
			case config_C:
			case config_D:
			{
				process_configuration (10);
			}
			break;
			case config_H:
			{
				process_configuration (16);
			}
			break;
			case config_O:
			{
				process_configuration (8);
			}
			break;
			case config_I:
			{
				process_ignore ();
			}
			break;
			case config_Encoding:
			{
				process_encoding ();
			}
			break;
			default:
			break;
		}
	}
}

#ifdef __STDC__
void process_ligatures (AFM_info_tp * ai)
#   else
void process_ligatures (ai)
AFM_info_tp * ai;
#	endif
{
	AFM_info_tp * ai2, * ai3;

	while (ai -> ligs != NULL)
	{
		if (! has_ligs [ai -> TeX_num])
		{
			ai2 = find_AFM_info_for (ai -> ligs -> succ);
			ai3 = find_AFM_info_for (ai -> ligs -> sub);
			if (ai2 -> in_MF_file && ai3 -> in_MF_file)
			{
				if (first_ligtable_entry)
					fprintf (mf_file, "\nligtable %d",
						 ai -> TeX_num);
				fprintf (mf_file, "%s%d=:%d",
					 first_ligtable_entry ? ":\n" : ",\n",
					 ai2 -> TeX_num, ai3 -> TeX_num);
				has_ligs [ai -> TeX_num] = TRUE;
				first_ligtable_entry = FALSE;
			}
		}
		ai -> ligs = ai -> ligs -> next;
	}
}

#ifdef __STDC__
void process_kerns (AFM_info_tp * ai)
#   else
void process_kerns (ai)
AFM_info_tp * ai;
#	endif
{
	AFM_info_tp * ai2;

	while (ai -> kerns != NULL)
	{
		if (! has_ligs [ai -> TeX_num])
		{
			ai2 = find_AFM_info_for (ai -> kerns -> succ);
			if (ai2 -> in_MF_file)
			{
				if (first_ligtable_entry)
					fprintf (mf_file, "\nligtable %d",
						 ai -> TeX_num);
				fprintf (mf_file, "%s%d kern %dFX#",
					 first_ligtable_entry ? ":\n" : ",\n",
					 ai2 -> TeX_num, ai -> kerns -> delta);
				first_ligtable_entry = FALSE;
			}
		}
		ai -> kerns = ai -> kerns -> next;
	}
}

void process_ligatures_and_kerns ()
{
	AFM_info_tp * ai, * ai2, * ai3;
	int i;
	
	fprintf (stderr, "Generating a ligaturetable\n");
	for (ai = AFM_chars; ai; ai = ai -> next)
	{
		for (i = 0; i < 255; i ++) has_ligs [i] = FALSE;
		first_ligtable_entry = TRUE;
		if (ai -> in_MF_file && (ai -> ligs != NULL
					 || ai -> kerns != NULL))
		{
			if (ai -> ligs != NULL) process_ligatures (ai);
			if (ai -> kerns != NULL) process_kerns (ai);
			if (! first_ligtable_entry) fprintf (mf_file, ";\n");
		}
	}
}

