/**********************************************************************
	"Copyright 1990-2026 Brian MacWhinney. Use is subject to Gnu Public License
	as stated in the attached "gpl.txt" file."
*/


#define CHAT_MODE 1
#include "cu.h"

#if !defined(UNX)
#define _main convort_main
#define call convort_call
#define getflag convort_getflag
#define init convort_init
#define usage convort_usage
#endif

#define IS_WIN_MODE FALSE
#include "mul.h"

#define DICNAME "ort.cut"
#define PERIOD 50

extern struct tier *defheadtier;
extern char OverWriteFile;

struct rtiers {
	char speaker[SPEAKERLEN];		/* code descriptor field of the turn	*/
	AttTYPE attSp[SPEAKERLEN];		/* Attributes assiciated with speaker name	*/
	char line[UTTLINELEN+1];		/* text field of the turn		*/ // found uttlinelen
	AttTYPE attLine[UTTLINELEN+1];	/* Attributes assiciated with text*/
	char tuttline[UTTLINELEN+1];	/* working copy of the text of the turn	*/
	struct rtiers *nexttier;	/* pointer to the next utterance, if	 */
} ;				/* there is only 1 utterance, i.e. no	 */

struct words {
	char isFinalPos;
	char *word;
	char *toword;
	struct words *next;
} ;


static FNType dicname[256];
static char convort_FirstTime, isdup, isCheckENG;
static struct rtiers *rootTiers;
static struct words *head;

static void convort_cleanup(void) {
	struct words *t;

	while (head != NULL) {
		t = head;
		head = head->next;
		free(t->word);
		free(t->toword);
		free(t);
	}
}

static void convort_overflow() {
	fprintf(stderr,"convort: no more memory available.\n");
	convort_cleanup();
	cutt_exit(0);
}

static void convort_makenewsym(char *b, char *e, char isFinalPos) {
	struct words *nextone;

	if (head == NULL) {
		head = NEW(struct words);
		nextone = head;
	} else {
		nextone = head;
		while (nextone->next != NULL) nextone = nextone->next;
		nextone->next = NEW(struct words);
		nextone = nextone->next;
	}
	if (nextone == NULL)
		convort_overflow();
	nextone->next = NULL;
	nextone->isFinalPos = isFinalPos;
	nextone->toword = NULL;
	nextone->word = (char *)malloc(strlen(b)+1);
	if (nextone->word == NULL)
		convort_overflow();
	strcpy(nextone->word,b);
	nextone->toword = (char *)malloc(strlen(e)+1);
	if (nextone->toword == NULL)
		convort_overflow();
	strcpy(nextone->toword, e);
	if (isFinalPos)
		fprintf(stderr, "From \"%s\" to \"%s\" final word position\n", nextone->word, nextone->toword);
	else
		fprintf(stderr, "From \"%s\" to \"%s\"\n", nextone->word, nextone->toword);
}

static void convort_readdict(void) {
	FILE *fdic;
	char line[1024], *b, *e, isFinalPos;
	FNType mFileName[FNSize];

	if (dicname[0] == EOS) {
		uS.str2FNType(dicname, 0L, DICNAME);
	}
	if ((fdic=OpenGenLib(dicname,"r",TRUE,TRUE,mFileName)) == NULL) {
		fputs("Can't open either one of the changes files:\n",stderr);
		fprintf(stderr,"\t\"%s\", \"%s\"\n", dicname, mFileName);
		cutt_exit(0);
	}
	while (fgets_cr(line, 1024, fdic)) {
		if (uS.isUTF8(line) || uS.isInvisibleHeader(line))
			continue;
		if (line[0] == ';' && line[1] == ' ')
			continue;
		if (line[0] == '#' && line[1] == ' ')
			continue;
		if (strncmp(line, "*FINAL_WORD_POS*", 16) == 0) {
			isFinalPos = TRUE;
			strcpy(line, line+16);
		} else
			isFinalPos = FALSE;
		uS.remFrontAndBackBlanks(line);
		if (line[0] == EOS)
			continue;
		b = line;
		for (e=line; *e != ' ' && *e != '\t' && *e != '\n' && *e != EOS; e++) ;
		if (*e != EOS) {
			*e = EOS;
			for (e++; *e == ' ' || *e == '\t' || *e == '\n'; e++) ;
		}
		convort_makenewsym(b, e, isFinalPos);
	}
	fclose(fdic);
	fprintf(stderr,"\n");
}

void init(char f) {
	if (f) {
		stout = FALSE;
		*dicname = EOS;
		isdup = FALSE;
		convort_FirstTime = TRUE;
		isCheckENG = FALSE;
		OverWriteFile = TRUE;
		FilterTier = 0;
		LocalTierSelect = TRUE;
		head = NULL;
		onlydata = 1;
	} else if (convort_FirstTime) {
		if (isdup == FALSE)
			convort_readdict();
		convort_FirstTime = FALSE;
	}
}

void usage() {
	puts("CONVORT changes one orthography to other one.");
	printf("Usage: convort [cF %s] filename(s)\n", mainflgs());
	printf("+cF: dictionary file (Default %s) \n", DICNAME);
	printf("+d : only duplicate speaker tier on %%ort tier\n");
	mainusage(FALSE);
	puts("Dictionary file format: \"from string\" \"to string\"");
	puts("\nExample:");
	puts("\tconvort +cFileName.cut *.cha");
	cutt_exit(0);
}

CLAN_MAIN_RETURN main(int argc, char *argv[]) {
	isWinMode = IS_WIN_MODE;
	chatmode = CHAT_MODE;
	CLAN_PROG_NUM = CONVORT;
	OnlydataLimit = 0;
	UttlineEqUtterance = FALSE;
	bmain(argc,argv,NULL);
	convort_cleanup();
}

static int isWordEng(char *line, int pos) {
	if (pos == 0 || (pos > 1 && isSpace(line[pos-1]))) {
		for (; !isSpace(line[pos]) && line[pos] != '\n' && line[pos] != EOS; pos++) {
			if (line[pos] == '@' && line[pos+1] == 's') {
				for (pos++; !isSpace(line[pos]) && line[pos] != '\n' && line[pos] != EOS; pos++) ;
				return(pos);
			}
		}
	}
	return(0);
}

static int change(char *line, AttTYPE *att, int pos, int wlen, int tlen, char *to_word) {
	int i;

	if (tlen > wlen) {
		int num;
		num = tlen - wlen;
		for (i=strlen(line); i >= pos; i--) {
			line[i+num] = line[i];
			att[i+num] = att[i];
		}
	} else if (tlen < wlen) {
		if (*to_word == EOS)
			att_cp(0, line+pos+tlen, line+pos+wlen, att+pos+tlen, att+pos+wlen);
		else
			att_cp(0, line+pos+tlen-1, line+pos+wlen-1, att+pos+tlen-1, att+pos+wlen-1);
	}

	for (; *to_word != EOS; pos++) {
		line[pos] = *to_word++;
		if (pos > 0)
			att[pos] = att[pos-1];
	}
	return(pos-1);
}	

static char isRightPos(char *line, int wlen, struct words *nextone) {
	if (nextone->isFinalPos == TRUE) {
		for (; *line != EOS && !isSpace(*line) && *line != '@' && wlen > 0; wlen--)
			line++;
		if (wlen > 0)
			return(FALSE);
		else if (*line == EOS || isSpace(*line) || *line == '@')
			return(TRUE);
		return(FALSE);
	}
	return(TRUE);
}

static long FindAndChange(char *line, AttTYPE *att, long isFound) {
	int pos;
	struct words *nextone;
	int wlen, tlen;
	char isOneMatch;

	pos = 0;
	nextone = head;
	while (line[pos] != EOS) {
		if (line[pos] == (char)0xE2 && line[pos+1] == (char)0x80) {
			pos += 3;
		} else if (line[pos] == '#' || line[pos] == '_') {
			pos++;
		} else if (line[pos] == HIDEN_C) {
			for (pos++; line[pos] != HIDEN_C && line[pos] != '\n' && line[pos] != EOS; pos++) ;
			if (line[pos] == HIDEN_C)
				pos++;
		} else if (line[pos] == '@') {
			for (pos++; !isSpace(line[pos]) && line[pos] != '\n' && line[pos] != EOS; pos++) ;
		} else if (line[pos] == '&' && (line[pos+1] == '=' || line[pos+1] == '~')) {
			for (pos++; !isSpace(line[pos]) && line[pos] != '\n' && line[pos] != EOS; pos++) ;
		} else if (line[pos] == '[' && (line[pos+1] == '^' || line[pos+1] == '%' || line[pos+1] == '*')) {
			for (pos++; line[pos] != ']' && line[pos] != EOS; pos++) ;
		} else if ((tlen=isWordEng(line, pos)) != 0) {
			pos = tlen;
		} else {
			isOneMatch = FALSE;
			nextone = head;
			while (nextone != NULL) {
				wlen = strlen(nextone->word);
				if (strncmp(line+pos, nextone->word, wlen) == 0 && isRightPos(line+pos, wlen, nextone)) {
					tlen = strlen(nextone->toword);
					if (tlen == 0) {
						strcpy(line+pos, line+pos+wlen);
						pos--;
					} else if (wlen == 1 && tlen == 1) {
						line[pos] = nextone->toword[0];
					} else {
						pos = change(line, att, pos, wlen, tlen, nextone->toword);
					}
					isFound++;
					isOneMatch = TRUE;
					break;
				} else
					nextone = nextone->next;
			}
			if (isOneMatch == FALSE) {
				if (line[pos] == '.' || line[pos] == ',' || line[pos] == '?' || line[pos] == '!' ||
					line[pos] == '(' || line[pos] == ')' || line[pos] == '[' || line[pos] == ']' ||
					line[pos] == '<' || line[pos] == '>' || line[pos] == '-' || 
					line[pos] == '~' || line[pos] == '+' || line[pos] == '%' || line[pos] == '"' ||
					line[pos] == ':' || line[pos] == '^' || line[pos] == '/' || line[pos] == '0' ||
					line[pos] == '\n' ||isSpace(line[pos])) {
				} else {
					fprintf(stderr,"\rFailed to match letter starting at \"%s\"\n", line+pos);
				}
			}
			pos++;
		}
	}
	return(isFound);
}

static struct rtiers *FreeUpTiers(struct rtiers *p) {
	struct rtiers *t;

	while (p != NULL) {
		t = p;
		p = p->nexttier;
		free(t);
	}
	return(NULL);
}

static struct rtiers *AddToTiers(struct rtiers *p, char *sp, AttTYPE *attSp, char *line, AttTYPE *attLine) {
	if (p == NULL) {
		if ((p=NEW(struct rtiers)) == NULL) out_of_mem();
		att_cp(0, p->speaker, sp, p->attSp, attSp);
		att_cp(0, p->line, line, p->attLine, attLine);
		p->nexttier = NULL;
	} else
		p->nexttier = AddToTiers(p->nexttier, sp, attSp, line, attLine);
	return(p);
}

static long processSpTier(long isFound, char *speaker, AttTYPE *attSp, char *line, AttTYPE *attLine) {
	int j;

	uS.remblanks(line);
	att_cp(0, spareTier1, line, tempAtt, attLine);
	if (!nomap) {
		for (j=0; line[j]; j++) {
			if (MBF) {
				if (my_CharacterByteType(line, (short)j, &dFnt) == 0)
					line[j] = (char)tolower((unsigned char)line[j]);
			} else {
				line[j] = (char)tolower((unsigned char)line[j]);
			}
		}
	}
	if (isdup == FALSE)
		isFound = FindAndChange(line, attLine, isFound);
	else
		isFound++;
	printout(speaker,line,attSp,attLine, 0);
	printout("%ort:", spareTier1, NULL, tempAtt, 0);

	return(isFound);
}

void call() {
	int  i;
	char engFound;
	long tlineno = 0L;
	long isFound;
	struct rtiers *rt;
	
	engFound = FALSE;
	rootTiers = NULL;
	isFound = 0L;
	currentatt = 0;
	currentchar = (char)getc_cr(fpin, &currentatt);
	while (getwholeutter()) {
		tlineno = tlineno + 1L;
		if (tlineno % PERIOD == 0)
			fprintf(stderr,"\r%ld ",tlineno);
		for (i=0; isSpace(utterance->line[i]); i++) ;
		if (utterance->speaker[0] == '@') {
			if (rootTiers != NULL) {
				if (engFound == TRUE) {
					isFound = processSpTier(isFound, rootTiers->speaker, rootTiers->attSp, rootTiers->line, rootTiers->attLine);
				} else {
					printout(rootTiers->speaker,rootTiers->line,rootTiers->attSp,rootTiers->attLine, 0);
				}
				for (rt=rootTiers->nexttier; rt != NULL; rt=rt->nexttier) {
					printout(rt->speaker,rt->line,rt->attSp,rt->attLine, 0);
				}
				rootTiers = FreeUpTiers(rootTiers);
				engFound = FALSE;
			}
			printout(utterance->speaker,utterance->line+i,utterance->attSp,utterance->attLine+i, 0);
		} else if (utterance->speaker[0] == '*') {
			if (isCheckENG) {
				if (rootTiers != NULL) {
					if (engFound == TRUE) {
						isFound = processSpTier(isFound, rootTiers->speaker, rootTiers->attSp, rootTiers->line, rootTiers->attLine);
					} else {
						printout(rootTiers->speaker,rootTiers->line,rootTiers->attSp,rootTiers->attLine, 0);
					}
					for (rt=rootTiers->nexttier; rt != NULL; rt=rt->nexttier) {
						printout(rt->speaker,rt->line,rt->attSp,rt->attLine, 0);
					}
					rootTiers = FreeUpTiers(rootTiers);
					engFound = FALSE;
				}
				rootTiers = AddToTiers(rootTiers, utterance->speaker, utterance->attSp, utterance->line+i, utterance->attLine+i);
			} else {
				isFound = processSpTier(isFound, utterance->speaker, utterance->attSp, utterance->line+i, utterance->attLine+i);
			}
		} else if (isCheckENG) {
			if (uS.partcmp(utterance->speaker, "%eng:", FALSE, FALSE))
				engFound = TRUE;
			rootTiers = AddToTiers(rootTiers, utterance->speaker, utterance->attSp, utterance->line+i, utterance->attLine+i);
		} else
			printout(utterance->speaker,utterance->line+i,utterance->attSp,utterance->attLine+i, 0);
	}
	if (rootTiers != NULL) {
		isFound = processSpTier(isFound, rootTiers->speaker, rootTiers->attSp, rootTiers->line, rootTiers->attLine);
		for (rt=rootTiers->nexttier; rt != NULL; rt=rt->nexttier) {
			printout(rt->speaker,rt->line,rt->attSp,rt->attLine, 0);
		}
		rootTiers = FreeUpTiers(rootTiers);
		engFound = FALSE;
	}
	fprintf(stderr,"\n");
#ifndef UNX
	if (isFound == 0L && fpout != stdout && !WD_Not_Eq_OD) {
		fprintf(stderr,"**- NO changes made in this file\n");
		if (!replaceFile) {
			fclose(fpout);
			fpout = NULL;
			if (unlink(newfname))
				fprintf(stderr, "Can't delete output file \"%s\".", newfname);
		}
	} else
#endif
	if (isFound > 0L)
		fprintf(stderr,"**+ %ld changes made in this file\n", isFound);
	else
		fprintf(stderr,"**- NO changes made in this file\n");
}

void getflag(char *f, char *f1, int *i) {
	f++;
	switch(*f++) {
		case 'c':
				if (*f)
					uS.str2FNType(dicname, 0L, getfarg(f,f1,i));
				break;
		case 'd':
				isdup = TRUE;
				nomap = TRUE;
				no_arg_option(f);
				break;
		case 'e':
				isCheckENG = TRUE;
				no_arg_option(f);
				break;
		default:
				maingetflag(f-2,f1,i);
				break;
	}
}
