#include <stdio.h>
#include <stdlib.h>
//#include <alloc.h>
#include <string.h>
//#include <mem.h>

#include "typedfs.h"
#include <stringa.h>
#include <dbllist.h>

#ifndef coreleft
  #define coreleft() 0l
#endif

#ifndef setmem
void setmem(void *buff,int count,char number)
{
while(count>0)
	{
#ifdef NO_LVAL_CAST
        *((char *)buff)=number;
        buff=(char *)buff + 1;
#else
	
	*((char *)buff)++=number;
#endif
	count--;
	}
}
#endif

class codepage
	{
public: codepage(void)	{name=NULL;characters=NULL;MaxUsed=Allocated=0;};
	~codepage(void)	{free(name);free(characters);Allocated=0;};

	char *name;
	WORD Allocated;
	WORD MaxUsed;
	char **characters;

	void InsertCharDesc(WORD position, const char *Name);
	};


class Translator
	{
public: Translator(void)  {ToCp=FromCp=NULL;Data=NULL;}
	~Translator(void) {if(Data!=NULL) {free(Data);Data=NULL;}}

	const codepage *FromCp;
	const codepage *ToCp;
	WORD *Data;

	void erase(void) {if(Data!=NULL) {free(Data);Data=NULL;}ToCp=FromCp=NULL;}
	};


void codepage::InsertCharDesc(WORD position, const char *Name)
{
if(Name==NULL) return;
if(*Name==0) return;
if(position>=Allocated)
	{
	if( (characters=(char **)realloc(characters,sizeof(char **)*(position+1)))==NULL)
		{
NoMemory:	puts("Fatal: Memory exhausted");
//		return;
		exit(-1);
		}
	do {
	   characters[Allocated++]=NULL;
	   } while(position>=Allocated);
	}

if(characters[position]!=NULL)
	{
        printf("\n<%s>",characters[position]);

	if(!strcmp(characters[position],Name)) return;
	printf("Warning: redefinition of character 0x%X \"%s\" -> \"%s\" in \"%s\"!\n",
		position,characters[position],Name,(name==NULL)?"":name);
	free(characters[position]);
	characters[position]=NULL;
	}

if( (characters[position]=strdup(Name))==NULL ) goto NoMemory;
if(position>=MaxUsed) MaxUsed=position+1;
}

struct chardesc
	{
	WORD position;
	string name;
	};


/*This function read one line from the text file and store it to the string.*/
string & fGets2(FILE *f, string & pstr)
{
  char c;

  pstr="";
  while (!feof(f))
     {
     c = getc(f);
     if (c == '\n') break;
     if (c == '\r') continue;
     if ((unsigned char)c == 0xFF)
	if(feof(f)) break;	//Fix the situation, when 0xFF is read before eof

     pstr += c;
  }

 return(pstr);
}



string s;
#define CharSetSize 256
void ReadChar(FILE *f, codepage & cp)
{
static WORD previous=0;
WORD number=0,digits=0,n2=0;
char *text,*t2;

fGets2(f, s);
if(s=="") return;

text=s();
while(isspace(*text)) text++;
if(*text==0 || *text==';') return; // empty line - useless
while(isdigit(*text))
   {
   number = number*10 + *text-'0';
   text++;
   digits++;
   }
if(*text==',')
   {
   text++;
   while(isdigit(*text))
	{
	n2 = n2*10 + *text-'0';
	text++;
	}
   number = CharSetSize * number + n2;
   }
if(digits==0) number = previous++;
	 else previous= number;
while(isspace(*text)) text++;
if(*text==0 || *text==';') return; // only number - useless
t2=text;
while(*t2!=0)
	{
	if(*t2==';')
	    {
 	    *t2=0;
	    check(s);
	    break;
	    }
	t2++;
	}

n2=length(s);
while(isspace(s[n2-1]))
   {
   s[n2-1]=0;
   n2--;
   }

cp.InsertCharDesc(number,text);
}


void CreateTranslator(Translator & trn, const codepage & cp1, const codepage & cp2)
{
WORD i,j;

trn.erase();
printf("Info: Building translator %s -> %s\n",cp1.name,cp2.name);

if((trn.Data=(WORD *)calloc(cp1.MaxUsed,sizeof(WORD)))==NULL) 
  {
  printf(" >>Memory exhausted!");
  return;
  }
//setmem(trn.Data,cp1.MaxUsed,0); - calloc initialises memory.

trn.FromCp = &cp1;
trn.ToCp = &cp2;

for(i=0;i<cp1.MaxUsed;i++)
    {
    if(cp1.characters[i]==NULL) continue;
    for(j=0;j<cp2.MaxUsed;j++)
	{
	if(cp2.characters[j]==NULL) continue;
	if(!strcmp(cp1.characters[i],cp2.characters[j]))
		{
		if(cp1.characters[i]!=cp2.characters[j])
			{
			free(cp1.characters[i]);	//optimize memory
			cp1.characters[i]=cp2.characters[j];
			}
		trn.Data[i]=j;
		break;
		}
	}
    }

return;
}


void LoadCodePage(string cpfilename,codepage & cp,int ShowAll=1)
{
FILE *Fcp;

 printf("%s%s",ShowAll?"\nInfo: Loading codepage ":", ",cpfilename());

 cp.name=strdup(cpfilename);
 cpfilename+=".enc";

 if((Fcp=fopen(cpfilename,"r"))==NULL )
	{
	printf("\nError: cannot open file:%s!!!",cpfilename());
	return;
	}

 while(!feof(Fcp))
	ReadChar(Fcp,cp);

 fclose(Fcp);
}


void ReadTextChar(FILE *f,doublelist & ll)
{
string s,CharN,desc;
int i;

fGets2(f, s);
if(s=="") return;

i=0;

while(isspace(s[i])) i++;
if(s[i]==';')
	return;
while(!isspace(s[i]) && (s[i]!=0) ) {CharN+=s[i];i++;}
CharN.trim();
if(CharN=="") return;

while(isspace(s[i])) i++;
while(i < length(s))
	{
	if(s[i]==';' || s[i]==0) break;
	desc+=s[i];
	i++;
	}
desc.trim();

ll.Add(CharN,desc);
}


void LoadTextCodePage(string cpfilename,doublelist & ll)
{
FILE *Fcp;

 printf("Info: Loading text codepage %s\n",cpfilename());

 cpfilename+=".txc";

 if( (Fcp=fopen(cpfilename,"r"))==NULL )
	{
	printf("Error: cannot open file:%s!!!\n",cpfilename());
	return;
	}

 while(!feof(Fcp))
	ReadTextChar(Fcp,ll);

 fclose(Fcp);
}


void CreateCodePage(string cpfilename,codepage & cp,doublelist & ll)
{
int i;

cp.name=strdup(cpfilename);

i=0;
while( ll[i]!=NULL)
	{
	cp.InsertCharDesc(i,ll.Member(i,1));
	i++;
	}

}


void WriteStringTable(FILE *F,const codepage & cp,doublelist & ll)
{
int i;

if(F==NULL) return;
printf("Info: Writing string table %s.\n",cp.name);

fprintf(F,"const char *Table_%s[]={\n",cp.name);

i=0;
while( ll[i]!=NULL)
	{
	fprintf(F,"  \"%s\"",ll.Member(i,0));
	i++;
	if(ll[i]==NULL) fprintf(F,"};\n");
		   else fprintf(F,",\n");
	}

}


int WSparseCpTranslatorConsumption(WORD Max, WORD EmptyBankCount)
{
unsigned BanksTotal = (Max + 255) / 256;

return 512*(BanksTotal-EmptyBankCount) + 4*BanksTotal;
}


int BSparseCpTranslatorConsumption(WORD Max, WORD EmptyBankCount)
{
unsigned BanksTotal = (Max + 255) / 256;

return 256*(BanksTotal-EmptyBankCount) + 4*BanksTotal;
}



/*This procedure stores translator file to the disk*/
void WriteTranslator(const codepage & cp_from, const codepage & cp_to, Translator &trn, FILE *F)
{
WORD i,b,Max,EmptyBanks;
char *cp_from_name,*cp_to_name;
char TranslatorType=0;
long Banks[256];

 if(F==NULL) return;
 if(trn.Data==NULL)
   {
   printf("Error: Translator %s -> %s has not data!\n", cp_from.name, cp_to.name);
   return;
   }

 printf("Info: Writing translator %s -> %s\n",cp_from.name,cp_to.name);
 Max = cp_from.MaxUsed;
 while(Max>0)
    {
    if(trn.Data[Max-1]!=0 && trn.Data[Max-1]!=0xFFFF) break;
    Max--;
    }

 cp_from_name=StrStr(cp_from.name,"/");
 if(cp_from_name==NULL) cp_from_name=cp_from.name;
                   else cp_from_name++;
 cp_to_name=StrStr(cp_to.name,"/");
 if(cp_to_name==NULL) cp_to_name=cp_to.name;
                 else cp_to_name++;

 setmem(Banks,sizeof(Banks),0);
 TranslatorType = 2;		// BYTE array
 for(i=0; i<Max; i++)
   {
   if(trn.Data[i]!=0) Banks[i>>8]++;
   if(trn.Data[i]>=256) TranslatorType=1;  // WORD array
   }
 EmptyBanks=0;
 for(b=0;b<=(Max-1)>>8;b++)
   {
   if(Banks[b]==0) EmptyBanks++;
   }
 //printf(" Empty:%d %s_%s Max:%d %d ",EmptyBanks,cp_from_name,cp_to_name,Max,Max>>8);

 if(Max==0) TranslatorType=0;
 if(EmptyBanks>1)
  {
  if(TranslatorType==2)		// BYTE
    {
      if(BSparseCpTranslatorConsumption(Max,EmptyBanks)<Max)
	TranslatorType = 4;
    }
  else				// WORD
   {
     if(WSparseCpTranslatorConsumption(Max,EmptyBanks) < 2*Max)
     	TranslatorType = 3;
   }
  }

 switch(TranslatorType)
  {
  case 0:fprintf(F,"\nCpTranslator %s%s(\"%sTO%s\");\n\n",
           cp_from_name,cp_to_name,
           cp_from_name,cp_to_name);
         break;

  case 1:fprintf(F,"const WORD %s_%s[]={",cp_from_name,cp_to_name);
	 for(i=0;i<Max;i++)
		{
		fprintf(F,"  %u%s", trn.Data[i], (i==Max-1)?"};":",");
		if(cp_from.characters[i]!=NULL && trn.Data[i]!=0)
		fprintf(F,"	/* %s */", cp_from.characters[i]);
		fputc('\n',F);
		}
         fprintf(F,"\nWCpTranslator %s%s(\"%sTO%s\",%u,%s_%s);\n\n",
           cp_from_name,cp_to_name,
           cp_from_name,cp_to_name,(unsigned)Max,
           cp_from_name,cp_to_name);
         break;

  case 2:fprintf(F,"const BYTE %s_%s[]={",cp_from_name,cp_to_name);
  	 for(i=0;i<Max;i++)
		{
//		printf("  %d,\n",trn[i]);
		fprintf(F,"  %u%s",trn.Data[i],
			i==Max-1?"};":",");
		if(cp_from.characters[i]!=NULL && trn.Data[i]!=0)
		fprintf(F,"	/* %s */",cp_from.characters[i]);
		fputc('\n',F);
		}
  	 fprintf(F,"\nBCpTranslator %s%s(\"%sTO%s\",%u,%s_%s);\n\n",
           cp_from_name,cp_to_name,
           cp_from_name,cp_to_name,(unsigned)Max,
           cp_from_name,cp_to_name);
         break;

  case 3:for(b=0;b<=(Max-1)>>8;b++)
  	     {
             if(Banks[b]==0) continue;
             fprintf(F,"const WORD %s_%sB%d[]={",cp_from_name,cp_to_name,b);
             for(i=b*256;i<(b+1)*256;i++)
		{
		fprintf(F,"  %d%s",(i>=Max)?0:trn.Data[i],
			i==((b+1)*256-1)?"};":",");
		if(i<Max)
		  if(cp_from.characters[i]!=NULL && trn.Data[i]!=0)
		     fprintf(F,"	/* %s */",cp_from.characters[i]);
		fputc('\n',F);
		}
             }
        fputc('\n',F);     
        fprintf(F,"const WORD *%s_%sB[]={",cp_from_name,cp_to_name);
        for(b=0;b<=(Max-1)>>8;b++)
  	     {
             if(Banks[b]==0) fprintf(F," NULL%s",(b==(Max-1)>>8)?"};":",");
                       else  fprintf(F," %s_%sB%d%s",cp_from_name,cp_to_name,(int)b,(b==(Max-1)>>8)?"};":",");
             fputc('\n',F);          
             }
        fprintf(F,"\nWSparseCpTranslator %s%s(\"%sTO%s\",%u,%s_%sB);\n\n",
           cp_from_name,cp_to_name,
           cp_from_name,cp_to_name,(unsigned)((Max-1)>>8)+1,
           cp_from_name,cp_to_name);
        break;

  case 4:for(b=0;b<=(Max-1)>>8;b++)
  	     {
             if(Banks[b]==0) continue;
             fprintf(F,"const BYTE %s_%sB%d[]={",cp_from_name,cp_to_name,b);
             for(i=b*256;i<(b+1)*256;i++)
		{
		fprintf(F,"  %d%s",(i>=Max)?0:trn.Data[i],
			i==((b+1)*256-1)?"};":",");
		if(i<Max)
		  if(cp_from.characters[i]!=NULL && trn.Data[i]!=0)
		     fprintf(F,"	/* %s */",cp_from.characters[i]);
		fputc('\n',F);
		}
             }
        fputc('\n',F);     
        fprintf(F,"const BYTE *%s_%sB[]={",cp_from_name,cp_to_name);
        for(b=0;b<=(Max-1)>>8;b++)
  	     {
             if(Banks[b]==0) fprintf(F," NULL%s",(b==(Max-1)>>8)?"};":",");
                       else  fprintf(F," %s_%sB%d%s",cp_from_name,cp_to_name,(int)b,(b==(Max-1)>>8)?"};":",");
             fputc('\n',F);          
             }
        fprintf(F,"\nBSparseCpTranslator %s%s(\"%sTO%s\",%u,%s_%sB);\n\n",
           cp_from_name,cp_to_name,
           cp_from_name,cp_to_name,(unsigned)((Max-1)>>8)+1,
           cp_from_name,cp_to_name);
        break;
  }
}


/*This procedure displays errors that occured inside atoms unit*/
#ifdef ERROR_HANDLER
void RaiseError(int ErrNo, void *Instance)
{
static int LastErrNo=0;
static void *LastInstance=NULL;
const char *UnitName="";
  //if(Verbosing<=-1) return;
  if(LastErrNo!=ErrNo || LastInstance!=Instance)
    {
    if((ErrNo&0xFF00)==ListsId) UnitName=" Lists";
    if((ErrNo&0xFF00)==StringsId) UnitName=" Strings";
    fprintf(stderr,"\nERROR: Internal inside ATOMS unit%s 0x%X object:(%p)",
	UnitName,ErrNo,Instance);
    LastErrNo=ErrNo;LastInstance=Instance;
    }
}
#endif
