#include <fishtalk.h>

/* phonemes per letter */
#define MAX_PPL 5

#define FT_PHONEME(X) phon_##X,
static signed char* phons[] = {NULL, NULL, NULL, FT_PHONEMES};
#undef FT_PHONEME

#define FT_PHONEME(X) , #X
static const char* phon_str[] = {"(none)", "(space)", "" FT_PHONEMES};
#undef FT_PHONEME

static int p_match(const char* buffer, const char* target, long buffer_length) {
	int i;

	if(buffer_length < strlen(target)) return 0;

	for(i = 0; target[i] != 0; i++) {
		if(target[i] == '_') continue;

		if(tolower(buffer[i]) != tolower(target[i])) return 0;
	}

	return 1;
}

#define M(X) p_match(buffer + i, (X), length - i)
#define DEF(X) static int X(unsigned char* ph, int i, const char* buffer, long length, int first, int last, char b_c)

DEF(p_gen_4) {
	if(M("tion")) {
		ph[i * MAX_PPL + 0] = FT_P_SH;
		ph[i * MAX_PPL + 1] = FT_P_AH;
		ph[i * MAX_PPL + 2] = FT_P_N;
		return 3;
	} else if(M("sion")) {
		ph[i * MAX_PPL + 0] = FT_P_ZH;
		ph[i * MAX_PPL + 1] = FT_P_AH;
		ph[i * MAX_PPL + 2] = FT_P_N;
		return 3;
	}

	return -1;
}

DEF(p_silent_e) {
	if(M("a_e")) {
		ph[i * MAX_PPL + 0] = FT_P_EY;

		return 0;
	} else if(M("i_e")) {
		ph[i * MAX_PPL + 0] = FT_P_AY;

		return 0;
	} else if(M("o_e")) {
		ph[i * MAX_PPL + 0] = FT_P_OW;

		return 0;
	} else if(M("u_e")) {
		ph[i * MAX_PPL + 0] = FT_P_Y;
		ph[i * MAX_PPL + 1] = FT_P_UW;

		return 0;
	}

	return -1;
}

DEF(p_special_2) {
	if(M("ly") && last) {
		ph[i * MAX_PPL + 0] = FT_P_L;
		ph[i * MAX_PPL + 1] = FT_P_IY;

		return 1;
	} else if(M("le") && last) {
		ph[i * MAX_PPL + 0] = FT_P_L;
		ph[i * MAX_PPL + 1] = FT_P_EH;

		return 1;
	}

	return -1;
}

DEF(p_gen_2) {
	if(M("ll")) {
		ph[i * MAX_PPL + 0] = FT_P_L;

		return 1;
	} else if(M("sh")) {
		ph[i * MAX_PPL + 0] = FT_P_SH;

		return 1;
	} else if(M("ch")) {
		ph[i * MAX_PPL + 0] = FT_P_CH;

		return 1;
	} else if(M("th")) {
		ph[i * MAX_PPL + 0] = FT_P_TH;
		i++;

		return 1;
	} else if(M("ph")) {
		ph[i * MAX_PPL + 0] = FT_P_F;

		return 1;
	} else if(M("ng")) {
		ph[i * MAX_PPL + 0] = FT_P_NG;

		return 1;
	} else if(M("wr")) {
		ph[i * MAX_PPL + 0] = FT_P_R;

		return 1;
	} else if(M("kn") || M("gn")) {
		ph[i * MAX_PPL + 0] = FT_P_N;

		return 1;
	} else if(M("ck")) {
		ph[i * MAX_PPL + 0] = FT_P_K;

		return 1;
	} else if(M("gi")) {
		ph[i * MAX_PPL + 0] = FT_P_G;
		ph[i * MAX_PPL + 1] = FT_P_IY;

		return 1;
	} else if(M("wa")) {
		ph[i * MAX_PPL + 0] = FT_P_W;
		ph[i * MAX_PPL + 1] = FT_P_AE;

		return 1;
	} else if(M("te")) {
		ph[i * MAX_PPL + 0] = FT_P_T;
		ph[i * MAX_PPL + 1] = FT_P_EH;

		return 1;
	} else if(M("tt")) {
		ph[i * MAX_PPL + 0] = FT_P_T;

		return 1;
	} else if(M("do")) {
		ph[i * MAX_PPL + 0] = FT_P_D;
		ph[i * MAX_PPL + 1] = FT_P_OW;

		return 1;
	} else if(M("ss")) {
		ph[i * MAX_PPL + 0] = FT_P_S;

		return 1;
	}

	return -1;
}

DEF(p_vowel_2) {
	if(M("ee") || M("ea")) {
		ph[i * MAX_PPL + 0] = FT_P_EY;

		return 1;
	} else if(M("oo")) {
		ph[i * MAX_PPL + 0] = FT_P_UW; /* FIXME: could be FT_P_UH */

		return 1;
	} else if(M("ou")) {
		ph[i * MAX_PPL + 0] = FT_P_AW;

		return 1;
	} else if(M("ow")) {
		ph[i * MAX_PPL + 0] = FT_P_OW;

		return 1;
	} else if(M("oi") || M("oy")) {
		ph[i * MAX_PPL + 0] = FT_P_OY;

		return 1;
	} else if(M("rr")) {
		ph[i * MAX_PPL + 0] = FT_P_R;

		return 1;
	} else if(M("ar")) {
		ph[i * MAX_PPL + 0] = FT_P_AA;
		ph[i * MAX_PPL + 1] = FT_P_R;

		return 1;
	} else if(M("er") || M("ir") || M("ur")) {
		ph[i * MAX_PPL + 0] = FT_P_ER;

		return 1;
	} else if(M("or")) {
		ph[i * MAX_PPL + 0] = FT_P_AO;
		ph[i * MAX_PPL + 1] = FT_P_R;

		return 1;
	} else if(M("zz")) {
		ph[i * MAX_PPL + 0] = FT_P_Z;

		return 1;
	}

	return -1;
}

DEF(p_soft) {
	if(M("c")) {
		if(b_c == 'e' || b_c == 'i' || b_c == 'y') {
			ph[i * MAX_PPL + 0] = FT_P_S;
		} else {
			ph[i * MAX_PPL + 0] = FT_P_K;
		}

		return 0;
	} else if(M("g")) {
		if(b_c == 'e' || b_c == 'i' || b_c == 'y') {
			ph[i * MAX_PPL + 0] = FT_P_JH;
		} else {
			ph[i * MAX_PPL + 0] = FT_P_G;
		}

		return 0;
	}

	return -1;
}

DEF(p_special_1) {
	if(M("e") && last && tolower(b_c) != 'm') {
		return 0;
	} else if(M("y") && first) {
		ph[i * MAX_PPL + 0] = FT_P_Y;

		return 0;
	} else if(M("y") && last) {
		ph[i * MAX_PPL + 0] = FT_P_IY;

		return 0;
	} else if(M("n") && last) {
		ph[i * MAX_PPL + 0] = FT_P_N;

		return 0;
	} else if(M("k") && last) {
		ph[i * MAX_PPL + 0] = FT_P_K;

		return 0;
	} else if(M("x") && first) {
		ph[i * MAX_PPL + 0] = FT_P_Z;

		return 0;
	} else if(M("y") && last) {
		ph[i * MAX_PPL + 0] = FT_P_Y;

		return 0;
	}

	return -1;
}

DEF(p_gen_1) {
	if(M("s")) {
		ph[i * MAX_PPL + 0] = FT_P_S;

		return 0;
	} else if(M("f")) {
		ph[i * MAX_PPL + 0] = FT_P_F;

		return 0;
	} else if(M("t")) {
		ph[i * MAX_PPL + 0] = FT_P_T;

		return 0;
	} else if(M("p")) {
		ph[i * MAX_PPL + 0] = FT_P_P;

		return 0;
	} else if(M("l")) {
		ph[i * MAX_PPL + 0] = FT_P_L;

		return 0;
	} else if(M("m")) {
		ph[i * MAX_PPL + 0] = FT_P_M;

		return 0;
	} else if(M("n")) {
		ph[i * MAX_PPL + 0] = FT_P_N;

		return 0;
	} else if(M("d")) {
		ph[i * MAX_PPL + 0] = FT_P_D;

		return 0;
	} else if(M("h")) {
		ph[i * MAX_PPL + 0] = FT_P_HH;

		return 0;
	} else if(M("r")) {
		ph[i * MAX_PPL + 0] = FT_P_R;

		return 0;
	} else if(M("v")) {
		ph[i * MAX_PPL + 0] = FT_P_V;

		return 0;
	} else if(M("w")) {
		ph[i * MAX_PPL + 0] = FT_P_W;

		return 0;
	} else if(M("x")) {
		ph[i * MAX_PPL + 0] = FT_P_K;
		ph[i * MAX_PPL + 1] = FT_P_S;

		return 0;
	} else if(M("y")) {
		ph[i * MAX_PPL + 0] = FT_P_Y;

		return 0;
	} else if(M("z")) {
		ph[i * MAX_PPL + 0] = FT_P_Z;

		return 0;
	} else if(M("b")) {
		ph[i * MAX_PPL + 0] = FT_P_B;

		return 0;
	}

	return -1;
}

DEF(p_vowel_1) {
	if(M("a")) {
		ph[i * MAX_PPL + 0] = FT_P_AE;

		return 0;
	} else if(M("e")) {
		ph[i * MAX_PPL + 0] = FT_P_EH;

		return 0;
	} else if(M("i")) {
		ph[i * MAX_PPL + 0] = FT_P_IH;

		return 0;
	} else if(M("o")) {
		ph[i * MAX_PPL + 0] = FT_P_AA;

		return 0;
	} else if(M("u")) {
		ph[i * MAX_PPL + 0] = FT_P_AH;

		return 0;
	}

	return -1;
}

#undef DEF
#undef M

void ft_phon_main(FT_HANDLE handle, const char* buffer, long length) {
	unsigned char* ph = malloc(length * MAX_PPL);
	int	       i;

	memset(ph, FT_P_NONE, length * MAX_PPL);

	for(i = 0; i < length; i++) {
		int  first = 0;
		int  last  = 0;
		char b_c   = (i > 0) ? buffer[i - 1] : 0;
		int  n	   = -1;

		b_c = tolower(b_c);

		if(i == 0 || FT_IS_SKIPPABLE(buffer[i - 1])) first = 1;
		if(i == (length - 1) || FT_IS_SKIPPABLE(buffer[i + 1])) last = 1;

		if(ph[i * MAX_PPL] != FT_P_NONE) continue;

		if(FT_IS_SKIPPABLE(buffer[i])) {
			ph[i * MAX_PPL + 0] = FT_P_SPACE;

			n = 0;
		} else if((n = p_gen_4(ph, i, buffer, length, first, last, b_c)) != -1) {
		} else if((n = p_silent_e(ph, i, buffer, length, first, last, b_c)) != -1) {
		} else if((n = p_special_2(ph, i, buffer, length, first, last, b_c)) != -1) {
		} else if((n = p_gen_2(ph, i, buffer, length, first, last, b_c)) != -1) {
		} else if((n = p_vowel_2(ph, i, buffer, length, first, last, b_c)) != -1) {
		} else if((n = p_soft(ph, i, buffer, length, first, last, b_c)) != -1) {
		} else if((n = p_special_1(ph, i, buffer, length, first, last, b_c)) != -1) {
		} else if((n = p_gen_1(ph, i, buffer, length, first, last, b_c)) != -1) {
		} else if((n = p_vowel_1(ph, i, buffer, length, first, last, b_c)) != -1) {
		}

#ifdef DEBUG
		if(n == -1) fprintf(stderr, "cannot find phoneme for '%c', character behind was '%c'\n", buffer[i], b_c);
#endif

		if(n != -1) i += n;
	}

#define SYNTH \
	if(ph[i] != FT_P_NONE && ph[i] != FT_P_IGNORE) { \
		ft_synth_main(handle, ph[i]); \
	}

#ifdef DEBUG
	fprintf(stderr, "phoneme of \"");
	fwrite(buffer, length, 1, stderr);
	fprintf(stderr, "\":");
	for(i = 0; i < length * MAX_PPL; i++) {
		if(ph[i] == FT_P_NONE) continue;

		fprintf(stderr, " %s", phon_str[ph[i]]);
		fflush(stderr);

		SYNTH;
	}
	fprintf(stderr, "\n");
#else
	for(i = 0; i < length * MAX_PPL; i++) {
		SYNTH;
	}
#endif

#undef SYNTH

	free(ph);
}
