#!/usr/bin/perl

# tecindo.cgi
#
# Converts a user-supplied (Quenya) string into tengwar.

$|=1;


%charTable = (
	tinco => '1',
	parma => 'q',
	calma => 'a',
	quesse => 'z',
	ando => '2',
	umbar => 'w',
	anga => 's',
	ungwe => 'x',
	thule => '3',
	formen => 'e',
	harma => 'd',
	hwesta => 'c',
	anto => '4',
	ampa => 'r',
	anca => 'f',
	unque => 'v',
	numen => '5',
	malta => 't',
	noldo => 'g',
	nwalme => 'b',
	ore => '6',
	vala => 'y',
	anna => 'h',
	vilya => 'n',
	romen => '7',
	arda => 'u',
	lambe => 'j',
	alda => 'm',
	silme => '8',
	'silme nuquerna' => 'i',
	are => 'k',
	'are nuquerna' => ',',
	hyarmen => '9',
	'hwesta sindarinwa' => 'o',
	yanta => 'l',
	ure => '.',

	halla => "\xBD",

	'long carrier' => '~',
	'short carrier' => '`',

	'a tehta left' => '#',
	'a tehta mid' => 'E',
	'a tehta right' => 'D',
	'a tehta narrow' => 'C',
	'e tehta left' => '$',
	'e tehta mid' => 'R',
	'e tehta right' => 'F',
	'e tehta narrow' => 'V',
	'i tehta left' => '%',
	'i tehta mid' => 'T',
	'i tehta right' => 'G',
	'i tehta narrow' => 'B',
	'o tehta left' => '^',
	'o tehta mid' => 'Y',
	'o tehta right' => 'H',
	'o tehta narrow' => 'N',
	'u tehta left' => '&',
	'u tehta mid' => 'U',
	'u tehta right' => 'J',
	'u tehta narrow' => 'M',

	'A tehta' => '#E',
	'E tehta' => '$F',
	'I tehta' => '%G',
	'O tehta' => '^H',
	'U tehta' => '&J',

	'following y left' => "\xCC",
	'following y mid' => "\xCD",
	'following y right' => "\xCE",
	'following y narrow' => "\xCF",
	'following y inside' => "\xB4",

	'hook wide' => '+',
	'hook narrow' => '_',
	'hook reversed' => '|',

	'doubler wide' => ':',
	'doubler narrow' => ';',
	'doubler wide low' => '?',
	'doubler narrow low' => '/',
	'doubler inside' => "\xB0",
	'doubler narrow above' => 'p',


	comma => '=',
	period => '-- ',
	semicolon => '-',
	hyphen => '=',
	ques => "\xC0".' ',
	bang => "\xC1".' ',
	paren => "\x9B",


);

my @calls;

# Get input, whether by GET or POST.

if ($ENV{REQUEST_METHOD} eq GET)
{
	@pairs = split(/&/, $ENV{QUERY_STRING});
}
elsif ($ENV{REQUEST_METHOD} eq POST)
{
	read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
	@pairs = split(/&/, $buffer);
}
else
{
	print "Script $0 called with no CGI method! Aborting.\n";
	exit 1;
}


foreach (@pairs)
{
	($name, $value) = split(/=/);
	$name =~ tr/+/ /;
	$name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

	$value =~ tr/+/ /;
	$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

	${$name} = $value;
}



if (defined $quettar)
{
	$input = $quettar;
	$input .= ' ';
	$input = lc($input);
#	$english = '' unless ($english eq 'on');
#	$diacritred = '' unless ($diacritred eq 'on');
}
elsif (defined $redo || $english ne '')
{
	foreach (EnglishPunct, RedPunct, RedDiacrit, DblSilme, FinalOre, InitialHalla)
	{
		$$_ = ($$_ eq 'on') ? ' checked' : '';
	}
	foreach (0..2)
	{
		local $var = 'CarrierPref'.$_;
		$$var = ($CarrierPref == $_) ? ' selected' : '';
	}
	$NoteOne = '';


	local $file = ( ($english eq 'on') ? 'tecindo-english.html' : 'tecindo-input.html' );
	HTMLspew($file);
	exit 0;

}
else
{
	$NoteOne = '(A sample phrase is already there for you.)';
	$addQuettar = 'VALUE="&Aacute; tece sinome quettar Quenyainen."';

	foreach (EnglishPunct, RedPunct, RedDiacrit, DblSilme, FinalOre, InitialHalla)
	{
		$$_ = '';
	}
	$CarrierPref0 = '';
	$CarrierPref1 = ' selected';
	$CarrierPref2 = '';

	HTMLspew('tecindo-input.html');
	exit 0;
}


# Make sure it's actually Quenya; otherwise, transliteration
# attempts will fail.

if ($input =~ /(\band\s|\bthe\b|\bof\b)/)
{
	$disallowed = '"'.$1.'" as its own word';
	HTMLspew('tecindo-ume-quenya.html');
	exit 1;
}
if ($input =~ /(\b[db])/)
{
	$disallowed = '"'.$1.'" at the beginning of a word';
	HTMLspew('tecindo-ume-quenya.html');
	exit 1;
}
if ($input =~ /(ng\s)/)
{
	$disallowed = '"'.$1.'" at the end of a word';
	HTMLspew('tecindo-ume-quenya.html');
	exit 1;
}
if ($input =~ /(j|ch|sh)/)
{
	$disallowed = '"'.$1."'";
	HTMLspew('tecindo-ume-quenya.html');
	exit 1;
}


# Begin transliteration.

# Start regularizing orthography.

$input =~ s/qu/q/g;
$input =~ s/c/k/g;
$input =~ s/x/ks/g;
$input =~ s/ng/N/g;

# Now, figure out what diacritical system the person used.

if ($input =~ /[\xC0-\xFF]/)
{
	$dia_type = 'high-ASCII';
}
elsif ($input =~ /aa|ee|ii|oo|uu/)
{
	$dia_type = 'doubled vowels';
}
elsif ($input =~ /[aeiou]['\"\:]/)
{
	$dia_type = 'following mark';
}
else
{
	$dia_type = 'unknown!';
}

#print "\nThis uses diacritical type: $dia_type\n";

if ($dia_type eq 'high-ASCII')
{
	$input =~ s/[\xC1\xE1]/A/g;
	$input =~ s/[\xC9\xE9]/E/g;
	$input =~ s/[\xCB\xEB]/e/g;
	$input =~ s/[\xCD\xED]/I/g;
	$input =~ s/[\xD1\xF1]/N/g;
	$input =~ s/[\xD3\xF3]/O/g;
	$input =~ s/[\xDA\xFA]/U/g;
}
elsif ($dia_type eq 'doubled vowels')
{
	$input =~ s/aa/A/g;
	$input =~ s/ee/E/g;
	$input =~ s/ii/I/g;
	$input =~ s/oo/O/g;
	$input =~ s/uu/U/g;
}
elsif ($dia_type eq 'following mark')
{
	$input =~ s/a'/A/g;
	$input =~ s/e'/E/g;
	$input =~ s/i'/I/g;
	$input =~ s/o'/O/g;
	$input =~ s/u'/U/g;
	$input =~ s/e[\:\"]/e/g;
}

$transform = $input;

######
#
# Note for future expansion: this is where we're going to have to
# perform the kind of morphological scan that will be necessary
# to properly handle the suule/silme problem. This will be a
# high-level scan that checks morphology and etymology, and turns
# any "s" that should be spelled with suule into "th".
#
# Ick.
#
######


# Initialize output string, and prepare to process through input
# character-by-character, transliterating as you go.

$output = '';
$prevChar = 'null';
$prevCharType = 'null';
$lastChar = 'null';

$thisChar = '';
$nextChar = '';
$thisCharType = 'null';
$nextCharType = 'null';

LOOP:
while ($input ne '')
{
	getNextChar();

	if ($thisCharType eq 'vowel')
	{
		# Find out if this is part of a diphthong.
		$dipTest = $thisChar.$nextChar;
		if ($dipTest eq 'ai' || $dipTest eq 'au' ||
		    $dipTest eq 'eu' || $dipTest eq 'iu' ||
		    $dipTest eq 'oi' || $dipTest eq 'ui')
		{
			$diphthong = $dipTest.' diphthong';
			addChar($diphthong);
			getNextChar();
		}
		elsif ($prevCharType eq 'consonant' || $prevCharType eq 'semivowel')
		{
			# It's not a diphthong. But the previous
			# character was a consonant, so we can put
			# a _tehta_ on it.


			if ($thisChar =~ /[AEIOU]/)
			{
				# Whoops, this vowel is long. We may not be 
				# allowed to put a _tehta_ over the prev. 
				# consonant.

				if ($CarrierPref == 1 && $thisChar =~ /[AI]/)
				{
					addChar('long carrier');
					$tehta = lc($thisChar).' tehta';
					addChar($tehta);
				}
				elsif ($CarrierPref == 2)
				{
					addChar('long carrier');
					$tehta = lc($thisChar).' tehta';
					addChar($tehta);
				}
				else
				{
					$tehta = $thisChar.' tehta';
					addChar($tehta);
				}
			}
			else
			{
				# Ah, this vowel is short (and we're still
				# processing the case of just post-consonant).
				# Just go ahead and put a single _tehta_ on
				# the last character.

				$tehta = $thisChar.' tehta';
				addChar($tehta);
			}
		}
		elsif ($prevCharType eq 'vowel' || $prevCharType eq 'null')
		{
			# The previous character wasn't a consonant; we 
			# flat-out *need* a carrier.

			# Long or short carrier?
			if (lc($thisChar) eq $thisChar)
			{
				$carrier = 'short carrier';
			}
			else
			{
				$carrier = 'long carrier';
			}

			addChar($carrier);
			$tehta = lc($thisChar).' tehta';
			addChar($tehta);
		}
		else
		{
			# The previous character was a semivowel.
			# I'll figure out what to do later.
		}
	}
	elsif ($thisCharType eq 'consonant')
	{
		# First, check to see if this is the second
		# of a doubled consonant-pair. If so, just
		# put a 'doubler' symbol under the previous
		# one.

		# (Note that doubled S is handled below, which
		# is *earlier* in the string parsing!)

		if ($thisChar eq $prevChar)
		{
			addChar('doubler');
			next LOOP;
		}


		# Next... could this be a compound consonant?

		$compound = $thisChar . $nextChar;
#		print "   Compound check: $compound\n";
#		if ($compound eq 'ss' && ! $DblSilme) 
#		{
#			getNextChar();
#			if ($nextCharType eq 'vowel')
#			{
#				addChar('are nuquerna');
#			}
#			else
#			{
#				addChar('are');
#			}
#			next LOOP;
#		}
		if ($compound eq 'hw')
		{
			addChar('hwesta');
			getNextChar();
			$thisCharType = 'consonant';
			next LOOP;
		}
		elsif ($compound eq 'ld')
		{
			addChar('alda');
			getNextChar();
			next LOOP;
		}	
		elsif ($compound eq 'mb')
		{
			addChar('umbar');
			getNextChar();
			next LOOP;
		}	
		elsif ($compound eq 'mp')
		{
			addChar('ampa');
			getNextChar();
			next LOOP;
		}	
		elsif ($compound eq 'nd')
		{
			addChar('ando');
			getNextChar();
			next LOOP;
		}	
		elsif ($compound eq 'nk')
		{
			addChar('anca');
			getNextChar();
			next LOOP;
		}	
		elsif ($compound eq 'nq')
		{
			addChar('unque');
			getNextChar();
			next LOOP;
		}	
		elsif ($compound eq 'nt')
		{
			addChar('anto');
			getNextChar();
			next LOOP;
		}	
		elsif ($compound eq 'Nw')
		{
			addChar('ungwe');
			getNextChar();
			next LOOP;
		}	
		elsif ($compound eq 'rd')
		{
			addChar('arda');
			getNextChar();
			next LOOP;
		}	
		elsif ($compound eq 'th')
		{
			addChar('thule');
			getNextChar();
			next LOOP;
		}	

		# Okay, if you got through that, then the consonant
		# wasn't a compound type.

		# If it's an s, it may be a following-s, or it may
		# have a vowel after it (in which case it should be
		# _nuquerna_ to make room for the _tehta_), or it
		# may have a consonant or space following it (in
		# which case it can be normal).

		if ($thisChar eq 's')
		{
			if ($prevCharType eq 'consonant')
			{
				# Now we have some minor hell to go through.
				# Because of the width of the hook, we have
				# to apply a following vowel *first* -- but
				# not if it's a diphthong!

				local $twoCheck = $nextChar.substr($input, 1,1);
				if ($twoCheck =~ /(ai|au|eu|iu|oi|ui)/)
				{
					addChar('hook');
					next LOOP;
				}
				elsif ($nextCharType eq 'vowel')
				{
					$tehta = $nextChar.' tehta';
					addChar($tehta);
					addChar('hook');
					next LOOP;
				}
			}
			else
			{
				local $baseLetter;
				if ($nextChar eq 's')
				{
					$baseLetter = ($DblSilme eq 'on') ? 'silme' : 'are';
					if (shouldBeNuquerna(double))
					{
						addChar($baseLetter.' nuquerna');
					}
					else
					{
						addChar($baseLetter);
					}
					getNextChar() unless($DblSilme eq 'on');
					next LOOP;
				}
				else
				{
					$baseLetter = 'silme';
					if (shouldBeNuquerna())
					{
						addChar($baseLetter.' nuquerna');
					}
					else
					{
						addChar($baseLetter);
					}
					next LOOP;
				}
			}
#			else
#			{
#				addRomanChar('?');
#				next LOOP;
#			}
		}

		if ($thisChar eq 't')
		{
			addChar('tinco');
			next LOOP;
		}
		if ($thisChar eq 'p')
		{
			addChar('parma');
			next LOOP;
		}
		if ($thisChar eq 'k')
		{
			addChar('calma');
			next LOOP;
		}
		if ($thisChar eq 'q')
		{
			addChar('quesse');
			next LOOP;
		}
		if ($thisChar eq 'N')
		{
			if ($nextChar eq 'o' || $nextChar eq 'O')
			{
				addChar('noldo');
			}
			else
			{
				addChar('anga');
			}
			next LOOP;
		}
		if ($thisChar eq 'f')
		{
			addChar('formen');
			next LOOP;
		}
		if ($thisChar eq 'n')
		{
			addChar('numen');
			next LOOP;
		}
		if ($thisChar eq 'm')
		{
			addChar('malta');
			next LOOP;
		}
		if ($thisChar eq 'v')
		{
			addChar('vala');
			next LOOP;
		}
		if ($thisChar eq 'w')
		{
			addChar('vilya');
			next LOOP;
		}
		if ($thisChar eq 'r')
		{
			if ($nextCharType eq 'null' || $nextCharType eq 'consonant')
			{
				addChar('ore');
			}
			else
			{
				addChar('romen');
			}
			next LOOP;
		}
		if ($thisChar eq 'l')
		{
			addChar('lambe');
			next LOOP;
		}
		if ($thisChar eq 'h')
		{
			if ($prevCharType eq 'null' && $InitialHalla && $nextCharType eq 'consonant')
			{
				addChar('halla');
			}
			else
			{
				addChar('hyarmen');
			}
			next LOOP;
		}

	}
	elsif ($thisChar eq 'y')
	{
		if ($prevCharType eq 'null')
		{
			addChar('anna');
			addChar('following y');
			$thisCharType = 'consonant';
			next LOOP;
		}
		elsif ($prevCharType eq 'consonant')
		{
			addChar('following y');
			$thisCharType = 'consonant';
			next LOOP;
		}
		elsif ($prevCharType eq 'vowel')
		{
			addChar('anna');
			addChar('following y');
			$thisCharType = 'consonant';
			next LOOP;
		}
	}
	elsif ($thisChar eq ' ')
	{
		addChar(' ');
#		getNextChar();
		next LOOP;
	}
#	elsif ($thisChar =~ /[\n\r]/)
#	{
#		$output .= "<BR>\n";
#		getNextChar();
#		next LOOP;
#	}
	elsif ($thisCharType eq 'punct')
	{
		if ($EnglishPunct)
		{
			addRomanChar($thisChar);
		}
		else
		{
			if ($thisChar eq ',')
			{
				addChar('comma');
			}
			elsif ($thisChar eq '.')
			{
				addChar('period');
			}
			elsif ($thisChar eq ';')
			{
				addChar('semicolon');
			}
			elsif ($thisChar eq '?')
			{
				addChar('ques');
			}
			elsif ($thisChar eq '!')
			{
				addChar('bang');
			}
			elsif ($thisChar eq '-')
			{
				addChar('hyphen');
			}
			else
			{
				addChar('paren');
			}
		}
		next LOOP;
	}
	elsif ($thisCharType eq 'numeral')
	{
		addChar(chr(240+$thisChar));
	}
	else
	{
		addRomanChar($thisChar);
	}

}

chomp($dateStamp = `date +'%Y-%m-%d: %T'`);
$remoteIP = $ENV{REMOTE_ADDR};
$userAgent = $ENV{HTTP_USER_AGENT};
$log_file = 'tecindo.log';

$optString = 'CPref '.$CarrierPref.'; ';
$optString .= 'EPun, '   if ($EnglishPunct eq 'on');
$optString .= 'RPun, '   if ($RedPunct eq 'on');
$optString .= 'RDia, '   if ($RedDiacrit eq 'on');
$optString .= 'Silme, '  if ($DblSilme eq 'on');
$optString .= 'Ore, '    if ($FinalOre eq 'on');
$optString .= 'Halla, '  if ($InitialHalla eq 'on');

$optString =~ s/, $//;

unless ($remoteIP eq '64.81.50.199')
{
	open LOG, ">>$log_file";
	print LOG "$dateStamp [$remoteIP] $optString\n   $userAgent\n   $quettar\n";
	close LOG;
}

pop @tengwar;
$tengwessi = join(', ', @tengwar);
$tengwessi =~ s/pace\),/pace\),<BR>\n/g;

$calls_string = join("\n\t<li>", @calls);
$calls_string = '<li>'.$calls_string;
$calls_string =~ s/<li> ?\n/<li>&nbsp;&nbsp;<I>(space)<\/i>\n/g;

#$foobar = $#tengwar;

if ($#tengwar > 0)
{
	$tengwessiPlural = 'tengwessi';
	$letternamePlural = 's';
	$verbPlural = 'were';
}
else
{
	$tengwessiPlural = 'tengwesse';
	$letternamePlural = '';
	$verbPlural = 'was';
}


if ($quettar eq 'Merin cene essenya Tengwainen.')
{
	$extraNote =  <<EOF;
<P>Unfortunately, <I>tecindo</i> cannot tell you your name in Quenya. However, you may be able to find your name listed in the <A HREF="http://www.elvish.org/elm/names.html" TARGET="_blank">Quenya Lapseparma</a> (Quenya Baby-Name-Book), a dictionary of name meanings and their translations in Quenya. If so, you can always come back and enter your Quenya name into <I>tecindo</i>, to see your name in Quenya <STRONG>and</strong> in <I>tengwar</i>!
</p>
EOF
}
elsif ($quettar eq 'Merin iste quete Quenyainen.')
{
	$extraNote = <<EOF
<P><I>Tecindo</i> is not designed to teach you how to speak Quenya. But if you're interested, you can download Helge Fauskanger's <A HREF="http://www.uib.no/People/hnohf/qcourse.htm" TARGET="_blank">Quenya Course</a> from his excellent site, <A HREF="http://www.uib.no/People/hnohf/" TARGET="_blank"><I>Ardalambion</i></a> (roughly, &quot;The Languages of Middle-Earth&quot;). Mr. Fauskanger's course is highly recommended for those who want to learn Quenya. Also, you should be strongly advised to <STRONG>avoid</strong> Ruth Noel's book &quot;The Languages of Tolkien's Middle-Earth&quot; -- it is <A HREF="http://www.elvish.org/articles/LRH.html" TARGET="_blank">extremely inaccurate</a>.
</p>
EOF
}
elsif ($quettar eq 'Merin iste tece tengwainen.')
{
	$extraNote = <<EOF
<P><I>Tecindo</i> is not designed to actually teach you to write <I>tengwar</i>. If you want to learn <I>tengwar</i> (the alphabet, as opposed to learning Quenya or Sindarin, which are the major Elven languages), here are some resources:
</p>
<UL>
	<LI>M&aring;ns Bj&ouml;rkman's <A HREF="http://hem.passagen.se/mansb/at/" TARGET="_blank">Amanye Tenceli</a> (&quot;Writing-Systems of Middle-Earth&quot") includes illuminated <I>tengwar</i> manuscripts.
	<LI>Harri Per&auml;l&auml;'s <A HREF="http://www.sci.fi/~alboin/tengwartutorial.htm" TARGET="_blank">Writing With Elvish Fonts</a> includes discussion of various <I>tengwar</i> tools and programs.
	<LI>Of course, <A HREF="http://www.geocities.com/TimesSquare/4948/tengwar/index.htm" TARGET="_blank">Tengwar</a>, by Dan Smith (author of the font used to create the output above!) is a must-see resource.
</ul>	
EOF
}
else
{
	$extraNote = '';
}

HTMLspew('tecindo-output.html');
exit 0;





# ------------ End main routine. ----------------------------

sub HTMLspew {

my $spew_file = $_[0];
$FullError = $!;

my ($line, $token, $substitute, $sub2, $starred);

if (-f $spew_file)
{
	open SPEW, $spew_file || die("Yikes! Couldn't open display file $spew_file: $!");
	print "Content-type: text/html\n\n";
	while ($line = <SPEW>)
	{
		$starred = 0;
		while ( $line =~ /(\[[\_a-zA-Z0-9]+\])/)
		{
			$token = $1;
			local $stars = $token;
			$substitute = substr($token, 1, length($token) - 2);
			$token =~ s/\[/\\\[/;
			$token =~ s/\]/\\\]/;
			if (defined $$substitute)
			{
				$sub2 = $substitute;
				$substitute = ${$substitute};
				$line =~ s/$token/$substitute/;
			} elsif (defined $ENV{$substitute}) {
				$sub2 = $substitute;
				$substitute = $ENV{$substitute};
				$line =~ s/$token/$substitute/;
			} else {
				$stars =~ s/\[/[\*/;
				$stars =~ s/]/\*]/;
				$line =~ s/$token/$stars/;
				$starred = 1;
			}	
		}	
		# Then take out that crud if ($variable set).
		if ($starred)
		{
			$line =~ s/\[\*/[/g;
			$line =~ s/\*]/]/g;
		}
		print "$line";
	}
	close SPEW;
	exit 1;
} else {
	print "Content-type: text/html\n\n";
	print <<EOF;
<HTML>

<HEAD>
	<TITLE>Error! Template File Missing</title>
</head>

<BODY>

<H1>Error! Template File Missing</h1>

<H2>File Name: $spew_file</h2>

<P>At this point, I'm supposed to display the template file <TT>$spew_file</tt> (where "I" am the <TT>$ENV{SCRIPT_NAME}</tt> script). Unfortunately, I couldn't find that file. You may wish to contact the <A HREF="mailto:$ENV{SERVER_ADMIN}">server administrator</a> or otherwise let someone know that there's been a problem with me.
</p>

</body>
</html>
EOF

	exit 1;
	
}

}












sub getNextChar {

$prevChar = $thisChar;
$prevCharType = $thisCharType;

$thisChar = substr($input, 0, 1);
$input = substr($input, 1);
$nextChar = substr($input, 0, 1);

$thisCharType = getCharType($thisChar);
$nextCharType = getCharType($nextChar);

}



sub getCharType {

local ($char = $_[0]);

if ($char =~ /[aeiou]/i)
{
	$return = 'vowel';
}
#elsif ($char =~ /[wy]/i)
elsif ($char =~ /[y]/i)
{
	$return = 'semivowel';
}
elsif ($char =~ /\d/)
{
	$return = 'numeral';
}
elsif ($char =~ /[\.,;!?\(\)\-]/)
{
	$return = 'punct';
}
elsif ($char =~ /[bdfghklmnpqrstvwz]/i)
{
	$return = 'consonant';
}
elsif ($char =~ /\s/)
{
	$return = 'null';
}
else
{
	$return = 'unknown';
}
return $return;

}



sub addRomanChar {

local $newChar = $_[0];
$output .= "</font>$newChar<FONT FACE=\"Tengwar Quenya\">";
push @tengwar, $newChar;
push @calls, $newChar;

}



sub addChar {

local $newChar = $_[0];
local $tengwesse = '';

if ($newChar eq ' ') 
{ 
	$tengwesse = '(space)';
}
else
{
	$tengwesse = $newChar;
}

if ($tengwesse =~ /tehta/)
{
	$tengwesse =~ s/tehta/<I>tehta<\/i>/;
	if ($tengwesse =~ /^[AEIOU]/)
	{
		$tengwesse = 'doubled-'.lc($tengwesse);
	}
	$tengwesse =~ s/ /-/;
	if ($tengwar[$#tengwar] =~ /with/)
	{
		$tengwesse = ' and '.$tengwesse;
	}
	else
	{
		$tengwesse = ' with '.$tengwesse;
	}
	$tengwar[$#tengwar] .= $tengwesse;
}
elsif ($tengwesse =~ /diphthong/)
{
	local ($dipTehta, $letter) = (split(//, $tengwesse))[0,1];
	$letter = ($letter eq 'i') ? yanta : ure;
	$tengwesse .= " (<I>$letter</i> with $dipTehta-<I>tehta</i>)";
	push @tengwar, $tengwesse;
}
elsif ($tengwesse =~ /follow/)
{
	$tengwesse =~ s/ /-/;
	$tengwesse = ' with '.$tengwesse.' marker';
	$tengwar[$#tengwar] .= $tengwesse;
}
elsif ($tengwesse eq 'doubler')
{
	$tengwesse = ' with doubler';
	$tengwar[$#tengwar] .= $tengwesse;
}
else
{
	unless (getCharType($thisChar) eq 'punct' ||
			$tengwesse =~ /space/           ||
			$tengwesse =~ /carrier/ )
	{
		$tengwesse = '<I>'.$tengwesse.'</i>'
	}

	$tengwesse = 'question mark' if ($tengwesse eq 'ques');
	$tengwesse = 'exclamation point' if ($tengwesse eq 'bang');
	$tengwesse .= 'thesis' if ($tengwesse eq 'paren');

	push @tengwar, $tengwesse;
}

push @calls, $newChar;

$tyelle = getTyelle($newChar);
$tema = getTema($newChar);

if ($tyelle && $tema)
{
	$output .= $charTable{$newChar};
}
elsif ($newChar =~ /carrier/)
{
	$output .= $charTable{$newChar};
}
elsif ($thisCharType eq 'punct')
{
	if ($RedPunct)
	{
		$output .= '<FONT COLOR="#FF0000">'.$charTable{$newChar}.'</font>';
	}
	else
	{
		$output .= $charTable{$newChar};
	}
}
elsif ($thisCharType eq 'null')
{
	$output .= $newChar;
}
elsif ($newChar =~ /tehta/)
{
	local $subOutput;
	if($newChar =~ /[AEIOU]/)
	{
		$subOutput = $charTable{$newChar};
	}
	else
	{
		local $addString = $newChar.' '.getTehtaType();
		$subOutput = $charTable{$addString};
	}

	if ($RedDiacrit)
	{
		$subOutput = '<FONT COLOR="#FF0000">'.$subOutput.'</font>';
	}
	$output .= $subOutput;
}
elsif ($newChar =~ /diphthong/)
{
	local $firstLetter = substr($newChar, 0,1);
	local $secondLetter = substr($newChar, 1,1);
	local ($tengwa, $place);
	if ($secondLetter eq 'u')
	{
		$tengwa = 'ure';
		$place = 'right';
	}
	else
	{
		$tengwa = 'yanta';
		$place = 'mid';
	}

	local $tehta = $firstLetter.' tehta '.$place;
	$tehta = $charTable{$tehta};
	if ($RedDiacrit)
	{
		$tehta = '<FONT COLOR="#FF0000">'.$tehta.'</font>';
	}

	$output .= $charTable{$tengwa}.$tehta;
}
elsif ($newChar =~ /doubler/)
{
	local $doublerString = 'doubler '.getDoublerType();
	$doublerString = $charTable{$doublerString};
	if ($RedDiacrit)
	{
		$doublerString = '<FONT COLOR="#FF0000">'.$doublerString.'</font>';
	}
	$output .= $doublerString;

}
elsif ($newChar eq 'hook')
{
	local $hookString = 'hook '.getHookType();
	$hookString = $charTable{$hookString};
	$output .= $hookString;
}
elsif ($newChar =~ /^following/)
{
	local $wyeString = 'following y '.getFollowingYType();
	push @calls, $wyeString;
	$wyeString = $charTable{$wyeString};
	if ($RedDiacrit)
	{
		$wyeString = '<FONT COLOR="#FF0000">'.$wyeString.'</font>';
	}
	$output .= $wyeString;
}
elsif ($newChar eq 'halla')
{
	local $hallaString = $charTable{halla};
	if ($RedDiacrit)
	{
		$hallaString = '<FONT COLOR="#FF0000">'.$hallaString.'</font>'; 
	}
	$output .= $hallaString;
}
else
{
	$output .= $newChar;
}

$lastChar = $newChar unless (($thisChar eq 's' && $newChar =~ /tehta/) || 
						$newChar eq 'doubler' ||
						$newChar eq 'following y');

}


sub getTyelle {

local $getChar = $_[0];
local $ret;

if     ($getChar eq 'tinco' || $getChar eq 'parma' ||
		$getChar eq 'calma' || $getChar eq 'quesse')
{
	$ret = 1;
}
elsif  ($getChar eq 'ando' || $getChar eq 'umbar' ||
		$getChar eq 'anga' || $getChar eq 'ungwe')
{
	$ret = 2;
}
elsif  ($getChar eq 'thule' || $getChar eq 'formen' ||
		$getChar eq 'harma' || $getChar eq 'hwesta')
{
	$ret = 3;
}
elsif  ($getChar eq 'anto' || $getChar eq 'ampa' ||
		$getChar eq 'anca' || $getChar eq 'unque')
{
	$ret = 4;
}
elsif  ($getChar eq 'numen' || $getChar eq 'malta' ||
		$getChar eq 'noldo' || $getChar eq 'nwalme')
{
	$ret = 5;
}
elsif  ($getChar eq 'ore' || $getChar eq 'vala' ||
		$getChar eq 'anna' || $getChar eq 'vilya')
{
	$ret = 6;
}
elsif  ($getChar eq 'romen' || $getChar eq 'arda' ||
		$getChar eq 'lambe' || $getChar eq 'alda')
{
	$ret = 7;
}
elsif  ($getChar =~ /silme/ || $getChar =~ /^are/)
{
	$ret = 8;
}
elsif  ($getChar eq 'hyarmen' || $getChar =~ /sindarin/ ||
		$getChar eq 'yanta' || $getChar eq 'ure')
{
	$ret = 9;
}
else
{
	$ret = 0;
}

return $ret;

}



sub getTema {

local $getChar = $_[0];
local $ret;

if     ($getChar eq 'tinco' || $getChar eq 'ando' || 
		$getChar eq 'thule' || $getChar eq 'anto' ||
		$getChar eq 'numen' || $getChar eq 'ore' ||
		$getChar eq 'romen' || $getChar eq 'silme' ||
		$getChar eq 'hyarmen')
{
	$ret = 1;
}
elsif  ($getChar eq 'parma' || $getChar eq 'umbar' ||
		$getChar eq 'formen' || $getChar eq 'ampa' ||
		$getChar eq 'malta' || $getChar eq 'vala' ||
		$getChar eq 'arda' || $getChar eq 'silme nuquerna' ||
		$getChar eq 'hwesta sindarinwa')
{
	$ret = 2;
}
elsif  ($getChar eq 'calma' || $getChar eq 'anga' ||
		$getChar eq 'harma' || $getChar eq 'anca' ||
		$getChar eq 'noldo' || $getChar eq 'anna' ||
		$getChar eq 'lambe' || $getChar eq 'are' ||
		$getChar eq 'yanta')
{
	$ret = 3;
}
elsif  ($getChar eq 'quesse' || $getChar eq 'ungwe' ||
		$getChar eq 'hwesta' || $getChar eq 'unque' ||
		$getChar eq 'nwalme' || $getChar eq 'vilya' ||
		$getChar eq 'alda' || $getChar eq 'are nuquerna' ||
		$getChar eq 'ure')
{
	$ret = 4;
}
else
{
	$ret = 0;
}

return $ret;

}





sub getTehtaType {

local $oldTyelle = getTyelle($lastChar);
local $oldTema = getTema($lastChar);
local $ret;

if ($oldTyelle == 1)
{
	$ret = 'right';
}
elsif ($oldTyelle == 2)
{
	$ret = 'left';
}
elsif ($oldTyelle == 3)
{
	if ($oldTema <= 2)
	{
		$ret = 'right';
	}
	else
	{
		$ret = 'left';
	}
}
elsif ($oldTyelle == 4)
{
	if ($oldTema <= 2)
	{
		$ret = 'mid';
	}
	else
	{
		$ret = 'left';
	}
}
elsif ($oldTyelle == 5)
{
	$ret = 'left';
}
elsif ($oldTyelle == 9 && $oldTema == 1)
{
	$ret = 'right';
}
elsif ($oldTyelle >= 6)
{
	$ret = 'mid';
}
elsif ($oldTyelle == 0)
{
	if ($lastChar =~ /carrier/)
	{
		$ret = 'narrow';
	}	
}

}


sub getDoublerType {

local $oldTyelle = getTyelle($lastChar);
local $oldTema = getTema($lastChar);
local $ret;

if ($oldTyelle == 1)
{
	$ret = 'narrow';
}
elsif ($oldTyelle == 2)
{
	$ret = 'wide';
}
elsif ($oldTyelle == 3)
{
	$ret = 'narrow';
}
elsif ($oldTyelle == 4)
{
	$ret = 'wide';
}
elsif ($oldTyelle == 5)
{
	$ret = 'wide';
}
elsif ($oldTyelle == 6)
{
	$ret = 'narrow';
}
elsif ($oldTyelle == 7)
{
	if ($oldTema <= 2)
	{
		$ret = 'narrow above';
	}
	elsif ($oldTema == 3)
	{
		$ret = 'inside';
	}
	elsif ($oldTema == 4)
	{
		$ret = 'narrow low';
	}
}
elsif ($oldTyelle == 8)
{
	$ret = 'narrow';
	$ret .= ' low' if ($oldTema == 2 || $oldTema == 4);
}

return $ret;

}


sub getHookType {

local $oldTyelle = getTyelle($lastChar);
local $oldTema = getTema($lastChar);
local $ret;

if ($tema == 1)
{
	$ret = 'wide';
}
elsif ($tema == 2)
{
	$ret = 'narrow';
}
else
{
	$ret = 'reversed';
}

return $ret;


}


sub getFollowingYType {

local $oldTyelle = getTyelle($lastChar);
local $oldTema = getTema($lastChar);
local $ret;

if ($oldTyelle == 1)
{
	if ($oldTema <= 2)
	{
		$ret = 'right';
	}
	else
	{
		$ret = 'left';
	}
}
elsif($oldTyelle == 2)
{
	if ($oldTema <= 2)
	{
		$ret = 'right';
	}
	else
	{
		$ret = 'left';
	}
}
elsif($oldTyelle == 3 || $oldTyelle == 6)
{
	$ret = 'mid';
}
elsif($oldTyelle == 4 || $oldTyelle == 5)
{
	$ret = 'left';
}
elsif($oldTyelle == 7)
{
	if ($oldTema <=2)
	{
		$ret = 'right';
	}
	elsif ($oldTema == 3)
	{
		$ret = 'inside';
	}
	else
	{
		$ret = 'mid';
	}
}
elsif($oldTyelle == 8)
{
	if ($oldTema <=2)
	{
		$ret = 'mid';
	}
	elsif ($oldTema == 3)
	{
		$ret = 'inside';
	}
	else
	{
		$ret = 'right';
	}
}
elsif($oldTyelle == 9)
{
	$ret = 'mid';
}


}


sub shouldBeNuquerna {

local $double = $_[0] || '';
local ($ret, $charToTest, $numLevel);

if ($double eq 'double')
{
	$charToTest = substr($input, 1, 1);
}
else
{
	$charToTest = $nextChar;
}

if ($charToTest =~ /[tpkqNrlmfvwh]/) { $numLevel = 0; }
if ($charToTest =~ /[AI]/)           { $numLevel = 1; }
if ($charToTest =~ /[EOU]/)          { $numLevel = 2; }
if ($charToTest =~ /[aeiou]/)        { $numLevel = 3; } 
if ($numLevel > $CarrierPref)
{
	$ret = 1;
}
else
{
	$ret = 0;
}

#push @tengwar, '(next: '.$charToTest.', level '.$numLevel.')';
return $ret;

}



