/* OTtongueRoot.c
 *
 * Copyright (C) 1997-2002 Paul Boersma
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * pb 2001/08/02
 * pb 2002/07/16 GPL
 */

#include "OTtongueRoot.h"
#include "longchar.h"

#define NO_ATR_high 1
#define NO_ATR_mid 2
#define NO_ATR_low 3
#define NO_RTR_high 4
#define NO_RTR_mid 5
#define NO_RTR_low 6
#define NO_TR_CONTOUR 7
#define PARSE_ATR 8
#define PARSE_RTR 9

static const char **constraintNames (I) {
	static const char *names [] = { "",
		"*[atr / hi]", "*[atr / mid]", "*[atr / lo]",
		"*[rtr / hi]", "*[rtr / mid]", "*[rtr / lo]",
		"*G\\s{ESTURE}\n(contour)", "P\\s{ARSE}\n(atr)", "P\\s{ARSE}\n(rtr)",
	NULL };
	(void) void_me;
	return & names [0];
}

static int evaluate (I, int which, const char *charInput, const char *charOutput, long icand_hint) {
	const short *pin, *pout;
	int marks = 0;
	short input [101], output [101];
	(void) void_me;
	(void) icand_hint;
	Longchar_genericToWide (charInput, input);
	Longchar_genericToWide (charOutput, output);
	switch (which) {
		case NO_ATR_high: {
			for (pout = output; *pout; pout ++) if (*pout == 'i ') marks ++;
		} break;
		case NO_ATR_mid: {
			for (pout = output; *pout; pout ++) if (*pout == 'e ') marks ++;
		} break;
		case NO_ATR_low: {
			for (pout = output; *pout; pout ++) if (*pout == 'sw') marks ++;
		} break;
		case NO_RTR_high: {
			for (pout = output; *pout; pout ++) if (*pout == 'ic') marks ++;
		} break;
		case NO_RTR_mid: {
			for (pout = output; *pout; pout ++) if (*pout == 'ep') marks ++;
		} break;
		case NO_RTR_low: {
			for (pout = output; *pout; pout ++) if (*pout == 'a ') marks ++;
		} break;
		case NO_TR_CONTOUR: {
			int currentAtr = -1;
			for (pout = output; *pout; pout ++) {
				if (*pout == 'i ' || *pout == 'e ' || *pout == 'sw') {
					if (currentAtr == 0) marks ++;
					currentAtr = 1;
				} else if (*pout == 'ic' || *pout == 'ep' || *pout == 'a ') {
					if (currentAtr == 1) marks ++;
					currentAtr = 0;
				}
			}
		} break;
		case PARSE_ATR: {
			for (pin = input, pout = output; *pin; pin ++, pout ++) if (*pin == 'i ' || *pin == 'e ' || *pin == 'sw')
				if (*pout != 'i ' && *pout != 'e ' && *pout != 'sw') marks ++;
		} break;
		case PARSE_RTR: {
			for (pin = input, pout = output; *pin; pin ++, pout ++) if (*pin == 'ic' || *pin == 'ep' || *pin == 'a ')
				if (*pout != 'ic' && *pout != 'ep' && *pout != 'a ') marks ++;
		} break;
		default: break;
	}
	return marks;
}

static int height (short kar) {
	if (kar == 'a ' || kar == 'sw') return 1;
	if (kar == 'ep' || kar == 'e ') return 2;
	return 3;
}

static const char * ipaSymbol (int height, int atr) {
	switch (height) {
		case 1: return atr ? "\\sw" : "a";
		case 2: return atr ? "e" : "\\ep";
		default: return atr ? "i" : "\\ic";
	}
	return "";
}

static void genTr (OTAnyTableau me, const short *input, int icand, int atr1, int atr2) {
	char cand [40];
	int height1 = height (input [0]), height2 = input [0] && input [1] ? height (input [2]) : 0;
	sprintf (cand, "%st%s", ipaSymbol (height1, atr1), ipaSymbol (height2, atr2));
	my candidates [icand] = Melder_strdup (cand);
}

static long gen (I, thou, const char *charInput) {
	thouart (OTAnyTableau);
	short input [101];
	(void) void_me;
	Longchar_genericToWide (charInput, input);
	genTr (thee, input, 1, 0, 0);
	genTr (thee, input, 2, 0, 1);
	genTr (thee, input, 3, 1, 0);
	genTr (thee, input, 4, 1, 1);
	iferror return 0;
	return 4;
}

static void generateInput (I, char input []) {
	const char *vowels [] = { 0, "i", "\\ic", "e", "\\ep", "a", "\\sw" };
	(void) void_me;
	sprintf (input, "%st%s", vowels [NUMrandomInteger (1, 6)], vowels [NUMrandomInteger (1, 6)]);
}

static int locallyRanked (I, int c1, int c2) {
	(void) void_me;
	switch (c1) {
		case NO_ATR_high: return c2 == NO_ATR_mid || c2 == NO_ATR_low ? +1 : 0;
		case NO_ATR_mid: return c2 == NO_ATR_high ? -1 : c2 == NO_ATR_low ? +1 : 0;
		case NO_ATR_low: return c2 == NO_ATR_high || c2 == NO_ATR_mid ? -1 : 0;
		case NO_RTR_high: return c2 == NO_RTR_mid || c2 == NO_RTR_low ? -1 : 0;
		case NO_RTR_mid: return c2 == NO_RTR_high ? +1 : c2 == NO_RTR_low ? -1 : 0;
		case NO_RTR_low: return c2 == NO_RTR_high || c2 == NO_RTR_mid ? +1 : 0;
		default: return 0;
	}
	return 0;
}

class_methods (OTGrammar_tongueRoot, OTAnyGrammar)
	class_method (constraintNames)
	class_method (evaluate)
	class_method (gen)
	class_method (generateInput)
	class_method (locallyRanked)
class_methods_end

OTAnyGrammar OTGrammar_tongueRoot_create (int small_large, int equal_random_infant_Wolof) {
	OTAnyGrammar me = OTAnyGrammar_create (classOTGrammar_tongueRoot);
	long i, n;
	OTConstraint *c;
	if (! me) return NULL;
	n = my constraints -> size;
	Melder_assert (n == 9);   /* Guard against future changes. */
	c = (OTConstraint *) my constraints -> item;
	if (equal_random_infant_Wolof == 1) {   /* equal? */
		for (i = 1; i <= n; i ++) c [i] -> ranking = 100.0;
	} else if (equal_random_infant_Wolof == 2) {   /* random? */
		for (i = 1; i <= n; i ++) c [i] -> ranking = NUMrandomGauss (100.0, 10.0);
	} else if (equal_random_infant_Wolof == 3) {   /* infant (= cannot speak) ? */
		for (i = 1; i <= 7; i ++) c [i] -> ranking = 100.0;   /* Structural constraints. */
		for (i = 8; i <= 9; i ++) c [i] -> ranking = 50.0;   /* Faithfulness constraints. */
	} else {   /* adult Wolof */
		c [NO_RTR_high] -> ranking = 100.0;
		c [PARSE_RTR] -> ranking = 50.0;
		c [NO_TR_CONTOUR] -> ranking = 30.0;
		c [PARSE_ATR] -> ranking = 20.0;
		c [NO_ATR_low] -> ranking = 10.0;
		c [NO_RTR_mid] -> ranking = 0.0;
		c [NO_RTR_low] -> ranking = -10.0;
		c [NO_ATR_mid] -> ranking = 0.0;
		c [NO_ATR_high] -> ranking = -10.0;
	}
	for (i = 1; i <= n; i ++) c [i] -> disharmony = c [i] -> ranking;
	if (small_large == 1) {   /* small ? */
		Collection_removeItem (my constraints, NO_RTR_low);
		Collection_removeItem (my constraints, NO_RTR_mid);
		Collection_removeItem (my constraints, NO_ATR_mid);
		Collection_removeItem (my constraints, NO_ATR_high);
	}
	OTAnyGrammar_sort (me, 0.0);
	return me;
}

/* End of file OTtongueRoot.c */
