/* Prool foolish translator (English to Russian interpreter) and others Prool's functions

   Prool here: http://prool.kharkov.org http://mud.kharkov.org

   prooltin repo: https://gitlab.com/prool/prooltin

   E-mail proolix AT gmail . com

*/

#include <string.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <iconv.h>
#include <dirent.h>

#include "tintin.h"
#include "prool.h"

#define MAXBUF 4096
#define MAXLEN 4096
#define MAXWORD 200
#define MAXWORDLEN 60
#define MAX_SOCK_BUF 1024
#define PROOL_LEN 2048

#define TOTAL_LOG_FILENAME "prooltin.log"

char *muller_tr (char *input, char *out);
void poisk (char *in, char *out);

int muller_mode; // 1 - word translator enable. 0 - disable. 2 - bilingua

int tron;
int total_log;
int coder;
long int start_time;
long int startwatchtime;
int prool_loop_counter;
long int watchdog;
FILE *muller_fp;
int path_n;

char *path[3] ={
"slovar2.txt",
"/usr/share/prooltin/slovar2.txt",
"/usr/local/share/prooltin/slovar2.txt"};

char English [MAXWORD] [MAXWORDLEN];
char Russian [MAXWORD] [MAXWORDLEN];

char buffer [MAXBUF];
char recoded_str [MAXBUF];
char clipboard [MAXBUF];

void prool_ident(void)
{
printf("Mod by Prool. 2014-2025. http://prool.kharkov.org proolix@gmail.com\n");
}

char pressanykey()
{char c;
printf("---press any key---");

while(1)
	{
	c=getchar();
	if (c!=-1) return c;
	}
}

void ls(void)
{DIR *dir;
struct dirent *entry;
int i=0;
int counter=1;
char c;
int line=0;
int lines=24;
int file_no=-1;
struct stat struktura;

dir = opendir(".");

if (dir==0) {printf("Can't open current directory\n"); return;}

printf("\n");

//set_terminal_raw();

while(1)
	{
	entry=readdir(dir);
	if (entry==0) break;
	if (i++ == file_no) printf("%2i. %s%s%s", counter++, REVERSE, entry->d_name, NORM_COLOR);
	else printf("%2i. %s", counter++, entry->d_name);
	if (!stat(entry->d_name, &struktura))
		{
		if (struktura.st_mode&0100000U) {/*printf("=regular file\n");*/}
		else if (struktura.st_mode&040000U) printf("/"); // directory
		else {/*print("=не файл и не каталог\n");*/}
		}
	printf("\n");
	if (++line>=(lines-1))
		{line=0;c=pressanykey();
		if (c=='q') {printf("QUIT\n");break;}else printf("\r                          \r");}
	}
//set_terminal_no_raw();
printf("*** end of filelist\n");
closedir(dir);
}

DO_COMMAND(do_ls)
{
ls();
return ses;
}

DO_COMMAND(do_translate_word)
{
char buf[MAXBUF];
if (muller_fp==NULL)
	{
	printf("Dict file not found!\n");
	}
else
	{
	poisk(arg,buf);
	printf("'%s'\n",buf);
	}
return ses;
}

void koi_to_utf8(char *str_i, char *str_o)
{
	iconv_t cd;
	size_t len_i, len_o = MAX_SOCK_BUF * 6;
	size_t i;

	if ((cd = iconv_open("UTF-8","KOI8-RU")) == (iconv_t) - 1)
	{
		printf("koi_to_utf8: iconv_open error\n");
		return;
	}
	len_i = strlen(str_i);
	if ((i = iconv(cd, &str_i, &len_i, &str_o, &len_o)) == (size_t) - 1)
	{
		printf("koi_to_utf8: iconv error\n");
		return;
	}
	*str_o = 0;
	if (iconv_close(cd) == -1)
	{
		printf("koi_to_utf8: iconv_close error\n");
		return;
	}
}

void utf8_to_koi(char *str_i, char *str_o)
{
	iconv_t cd;
	size_t len_i, len_o = MAX_SOCK_BUF * 6;
	size_t i;

	if ((cd = iconv_open("KOI8-RU", "UTF-8")) == (iconv_t) - 1)
	{
		printf("utf8_to_koi: iconv_open error\n");
		return;
	}
	len_i = strlen(str_i);
	if ((i=iconv(cd, &str_i, &len_i, &str_o, &len_o)) == (size_t) - 1)
	{
		printf("utf8_to_koi: iconv error\n");
		// return;
	}
	if (iconv_close(cd) == -1)
	{
		printf("utf8_to_koi: iconv_close error\n");
		return;
	}
}

DO_COMMAND(do_muller)
{
if (arg)
	{
	if (arg[0]=='0') {muller_mode=0; printf("muller mode disabled\n");}
	else if (arg[0]=='1') {muller_mode=1; printf("muller mode enabled\n");}
	else if (arg[0]=='2') {muller_mode=2; printf("muller mode bilingua enabled\n");}
	else if (arg[0]==0) {printf("muller mode=%i\n",muller_mode);}
	}
return ses;
}

DO_COMMAND(do_proolcoder_switch)
{
if (coder) coder=0;
else coder=1;
printf("coder mode=%i\n", coder);
return ses;
}

void do_coder(char *s_i, char *s_o)
{
koi_to_utf8(s_i,s_o);
}

char *prooltran(char *si0)
{char *pp, *p0;
int ii;
char *si;
char original [MAXBUF];
char recoded_local [MAXBUF];

si=si0;

if (coder)
	{
	do_coder(si,recoded_str);
	si=recoded_str;
	}

if (muller_mode==1)
	{
	muller_tr (si0, recoded_str);
	return recoded_str;
	}
else if (muller_mode==2) 
	{
	strcpy(original, si0);
	muller_tr (si0, recoded_local);
	strcpy(recoded_str,original);
	strcat(recoded_str," [ ");
	strcat(recoded_str,recoded_local);
	strcat(recoded_str," ] ");
	return recoded_str;
	}

if (total_log) prool_log(si);

if (tron==0) return si;

p0=buffer;
strcpy(buffer,si);

for(ii=0;ii<MAXWORD;ii++)
{// цикл по всем словам
if (English[ii][0]==0) continue;
	while(1) // многократная замена одного слова
	{
	pp=strstr(buffer,English[ii]);
	if (pp==0) break;
	if (pp!=p0) {memcpy(clipboard,buffer,(pp-p0)); clipboard[pp-p0]=0;}
	else clipboard[0]=0;
	//printf("clipboard=%s\n",clipboard);
	strcat(clipboard,Russian[ii]);
	//printf("clipboard=%s\n",clipboard);
	strcat(clipboard,buffer+(pp-p0)+strlen(English[ii]));
	//printf("clipboard=%s\n",clipboard);
	strcpy(buffer,clipboard);
	}
}

/*
if (pp)
	{
	if (pp!=p0) {memcpy(buffer,si,(pp-p0)); buffer[pp-p0]=0;}
	else buffer[0]=0;
	strcat(buffer,"Пруль");
	strcat(buffer,si+(pp-p0)+strlen("prool"));
	}
else	strcpy(buffer,si);
*/

//printf("buffer=%s\n", buffer);
return buffer;
}

char *ptime(void) // Возвращаемое значение: ссылка на текстовую строку с текущим временем
	{
	char *tmstr;
	time_t mytime;

	mytime = time(0);

	tmstr = (char *) asctime(localtime(&mytime));
	*(tmstr + strlen(tmstr) - 1) = '\0';

	return tmstr+4;

	}

void prool_log(char *message)
{
FILE *fp;

fp=fopen(TOTAL_LOG_FILENAME,"a");
if (fp==NULL) {/*printf("prooltin: can't open log\n");*/ total_log=0; return;}
fprintf(fp,"%i %s %s\r\n",getpid(),ptime(),message);
fclose(fp);
}

void uptime_(void)
{long uptime; char *tmstr;
uptime=time(0)-start_time;
if (uptime<=60)printf("Uptime %li sec", uptime);
else
	{
	uptime=uptime/60;
	if (uptime<=60) printf ("Uptime %li min", uptime);
	else
		{
		uptime=uptime/60;
		if (uptime<=24) printf("Uptime %li hour", uptime);
		else printf("Uptime %li days", uptime/24);
		}
	}
	tmstr = asctime(localtime(&start_time));
	*(tmstr + strlen(tmstr) - 1) = '\0';
printf(" since %s\n", tmstr);
}

void prooltranslate_init(int greeting)
{FILE *fp;
char buf [MAXBUF];
char buf2 [MAXBUF];
char *cc;
int i,j;

for (i=0;i<3;i++)
	{
	muller_fp=fopen(path[i],"r");
	if (muller_fp!=NULL) {path_n=i;break;}
	}

for (i=0;i<MAXWORD;i++)
	{
	English[0][0] = 0;
	Russian[0][0] = 0;
	}

fp=fopen("slovarb.csv","r");
if (fp==NULL) {/*if (greeting!=FALSE)printf("Can't open Slovarb\n");*/ return;}
j=0;
while(!feof(fp))
	{
	buf[0]=0;
	fgets(buf,MAXBUF,fp);
	cc=strchr(buf,'\n');
	if (cc) *cc=0;
	if (buf[0])
		{
		//if (greeting!=FALSE)printf("'%s' ", buf);
		cc=strchr(buf,',');
		if (cc==0) continue;
		strcpy(buf2,cc+1);
		*cc=0;
		//if (greeting!=FALSE)printf("1 '%s' [%i] 2 '%s' [%i]\n", buf, strlen(buf), buf2, strlen(buf2));
		if ((strlen(buf)>=MAXWORDLEN) || (strlen(buf2)>=MAXWORDLEN))
			{
			printf("Word length overflow. Max len=%i\n",MAXWORDLEN);
			prool_log("Word length overflow");
			break;
			}
		strncpy(English[j],buf,MAXWORDLEN);
		strncpy(Russian[j],buf2,MAXWORDLEN);
		if (++j>=MAXWORD)
			{
			printf("Dictionary overflow!\n");
			prool_log("Dictionary overflow!");
			break;
			}
		}
	}

for (i=0;i<MAXWORD;i++)
	{
	if (English[i][0]==0) break;
	//if (greeting!=FALSE)printf("%i) %s %s ", i, English[i], Russian[i]);
	}

printf("\n");
fclose(fp);
}

DO_COMMAND(do_prool)
{
struct winsize w;

prool_ident();

printf("Compile date %s %s\nCurrent date %s\n\
Command for MSSP: #config {debug telnet} on\n\
Prool command:\n\
#prool - prool help\n\
#tt - translate one word, f.e. #tt helmet\n\
#muller [1|0|2] - Muller dictionary translator enable/disable/bilingua\n\
#tron - enable foolish translator\n\
#troff - disable foolish translator\n\
#totalon - enable total logging\n\
#totaloff - disable total logging\n\
#listdic - list of dictionary\n\
#writedic - write dictionary to file\n\
#addword english,russian - add word pair to dic\n\
#delword english - del word from dic\n\
#proolwatchdogtimer [n] - set watchdog to n seconds\n\
#proolcoder - set text recoding koi8-ru (server) <-> UTF-8 (client)\n\
#ls - list of files #cat - cat file to console #cd - change dir\n\
Experimental cmds: #mouseon and #mouseoff\n\
",__DATE__,__TIME__,ptime());

printf("Translator = %i\n", tron);
printf("Total log = %i \"%s\"\n", total_log, TOTAL_LOG_FILENAME);
printf("PID = %i\n", getpid());
printf("prool loop counter = %i\n", prool_loop_counter);
printf("watchdog = %li\n", watchdog);
printf("coder = %i\n", coder);
printf("muller mode=%i\n",muller_mode);
if (muller_fp==NULL) printf("Muller file not found!\n"); else printf("Muller file %s\n", path[path_n]);

printf("arg='%s'\n", arg);

ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
printf("Window size: lines %i, columns %i\n",w.ws_row,w.ws_col);

	{char buf[MAXLEN];
	getcwd(buf,MAXLEN);
	printf("pwd=%s\n", buf);
	}

return ses;
}

DO_COMMAND(do_totalon)
{
total_log=1;
prool_log("Total log enabled");
printf("Total log enabled\n");
return ses;
}

DO_COMMAND(do_totaloff)
{
prool_log("Total log disabled");
printf("Total log disabled\n");
total_log=0;
return ses;
}

DO_COMMAND(do_tron)
{
tron=1;
printf("Translator enabled\n");
return ses;
}

DO_COMMAND(do_troff)
{
tron=0;
printf("Translator disabled\n");
return ses;
}

DO_COMMAND(do_addword)
{
char buf[MAXBUF];
char buf2[MAXBUF];
char *cc;
int i;

//printf("arg=%s\n", arg);

strncpy(buf,arg,MAXBUF);

cc=strchr(buf,',');
if (cc==0) {printf("ERROR: No comma\n"); return ses;}
strcpy(buf2,cc+1);
*cc=0;
printf("addword 1 '%s' 2 '%s'\n", buf, buf2);

if ((strlen(buf)>=MAXWORDLEN) || (strlen(buf2)>=MAXWORDLEN))
			{
			printf("addword: Word length overflow\n");
			prool_log("addword: Word length overflow");
			return ses;
			}

for(i=0;i<MAXWORD;i++)
	{
	if (English[i][0]==0)
		{
		strcpy(English[i],buf);
		strcpy(Russian[i],buf2);
		return ses;
		}
	}

printf("ERROR: addword: word overflow\n");
prool_log("ERROR: addword: word overflow");

return ses;
}

DO_COMMAND(do_delword)
{int i;
	if (*arg==0) {
		printf("usage: #delword word\n");
		return ses;
	}

	for (i=0;i<MAXWORD;i++)
	{
		if (!strcmp(English[i],arg))
		{
			English[i][0]=0;
			printf("Found and delete\n");
			return ses;
		}
	}
printf("Word '%s' not found\n", arg);
return ses;
}

DO_COMMAND(do_listdic)
{
	int i,count;

	count=0;
	for (i=0;i<MAXWORD;i++) {
		if (English[i][0]) {printf("%s,%s\n",English[i],Russian[i]); count++;}
	}

	printf("Total words %i\n", count);

	return ses;
}

DO_COMMAND(do_writedic)
{
	int i,count;
	FILE *fp;

	count=0;
	fp=fopen("slovarb2.csv","w");
	if (fp==NULL) {printf("writedic: can't open file\n"); return ses;}
	for (i=0;i<MAXWORD;i++) {
		if (English[i][0]) {fprintf(fp,"%s,%s\n",English[i],Russian[i]); count++;}
	}
	fclose(fp);

	printf("Total words %i\n", count);

	return ses;
}

DO_COMMAND(do_proolwatchdogtimer)
{
watchdog=atoi(arg);

printf("watchdog set to %li sec\n", watchdog);
prool_log("proolwatchdog set");

if (watchdog) startwatchtime=time(0);

return ses;
}

int prool_loop(void)
{
long int i;
prool_loop_counter++;
if (watchdog)
	{
	i=time(0);
	if ((i-startwatchtime)>watchdog)
		{
		printf("proolwatchdog bzzzzz!!!\n");
		prool_log("proolwatchdog bzzzzz!!!");
		watchdog=0;
		printf("tintin++ quit\n");
		prool_log("tintin++ quit");
		do_zap(0,0);
		printf("zapped\n");
		}
	}
return 0;
}

#define ESC 033
#define ESC_STR "\033"

void enable_mouse(void)
{
#if 1 // mouse code from midnight commander
//    case MOUSE_XTERM_NORMAL_TRACKING:
        /* save old highlight mouse tracking */
        printf (ESC_STR "[?1001s");

        /* enable mouse tracking */
        printf (ESC_STR "[?1000h");

        /* enable SGR extended mouse reporting */
//        printf (ESC_STR "[?1006h");

        fflush (stdout);

//    case MOUSE_XTERM_BUTTON_EVENT_TRACKING:
        /* save old highlight mouse tracking */
        printf (ESC_STR "[?1001s");

        /* enable mouse tracking */
        printf (ESC_STR "[?1002h");

        /* enable SGR extended mouse reporting */
//        printf (ESC_STR "[?1006h");

        fflush (stdout);
#endif
}

void disable_mouse(void)
{

#if 1 // mouse code from midnight commander

// case MOUSE_XTERM_NORMAL_TRACKING:
        /* disable SGR extended mouse reporting */
        printf (ESC_STR "[?1006l");

        /* disable mouse tracking */
        printf (ESC_STR "[?1000l");

        /* restore old highlight mouse tracking */
        printf (ESC_STR "[?1001r");

        fflush (stdout);
//    case MOUSE_XTERM_BUTTON_EVENT_TRACKING:
        /* disable SGR extended mouse reporting */
        printf (ESC_STR "[?1006l");

        /* disable mouse tracking */
        printf (ESC_STR "[?1002l");

        /* restore old highlight mouse tracking */
        printf (ESC_STR "[?1001r");

        fflush (stdout);
#endif
}

DO_COMMAND(do_mouseon)
{
	enable_mouse();
	printf("Mouse on\n");
return ses;
}

DO_COMMAND(do_mouseoff)
{
	disable_mouse();
	printf("Mouse off\n");
return ses;
}
/****************************************************/
char *muller_tr (char *input, char *out)
{
char buffer [PROOL_LEN];
char perevod [PROOL_LEN];
char *cc, *cc2, *cc3;

//return "[prool fool!]";

if (input==0) {out[0]=0; return out;}
if (*input==0) {out[0]=0; return out;} 

//printf("prool tr2 in='%s' [", input);

#if 0 // debug print
cc=input;
while (*cc) printf ("%02X ",*cc++);
printf(" ] \n");
#endif

buffer[0]=0;
out[0]=0;

if (input[0]=='\b') { // строки с таким символом в начале не переводятся!
	strcpy(out,input+1);
	return out;
}

#if 0 // prool: 0 for prooltin
if (tran_s(input,buffer)==0)
	{// translation ok
	strcpy(out,buffer);
	return out;
	}
#endif

if (muller_mode==0) {
	strcpy(out,input);
	return out;
}

// деление строки на слова 
cc=input;
while (1) {
cc2=strchr(cc,' ');
if (cc2==0) {// последнее слово
	//printf("prool w last='%s'\n", cc);
	perevod[0]=0;
	poisk(cc,perevod);
	strcat(out,perevod);
	break;
}
strcpy(buffer,cc);
cc3=strchr(buffer,' ');
if (cc3) *cc3=0;
//printf("prool w='%s'\n", buffer);
perevod[0]=0;
poisk(buffer,perevod);
strcat(out,perevod);
strcat(out," ");
cc=cc2+1;
if (*cc==0) break;
}

//printf("prool tr2 out='%s'\n", out);
return out;
} // end muller_tr

void poisk (char *in, char *out) // поиск слова в словаре Мюллера
{
char buf[PROOL_LEN];
char buf2[PROOL_LEN];
char prefix[PROOL_LEN];
char suffix[PROOL_LEN];
char slovo[PROOL_LEN];
char *cc;
int i, l, count, index, busstop, ampersend, first_upper, all_upper;

// слова, которые в принципе не переводятся
l=strlen(in);

if (in[0]==0) goto l1;
if (in[0]=='|') goto l1;
if (in[0]=='-') goto l1;
if (in[0]=='+') goto l1;
if ((in[0]=='&')&&(l==2)) goto l1;
if (strchr(in,'|')) goto l1;
if (strchr(in,'/')) goto l1;
if (strchr(in,'~')) goto l1;
if (strchr(in,'^')) goto l1;
if (strchr(in,'\\')) goto l1;

count=0; 
for (i=0;i<l;i++) if (in[i]=='.') count++;
if (count>1) goto l1;

count=0; 
for (i=0;i<l;i++) if (in[i]=='i') count++;
if (count>2) goto l1;

count=0; 
for (i=0;i<l;i++) if (in[i]=='o') count++;
if (count>2) goto l1;

count=0; 
for (i=0;i<l;i++) if (in[i]==':') count++;
if (count>2) goto l1;

// выделение префикса (предшествующей слову небуквенной строки, например кода смены цвета или кавычек
l=strlen(in);
index=0;
prefix[0]=0;
suffix[0]=0;
slovo[0]=0;
ampersend=0;

for (i=0;i<l;i++)
	{
	if (ampersend)
		{// после амперсенда идет цифра или буква, но это код цвета, то это часть префикса! хоть и буква!!
		prefix[index++]=in[i];
		ampersend=0;
		}
	else if (((in[i]>='a')&&(in[i]<='z')) || ((in[i]>='A')&&(in[i]<='Z')))
		{// єто буква
		prefix[index]=0;
		break;
		}
	else
		{// это не буква: значит это часть префикса
		prefix[index++]=in[i];
		if (in[i]=='&') ampersend=1;
		}
	}

prefix[index]=0;
busstop=i;

// выделение слова
index=0;
for (i=busstop; i<l; i++)
	{
	if (((in[i]>='a')&&(in[i]<='z')) || ((in[i]>='A')&&(in[i]<='Z')))
		{// єто буква
		slovo[index++]=in[i];
		}
	else
		{// это не буква
		slovo[index]=0;
		break;
		}
	}

slovo[index]=0;

busstop=i;

//выделение суффикса (следующей за словом небуквенной строки, например, точки, или кода смены цвета или точки и кода смены цвета

index=0;
for (i=busstop; i<l; i++)
	{
	if (((in[i]>='a')&&(in[i]<='z')) || ((in[i]>='A')&&(in[i]<='Z')))
		{// єто буква
		suffix[index]=0;
		break;
		}
	else
		{// это не буква
		suffix[index++]=in[i];
		}
	}
suffix[index]=0;

//printf("Деление слова '%s' == '%s' '%s' '%s'\n", in, prefix, slovo, suffix);

if (slovo[0]==0) goto l1; // слова нет, это небуквенное "псевдослово", не переводим

// анализ первой буквы слова, большая ли она
first_upper=0;
if (isupper(slovo[0])) first_upper=1;

// а может всё слово большими (заглавными) буквами?

l=strlen(slovo);
all_upper=1;
for (i=0;i<l;i++) if (islower(slovo[i])==0) all_upper=0;

// приводим всё слово к нижнему регистру
for (i=0;i<l;i++) slovo[i]=tolower(slovo[i]);

if (muller_fp==NULL) goto l1;
fseek(muller_fp,0,SEEK_SET);

while(1)
	{
	buf[0]=0;
	fgets(buf,PROOL_LEN,muller_fp);
	if (buf[0]==0) break;
	cc=strchr(buf,0x0A);
	if (cc) *cc=0;
	cc=strchr(buf,'#');
	if (cc==0) continue;
	strcpy(buf2,cc+1);
	*cc=0;
	if (!strcmp(slovo/*in*/,buf))
		{
		//strcpy(out,buf2);
		out[0]=0;
		if (prefix[0]) strcpy(out,prefix);
		if (buf2[0]) strcat(out,buf2);
		if (suffix[0]) strcat(out,suffix);
		// printf("'%s'->'%s'\n", in, out); // debug print: enabled on mud server and disabled on prooltin
		return;
		}
	}

//printf("'%s'-> ????\n", in); // debug print
l1:;
strcpy(out,in);

} // end poisk

void printfile(char *filename)
{
FILE *fp;
char str[MAXLEN];
char c;
int i;
int lines=24;

//printf("printfile() filename=%s\n", filename);

fp = fopen (filename,"r");
                                 
if (fp==NULL) {printf("Can't open file `%s'\n", filename); return;}

i=0;
//set_terminal_raw();
while(!feof(fp))
	{char *cc;
	str[0]=0;
	cc=fgets(str,MAXLEN,fp);
	if (cc==NULL) break;
	//if (*cc=='#') continue; // this is comment
	if (*cc==']')
		{// this is command
		//if (!memcmp(cc,"]COLOR",strlen("]COLOR"))) setcolor(atoi(cc+strlen("]COLOR")));
		//if (!memcmp(cc,"]ESC",strlen("]ESC"))) esc(atoi(cc+strlen("]ESC")));
		continue;
		}
	if (str[0])
		{int ii=0;
		while(str[ii]) putchar(str[ii++]);
		if (++i>=(lines-1)) {i=0;c=pressanykey(); if (c=='q') {printf("QUIT\n");break;} }
		}
	}
fclose(fp);
//set_terminal_no_raw();
//setcolor(0);
}

DO_COMMAND(do_cat)
{
printfile(arg);
return ses;
}

DO_COMMAND(do_chdir)
{
chdir(arg);
return ses;
}
