/*************************************************************************
* Bulgarian-English Dictionary
* Copyright (C) 2000  Radostin Radnev <radnev@yahoo.com>
*
* 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*************************************************************************/

#include <string.h>
#include <ctype.h>

#include "database.h"
#include "translator.h"



//=== Class Translator ===================================================
// Written by Radostin Radnev - radnev@yahoo.com
// $Id: translator.cpp,v 1.13 2001/03/11 22:41:46 radnev Exp $
//
// This class represents Translator.
// It holds databases and perform some text translation operations
//========================================================================



// Declaration about max data and word length
const int Translator::MAX_DATA_LEN = 150000;

// Declaration about dictinonary constants
const int Translator::ENG_BUL = 1;
const int Translator::BUL_ENG = 2;

// Declarations about Latin Input and Output
const char Translator::ENG_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ~`[{]}|\\:;'\"";
const char Translator::BUL_CHARS[] = "ÀÁÖÄÅÔÃÕÈÉÊËÌÍÎÏßÐÑÒÓÆÂÜÚÇ××ØØÙÙÞÞÛÛÝ";
const char Translator::LATIN_OUTPUT_CHARS[32][4] = {
	"a", "b", "v", "g", "d", "e", "j", "z",
	"i", "j", "k", "l", "m", "n", "o", "p",
	"r", "s", "t", "u", "f", "h", "c", "ch",
	"sh", "sht", "y", "x", "x", "ei", "iu", "ia"};

// Declarations about English Suffixs
const char Translator::ENG_SUFFIXS[3][4] = {"S", "ING", "ED"};

// Declaration about HTML decoration
const char Translator::BOLD_START[]          = "<B>";
const char Translator::BOLD_END[]            = "</B>";
const char Translator::BREAK_LINE[]          = "<BR>";
const char Translator::A_S_HL_HTML_START[]   = "<FONT color=\"red\">";
const char Translator::A_S_HL_HTML_END[]     = "</FONT>";
const char Translator::A_S_HL_NORMAL_START[] = "<----*";
const char Translator::A_S_HL_NORMAL_END[]   = "*---->";

// Declaration about start and stop bold decoration
const char Translator::NON_ALPHA_CHARS[] = " ,.;:!?'-/\"\\";

// Declaration about word delimiter test
const char Translator::TEST_DELIMITER = ',';


//=== Constructor ========================================================
// Set defaults variables
// Allocate memory
//========================================================================
Translator::Translator(const int wordLength, const int wordNumber) {
	maxWordLength             = wordLength;
	maxWordNumber             = wordNumber;
	separateMeanings          = true;
	latinOutput               = false;
	latinInput                = false;
	boldDecoration            = true;
	htmlOutput                = true;
	advancedSearchState       = false;
	advancedSearchHighlight   = true;
	advancedSearchWholeWord   = true;
	advancedSearchExactPhrase = false;
	currentDictionary         = ENG_BUL;
	testDictionary            = ENG_BUL;
	testLevel                 = 0;
	dataBuffer  = new char[MAX_DATA_LEN];
	dataBuffer2 = new char[MAX_DATA_LEN/10];
	wordBuffer  = new char[maxWordLength + 1];
	wordBuffer2 = new char[maxWordLength + 1];
	tempBuffer  = new char[(maxWordLength + 1)*maxWordNumber];
	wordPlus    = new bool[maxWordNumber];
} // End of Constructor



//=== Destructor =========================================================
// Free memory and dictionary objects
//========================================================================
Translator::~Translator() {
	delete [] dataBuffer;
	delete [] dataBuffer2;
	delete [] wordBuffer;
	delete [] wordBuffer2;
	delete [] tempBuffer;
	delete [] wordPlus;
	delete dicEB;
	delete dicBE;
} // End of Destructor



//=== Create Dictionary ==================================================
// Real construcor
// Call the same method of Database, and return same result
//========================================================================
bool Translator::createDictionary(const char *fileName, const int dictionary, const long fixedLastWordPointer = 0) {
	bool ret = false;
	switch (dictionary) {
		case ENG_BUL:
			dicEB = new Database();
			ret = dicEB->createDictionary(fileName, fixedLastWordPointer);
			currentDic = dicEB;
			break;
		case BUL_ENG:
			dicBE = new Database();
			ret = dicBE->createDictionary(fileName, fixedLastWordPointer);
			break;
	}
	return ret;
} // End of createDictionary



//=== To Lower Case ======================================================
// Transform characters to lower case work for bulgarian chars also
// Change buffer and return pointer to new word
//========================================================================
char *Translator::toLowerCase(const char *word, char *buf) {
	int i = 0;
	while (word[i] != '\0') {
		if (('A' <= word[i]) && (word[i] <= 'Z')) {
			buf[i] = tolower(word[i]);
		}
		else if (('À' <= word[i]) && (word[i] <= 'ß')) {
			buf[i] = word[i] - 'À' + 'à';
		}
		else {
			buf[i] = word[i];
		}
		i++;
	}
	buf[i] = '\0';
	return buf;
} // End of toLowerCase



//=== To Upper Case ======================================================
// Transform characters to upper case work for bulgarian chars also
// Change buffer and return pointer to new word
//========================================================================
char *Translator::toUpperCase(const char *word, char *buf) {
	int i = 0;
	while (word[i] != '\0') {
		if (('a' <= word[i]) && (word[i] <= 'z')) {
			buf[i] = toupper(word[i]);
		}
		else if (('à' <= word[i]) && (word[i] <= 'ÿ')) {
			buf[i] = word[i] + 'À' - 'à';
		}
		else {
			buf[i] = word[i];
		}
		i++;
	}
	buf[i] = '\0';
	return buf;
} // End of toUpperCase



//=== To Legal Dictionary Word ===========================================
// Transform word to legal dictionary word
// Only letters and only upper case
// Change buffer and return pointer to new word
//========================================================================
char *Translator::toLegalDictionaryWord(const char *word, char *buf) {
	int i = 0;
	int j = 0;
	while (word[i] != '\0') {
		if (('a' <= word[i]) && (word[i] <= 'z')) {
			buf[j] = toupper(word[i]);
			j++;
		}
		else if (('à' <= word[i]) && (word[i] <= 'ÿ')) {
			buf[j] = word[i] + 'À' - 'à';
			j++;
		}
		else if ((('A' <= word[i]) && (word[i] <= 'Z')) || (('À' <= word[i]) && (word[i] <= 'ß'))) {
			buf[j] = word[i];
			j++;
		}
		i++;
	}
	buf[j] = '\0';
	return buf;
} // End of toLegalDictionaryWord



//=== Trim Word ==========================================================
// Removing leading and ending spaces from word
// Change buffer and return pointer to new word
//========================================================================
char *Translator::trimWord(const char *word, char *buf) {
	bool add = false;
	int i = 0;
	int j = 0;
	while (word[i] != '\0') {
		if ((word[i] != ' ') || (add)) {
			add = true;
			buf[j] = word[i];
			j++;
		}
		i++;
	}
	buf[j] = '\0';
	while ((j > 0) && (buf[j - 1] == ' ')) {
		j--;
		buf[j] = '\0';
	}
	return buf;
} // End of trimWord



//=== Is English Word Without Suffix =====================================
// Return true if passed word end with some suffix for example s, ed ...
// Change buffer and return pointer to new word
//========================================================================
bool Translator::isEnglishWordWithoutSuffix(const char *word, char *buf) {
	int j, i = 0;
	bool ret = false;
	char *p;
	strcpy(buf, word);
	while (i < 3) {
		p = strrchr(buf, ENG_SUFFIXS[i][0]);
		if (p != NULL) {
			j = 0;
			while ((*p != '\0') && (ENG_SUFFIXS[i][j] != '\0') && (*p == ENG_SUFFIXS[i][j])) {
				p++;
				j++;
			}
			if ((*p == '\0') && (ENG_SUFFIXS[i][j] == '\0')) {
				p = strrchr(buf, ENG_SUFFIXS[i][0]);
				*p = '\0';
				ret = (strlen(buf) != 0);
				break;
			}
		}
		i++;
	}
	return ret;
} // End of isEnglishWordWithoutSuffix



//=== Is Latin Input =====================================================
// Return true if passed word is legal bulgarian word as LATIN_INPUT
// Return pointer to new word that is legal dictionary word
//========================================================================
bool Translator::isLatinInput(const char *word, char *buf, const bool ignoreSpace = false) {
	int i = 0;
	char *p;
	while (word[i] != '\0') {
		if ((ignoreSpace) && (word[i] == ' ')) {
			buf[i] = word[i];
		}
		else {
			p = strchr(ENG_CHARS, word[i]);
			if (p != NULL) {
				buf[i] = BUL_CHARS[p - ENG_CHARS];
			}
			else {
				return false;
			}
		}
		i++;
	}
	buf[i] = '\0';
	return true;
} // End of isLatinInput



//=== To Latin ===========================================================
// Transfer Bulgarian word to legal input word for LATIN_INPUT or
// to a normal latin output
// Change buffer and return pointer to new word
//========================================================================
char *Translator::toLatin(const char *word, char *buf, const bool legalLatinInput = true) {
	int i = 0;
	int j = 0;
	char *p;
	char c;
	while ((c = word[i]) != '\0') {
		if (legalLatinInput) {
			p = strchr(BUL_CHARS, c);
			if (p != NULL) {
				buf[j] = ENG_CHARS[p - BUL_CHARS];
				j++;
			}
		}
		else {
			if (('À' <= c) && (c <= 'ß')) {
				strcpy(buf + j, LATIN_OUTPUT_CHARS[c - 'À']);
				j += strlen(LATIN_OUTPUT_CHARS[c - 'À']);
			}
			else {
				buf[j] = c;
				j++;
			}
		}
		i++;
	}
	buf[j] = '\0';
	return buf;
} // End of toLatin



//=== Go To Next Word ====================================================
// Call the same method of Database, and return same result
//========================================================================
bool Translator::goToNextWord() {
	return currentDic->goToNextWord();
} // End of goToNextWord



//=== Get Word ===========================================================
// Call the same method of Database, and return same result
// But perform some operations
//========================================================================
char *Translator::getWord(const bool lowerCase = true, const bool legalLatinInput = true) {
	char *ret = NULL;
	switch (currentDictionary) {
		case ENG_BUL:
			ret = dicEB->getWord();
			if (lowerCase) {
				ret = toLowerCase(ret, wordBuffer);
			}
			break;
		case BUL_ENG:
			ret = dicBE->getWord();
			if ((latinInput) || (latinOutput)) {
				ret = toLatin(ret, wordBuffer, legalLatinInput);
				if ((!lowerCase) && (!legalLatinInput)) {
					ret = toUpperCase(ret, wordBuffer);
				}
			}
			else {
				if (lowerCase) {
					ret = toLowerCase(ret, wordBuffer);
				}
			}
			break;
	}
	return ret;
} // End of getWord



//=== Find Word ==========================================================
// Call the same method of Database, and return same result
// But perform some transformation of data input and output data
//========================================================================
bool Translator::findWord(const char *word, char **result) {
	char *p;
	bool ret = false;
	if (((latinInput) || (latinOutput)) && (isLatinInput(word, wordBuffer)) && (strlen(wordBuffer) > 0)) {
		currentDictionary = BUL_ENG;
		currentDic = dicBE;
	}
	else {
		toLegalDictionaryWord(word, wordBuffer);
		if (strlen(wordBuffer) == 0) {
			dataBuffer[0] = '\0';
			*result = dataBuffer;
			return false;
		}
		setCurrentDictionary(wordBuffer[0]);
	}
	switch (currentDictionary) {
		case ENG_BUL:
			ret = dicEB->findWord(wordBuffer, &p);
			if ((!ret) && (isEnglishWordWithoutSuffix(wordBuffer, wordBuffer2)) && (strlen(wordBuffer2) > 1)) {
				ret = dicEB->findWord(wordBuffer2, &p);
				if (!ret) {
					ret = dicEB->findWord(wordBuffer, &p);
				}
			}
			break;
		case BUL_ENG:
			ret = dicBE->findWord(wordBuffer, &p);
			break;
	}
	*result = transformResult(p);
	return ret;
} // End of findWord



//=== Transform Result ===================================================
// Perform some transformation operations depending from options
// Store result in dataBuffer and return pointer to it
//========================================================================
char *Translator::transformResult(const char *result) {
	int j, i, m, n;
	char *b;
	strcpy(dataBuffer2, result);
	if ((advancedSearchState) && (advancedSearchHighlight)) {
		for (i = 0; i < wordNumber; i++) {
			if (wordPlus[i]) {
				toUpperCase(dataBuffer2, dataBuffer);
				j = strlen(dataBuffer2);
				b = dataBuffer;
				n = 0;
				while ((b = search(b, tempBuffer + (maxWordLength + 1)*i, advancedSearchWholeWord)) != NULL) {
					m = b - dataBuffer + 2*n;
					memmove(dataBuffer2 + m + 1, dataBuffer2 + m, j - m + 2 + 2*n);
					dataBuffer2[m] = '|';
					m += strlen(tempBuffer + (maxWordLength + 1)*i) + 1;
					memmove(dataBuffer2 + m + 1, dataBuffer2 + m, j - m + 2 + 2*n);
					dataBuffer2[m] = '|';
					b += strlen(tempBuffer + (maxWordLength + 1)*i);
					n++;
				}
			}
		}
	}
	bool currentBold = false;
	bool currentColor = false;
	bool firstLineBreak = true;
	char c;
	i = 0;
	j = 0;
	while ((c = dataBuffer2[i]) != '\0') {
		if (c == '\n') {
			if (htmlOutput) {
				j += appendString(BREAK_LINE, j);
				if ((separateMeanings) && (!firstLineBreak)) {
					j += appendString(BREAK_LINE, j);
				}
			}
			else {
				dataBuffer[j] = '\n';
				j++;
				if ((separateMeanings) && (!firstLineBreak)) {
					dataBuffer[j] = '\n';
					j++;
				}
			}
			if (firstLineBreak) {
				firstLineBreak = false;
			}
		}
		else if ((advancedSearchState) && (advancedSearchHighlight) && (c == '|')) {
			if (htmlOutput) {
				// Fix the problem with cross tags
				if (currentBold) {
					j += appendString(BOLD_END, j);
				}
				// Append color end or color start tag
				j += appendString((currentColor ? A_S_HL_HTML_END : A_S_HL_HTML_START), j);
				// Fix the problem with cross tags
				if (currentBold) {
					// Fix the problem with Qt widget that doesn't
					// display space properly after the tag
					// "<B> text" is buggy and converted to " <B> text"
					if ((currentColor) && (dataBuffer2[i + 1] == ' ')) {
						dataBuffer[j] = ' ';
						j++;
					}
					j += appendString(BOLD_START, j);
				}
			}
			else {
				j += appendString((currentColor ? A_S_HL_NORMAL_END : A_S_HL_NORMAL_START), j);
			}
			currentColor = !currentColor;
		}
		else {
			if ((htmlOutput) && (boldDecoration)) {
				if (currentBold) {
					if (!((('A' <= c) && (c <= 'Z')) || (('a' <= c) && (c <= 'z'))) && (strchr(NON_ALPHA_CHARS, c) == NULL)) {
						j += appendString(BOLD_END, j);
						currentBold = false;
					}
				}
				else {
					if ((('A' <= c) && (c <= 'Z')) || (('a' <= c) && (c <= 'z'))) {
						j += appendString(BOLD_START, j);
						currentBold = true;
					}
				}
			}
			if (latinOutput) {
				if (('À' <= c) && (c <= 'ß')) {
					toUpperCase(LATIN_OUTPUT_CHARS[c - 'À'], wordBuffer2);
					j += appendString(wordBuffer2, j);
				}
				else if (('à' <= c) && (c <= 'ÿ')) {
					j += appendString(LATIN_OUTPUT_CHARS[c - 'à'], j);
				}
				else {
					dataBuffer[j] = c;
					j++;
				}
			}
			else {
				dataBuffer[j] = c;
				j++;
			}
		}
		i++;
	}
	if (currentBold) {
		j += appendString(BOLD_END, j);
	}
	if (htmlOutput) {
		j += appendString(BREAK_LINE, j);
	}
	else {
		dataBuffer[j] = '\n';
		j++;
	}
	dataBuffer[j] = '\0';
	return dataBuffer;
} // End of transformResult



//=== Append String ======================================================
// Append passed data to the dataBuffer and return length of append data
//========================================================================
int Translator::appendString(const char *data, const int currentPointer) {
	int i = 0;
	while (data[i] != '\0') {
		dataBuffer[currentPointer + i] = data[i];
		i++;
	}
	return i;
} // End of appendString



//=== Gets Result ========================================================
// Gets and transform result of last search (move word pointer) operation
//========================================================================
char *Translator::getResult() {
	return transformResult(currentDic->getResult());
} // End of getResult



//=== Gets Next Random Word ==============================================
// Gets next random word, perform some translations
//========================================================================
char *Translator::getNextRandomWord() {
	do {
		currentDic->getNextRandomWord();
	} while ((signed)strlen(currentDic->getResult()) < testLevel);
	return getWord(false, false);
} // End of getNextRandomWord



//=== Sets Test Dictionary ===============================================
// Sets test dictionary (used only for exam)
//========================================================================
void Translator::setTestParameters(const int dictionary, const int level) {
	testDictionary = dictionary;
	currentDictionary = dictionary;
	testLevel = level;
	switch (currentDictionary) {
		case ENG_BUL:
			currentDic = dicEB;
			break;
		case BUL_ENG:
			currentDic = dicBE;
			break;
	}
} // End of setTestDictionary



//=== Tests Word =========================================================
// Tests the user input and return true if user guess word
//========================================================================
bool Translator::testWord(const char *word) {
	char *p;
	char *b;
	int l = 0;
	if (strlen(word) == 0) {
		return false;
	}
	// Get result and make upper case
	toUpperCase(currentDic->getResult(), dataBuffer);
	// Analize word
	strcpy(tempBuffer, word);
	p = tempBuffer - 1;
	while (p != NULL) {
		b = p + 1;
		p = strchr(b, TEST_DELIMITER);
		l = (p == NULL ? strlen(b) : p - b);
		if (l >= maxWordLength) {
			l = maxWordLength - 1;
		}
		strncpy(wordBuffer2, b, l);
		wordBuffer2[l] = '\0';
		trimWord(wordBuffer2, wordBuffer2);
		if (((latinInput) || (latinOutput)) && (isLatinInput(wordBuffer2, wordBuffer)) && (strlen(wordBuffer) > 0)) {
			currentDictionary = BUL_ENG;
			currentDic = dicBE;
		}
		else {
			toUpperCase(wordBuffer2, wordBuffer);
			setCurrentDictionary(wordBuffer[0]);
		}
		currentDictionary = (currentDictionary == ENG_BUL ? BUL_ENG : ENG_BUL);
		currentDic = (currentDictionary == ENG_BUL ? dicEB : dicBE);
		if (currentDictionary != testDictionary) {
			currentDictionary = testDictionary;
			currentDic = (currentDictionary == ENG_BUL ? dicEB : dicBE);
		}
		else if (wordBuffer[0] != '\0') {
			if (search(dataBuffer, wordBuffer, true) != NULL) {
				return true;
			}
		}
	}
	return false;
} // End of testWord



//=== Set Advanced Search Text ===========================================
// Sets advanced search text
// Return true if text parsed without problems
//========================================================================
bool Translator::setAdvancedSearchText(const char *word) {
	int i = 0;
	int j = 0;
	wordNumber = 0;
	bool string = false;
	bool closeWord = false;
	char c;
	char oldc = ' ';
	bool wPlus = true;
	while ((c = word[i]) != '\0') {
		if (c == '"') {
			string = !string;
			closeWord = true;
		}
		else if (string) {
			if (!((c == ' ') && (oldc == ' '))) {
				tempBuffer[(maxWordLength + 1)*wordNumber + j] = c;
				j++;
			}
		}
		else if ((c == '+') || (c == '-') || ((c == ' ') && (!advancedSearchExactPhrase))) {
			closeWord = true;
		}
		else {
			if (!((c == ' ') && (oldc == ' '))) {
				tempBuffer[(maxWordLength + 1)*wordNumber + j] = c;
				j++;
			}
		}
		if (closeWord) {
			if ((j > 1) || ((j == 1) && (tempBuffer[(maxWordLength + 1)*wordNumber] != ' '))) {
				tempBuffer[(maxWordLength + 1)*wordNumber + j] = '\0';
				wordPlus[wordNumber] = wPlus;
				wordNumber++;
				if (wordNumber == maxWordNumber) {
					break;
				}
			}
			if (c == '-') {
				wPlus = false;
			}
			else if (c == '+') {
				wPlus = true;
			}
			else if ((c == ' ') && (j > 0)) {
				wPlus = true;
			}
			j = 0;
		}
		closeWord = false;
		oldc = c;
		i++;
	}
	if (j > 0) {
		if ((j > 1) || ((j == 1) && (tempBuffer[(maxWordLength + 1)*wordNumber] != ' '))) {
			tempBuffer[(maxWordLength + 1)*wordNumber + j] = '\0';
			wordPlus[wordNumber] = wPlus;
			wordNumber++;
		}
	}
	if (wordNumber > 0) {
		for (i = 0; i < wordNumber; i++) {
			trimWord(tempBuffer + (maxWordLength + 1)*i, tempBuffer + (maxWordLength + 1)*i);
			if (((latinInput) || (latinOutput)) && (isLatinInput(tempBuffer + (maxWordLength + 1)*i, wordBuffer2, true))) {
				strcpy(tempBuffer + (maxWordLength + 1)*i, wordBuffer2);
			}
			toUpperCase(tempBuffer + (maxWordLength + 1)*i, tempBuffer + (maxWordLength + 1)*i);
		}
		advancedSearchState = true;
		setCurrentDictionary(tempBuffer[0]);
		firstTimeAdvancedSearch = true;
		firstDataBaseAdvancedSearch = true;
		return true;
	}
	return false;
} // End of setAdvancedSearchText



//=== Search Next Word ===================================================
// Searchs for next word that mathc the specified advanced search text
// Return true if find next word
//========================================================================
bool Translator::searchNextWord() {
	int i;
	if (firstTimeAdvancedSearch) {
		currentDic->goToFirstWord();
		firstTimeAdvancedSearch = false;
	}
	else {
		if (!currentDic->goToNextWord()) {
			return false;
		}
	}
	while (true) {
		do {
			toUpperCase(currentDic->getResult(), dataBuffer);
			for (i = 0; i < wordNumber; i++) {
				if (((wordPlus[i]) && (search(dataBuffer, tempBuffer + (maxWordLength + 1)*i, advancedSearchWholeWord) == NULL))
					|| ((!wordPlus[i]) && (search(dataBuffer, tempBuffer + (maxWordLength + 1)*i, advancedSearchWholeWord) != NULL))) {
					break;
				}
			}
			if (i == wordNumber) {
				return true;
			}
		} while (currentDic->goToNextWord());
		if (firstDataBaseAdvancedSearch) {
			firstDataBaseAdvancedSearch = false;
			currentDictionary = (currentDictionary == ENG_BUL ? BUL_ENG : ENG_BUL);
			currentDic = (currentDictionary == ENG_BUL ? dicEB : dicBE);
			currentDic->goToFirstWord();
		}
		else {
			break;
		}
	}
	return false;
} // End of searchNextWord



//=== Set Current Dictionayr =============================================
// Determine dictionary (ENG_BUL or BUL_ENG)
//========================================================================
void Translator::setCurrentDictionary(const char c) {
	currentDictionary = (!(('À' <= c) && (c <= 'ß'))) ? ENG_BUL : BUL_ENG;
	currentDic = (currentDictionary == ENG_BUL ? dicEB : dicBE);
} // End of setCurrentDictionary



//=== Search =============================================================
// Searchs word in text and return pointer to first occurance or NULL
//========================================================================
char *Translator::search(char *text, const char *word, const bool exactSearch) {
	if (exactSearch) {
		char *b = text - 1;
		while (b != NULL) {
			b++;
			b = strstr(b, word);
			if ((b != NULL) &&
				((b == text) || ((b > text) &&
				(!isAlphaChar(text[b - text - 1])))) &&
				(((b - text + strlen(word) < strlen(text)) &&
				(!isAlphaChar(text[b - text + strlen(word)])) ||
				(b - text + strlen(word) == strlen(text))))) {
				return b;
			}
		}
	}
	else {
		return strstr(text, word);
	}
	return NULL;
} // End of search



//=== Is Alpha Char ======================================================
// Return true if char is Alpha (Bulgarian or English letter)
//========================================================================
bool Translator::isAlphaChar(const char c) {
	return ((('A' <= c) && (c <= 'Z')) || (('À' <= c) && (c <= 'ß')) || (('a' <= c) && (c <= 'z')) || (('à' <= c) && (c <= 'ÿ')));
} // End of isAlphaChar



//=== Extract Text =======================================================
// Extracts content from HTML formated text
// Return pointer to extracted text
// This function is useful for clipboard operation
//========================================================================
char *Translator::extractText(const char *text) {
	int j = 0;
	int i = 0;
	bool tag = false;
	char c;
	while (((c = text[i]) != '\0') && (j < MAX_DATA_LEN)) {
		if (c == '<') {
			tag = true;
			if (strncmp(text + i, BREAK_LINE, strlen(BREAK_LINE)) == 0) {
				dataBuffer[j] = '\n';
				j++;
			}
		}
		else if (c == '>') {
			tag = false;
		}
		else if (!tag) {
			dataBuffer[j] = c;
			j++;
		}
		i++;
	}
	dataBuffer[j] = '\0';
	return dataBuffer;
} // End of extractText
