/******************************************************************************
 *                                                                            *
 *                       (c) 2007-2026 Jaroslav fojtik                        *
 *                  This code could be used under LGPL licence                *
 *                                                                            *
 ******************************************************************************/
/** @file
 *  Translating and localisation C++ code including widgets.
 * Compiler: Borland C++ Builder 4 and 6
 *
 * Note: this cannot be compiled with Code GUARD in BCC6.
*/
#include <vcl.h>
#include <stdio.h>

#include <Buttons.hpp>		// For TBitBtn
#include <ComCtrls.hpp>

#include <string.h>

#pragma hdrstop

#include "locale_set.h"
#ifdef __gettext__
 #include <locale.h>
 #include <libintl.h>
 static const char GET_LOCALE_INFO[] = "";
 //#define USE_TabnotbkHPP
 //#define USE_TnT

 #ifdef USE_TnT
  #include "TntStdCtrls.hpp"
  #include "TntButtons.hpp"
 #endif
#endif

#ifndef EnvLanguage
 #define EnvLanguage "LANGUAGE="
#endif // EnvLanguage

//#define UTF8		16384


#ifdef __gettext__


short CodePage = 0;
const char *Empty2Info = NULL;


AnsiString Translate(const char *str)
{
  return _(str);
}


/// Recommended use: Form->Caption = TranslateX(N_("String to translate"));
AnsiString (*TranslateX)(const char *str) = Translate;


/// This function translatex a message and ten converts UTF8 encoded fragment to a local 
/// 8bit code page.
AnsiString TranslateUTF8(const char *str)
{
int len;
wchar_t *WStr;
char *str2;

  if(str==NULL || *str==0) return str;
  str2 = _(str);
  if(str2==NULL) return str;
  str = str2;

  while(*str2!=0)
  {
    if(*(unsigned char*)str2 >= 0x80) break;
    str2++;
  }
  if(*str2==0) return str;	// no extended UTF8 char found.

  len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
  WStr = (wchar_t*)malloc(2*len+2);
  if(WStr==NULL) return str;
  //mbstowcs(WStr,str,len);
  MultiByteToWideChar(CP_UTF8, 0, str, -1, WStr, len+1);

  len = WideCharToMultiByte(CP_ACP, 0, WStr, -1, NULL, 0,  NULL, NULL);  // The function returns the required buffer size, in bytes, including any terminating null character.
  if(len<=1) return str;                // zero size string.
#if 0
  str2 = (char*)malloc(len+1);
  if(str2==NULL) return str;
  WideCharToMultiByte(CP_ACP, 0, WStr, -1, str2, len,  NULL, NULL);
  free(WStr);
  AnsiString Astr(str2);
  free(str2);
#else
  AnsiString Astr;
  Astr.SetLength(len);		// This code is expected to allocate a total of len plus one character.
  WideCharToMultiByte(CP_ACP, 0, WStr, -1, Astr.c_str(), len,  NULL, NULL); // There are 2 zero terminators for safety
#endif

return Astr;
}


/// This function converts encoded fragment to a local 8bit code page and checks for 
/// codepage whether it is UTF8. No language translation occurs.
/// Recommended use: Form->Caption = FixUTF8(_("String to translate"));
AnsiString FixUTF8(const char *str)
{
int len;
wchar_t *WStr;
const char *str2;

  if(CodePage!=UTF8 || str==NULL || *str==0) return str;
  str2 = str;

  while(*str2!=0)
  {
    if(*(unsigned char*)str2 >= 0x80) break;
    str2++;
  }
  if(*str2==0) return str;	// no extended UTF8 char found.

  len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
  WStr = (wchar_t*)malloc(2*len+2);
  if(WStr==NULL) return str;
  //mbstowcs(WStr,str,len);
  MultiByteToWideChar(CP_UTF8, 0, str, -1, WStr, len+1);

  len = WideCharToMultiByte(CP_ACP, 0, WStr, -1, NULL, 0,  NULL, NULL); // The function returns the required buffer size, in bytes, including any terminating null character.
#if 0
  char *str3 = (char*)malloc(len+1);
  if(str3==NULL) return str;
  WideCharToMultiByte(CP_ACP, 0, WStr, -1, str3, len,  NULL, NULL);
  free(WStr);
  AnsiString Astr(str3);
  free(str3);
#else
  AnsiString Astr;
  Astr.SetLength(len);                          // This code is expected to allocate a total of len plus one character.
  WideCharToMultiByte(CP_ACP, 0, WStr, -1, Astr.c_str(), len,  NULL, NULL);  // There are 2 zero terminators for safety
#endif
return Astr;
}

#endif


WideString FixUTF8W(const char *str)
{
int len;
const char *str2;

  if(str==NULL || *str==0) return WideString(L"");
  str2 = str;

  while(*str2!=0)
  {
    if(*(unsigned char*)str2 >= 0x80) break;
    str2++;
  }
  if(*str2==0) return str;	// no extended UTF8 char found.

  len = MultiByteToWideChar((CodePage==UTF8)?CP_UTF8:CP_OEMCP, 0, str, -1, NULL, 0);

#if 0		// Less effective WString feed with auxiliary buffer.
  wchar_t *WStr = (wchar_t*)malloc(2*len+2);
  if(WStr==NULL) return str;
  //mbstowcs(WStr,str,len);
  MultiByteToWideChar((CodePage==UTF8)?CP_UTF8:CP_OEMCP, 0, str, -1, WStr, len+1);

  WideString w(WStr);
  free(WStr);
#else
  WideString w;			// This code is expected to allocate a total of len plus one character
  w.SetLength(len);		// Wraps SysReallocStringLen - https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysreallocstringlen
  MultiByteToWideChar((CodePage==UTF8)?CP_UTF8:CP_OEMCP, 0, str, -1, w.c_bstr(), len+1);
#endif
return w;
}


/** Translate strings in one particular form.
 * @param[in, out]      Frm     Form that is to be localised. */
void TranslateStrings(TForm *Frm)
{
#ifdef __gettext__
int i,j;
TComponent *pComp;
int numcontrols;

  if(Frm==NULL) return;

  Frm->Caption = TranslateX(Frm->Caption.c_str());
  numcontrols = Frm->ComponentCount;
  for(i=0; i<numcontrols; i++)
  {
    pComp = Frm->Components[i];
 #ifdef USE_TnT
    {
      TTntButton *pB = dynamic_cast<TTntButton *>(pComp);
      if(pB!=NULL)
      {
        const AnsiString AS(pB->Caption);
	pB->Caption = FixUTF8W(_(AS.c_str()));
        continue;
      }
    }
    {
      TTntLabel *Lbl = dynamic_cast<TTntLabel *>(pComp);
      if(Lbl)
      {
        const AnsiString AS(Lbl->Caption);
        Lbl->Caption = FixUTF8W(_(AS.c_str()));
	continue;
      }
    }
    {
      TTntBitBtn *BitBtn = dynamic_cast<TTntBitBtn *>(pComp);
      if(BitBtn)
      {
        const AnsiString AS(BitBtn->Caption);
        BitBtn->Caption = FixUTF8W(_(AS.c_str()));
        continue;
      }
    }
#endif
    {
      TBitBtn *BitBtn = dynamic_cast<TBitBtn *>(pComp);
      if(BitBtn)
      {
        BitBtn->Caption = TranslateX(BitBtn->Caption.c_str());
        continue;
      }
    }
    {
      TLabel *Lbl = dynamic_cast<TLabel *>(pComp);
      if(Lbl)
      {
        Lbl->Caption = TranslateX(Lbl->Caption.c_str());
	continue;
      }
    }
    {
      TRadioButton *pRadBtn = dynamic_cast<TRadioButton *>(pComp);
      if(pRadBtn)
      {
        pRadBtn->Caption = TranslateX(pRadBtn->Caption.c_str());
        continue;
      }
    }
    {
      TStaticText *STxt = dynamic_cast<TStaticText *>(pComp);
      if(STxt)
      {
        STxt->Caption = TranslateX(STxt->Caption.c_str());
        continue;
      }
    }
    {
      TButton *pB = dynamic_cast<TButton *>(pComp);
      if(pB!=NULL)
      {
	pB->Caption = TranslateX(pB->Caption.c_str());
        continue;
      }
    }
    {
      TCheckBox *pCB=dynamic_cast<TCheckBox *>(pComp);
      if(pCB!=NULL)
      {
	pCB->Caption = TranslateX(pCB->Caption.c_str());
        continue;
      }
    }
    {
      TTabControl *pTabC = dynamic_cast<TTabControl *>(pComp);
      if(pTabC!=NULL)
      {
	for(j=0;j<pTabC->Tabs->Count;j++)
          pTabC->Tabs->Strings[j] = TranslateX(pTabC->Tabs->Strings[j].c_str());
        continue;
      }
    }
    {
      TGroupBox *pGrBox = dynamic_cast<TGroupBox *>(pComp);
      if(pGrBox!=NULL)
      {
        pGrBox->Caption = TranslateX(pGrBox->Caption.c_str());
        continue;
      }
    }
#if defined(TabnotbkHPP) && defined(USE_TabnotbkHPP)
    {
      TTabbedNotebook *TabNtb = dynamic_cast<TTabbedNotebook *>(pComp);
      if(TabNtb)
      {
	for(j=0; j<TabNtb->Pages->Count; j++)
	    TabNtb->Pages->Strings[j] = TranslateX(TabNtb->Pages->Strings[j].c_str());
	continue;
      }
    }
#endif
    {
      TMenuItem *pMenuIt = dynamic_cast<TMenuItem *>(pComp);
      if(pMenuIt!=NULL)
      {
        if(pMenuIt->Caption!=NULL && pMenuIt->Caption.Length()>0)
            pMenuIt->Caption = TranslateX(pMenuIt->Caption.c_str());
        AnsiString S = pMenuIt->Caption;
        continue;
      }
    }
  }
#endif
}


/** Setup LANGUAGE according to LCID if it is not set yet. */
void FixEnv(void)
{
#ifdef __gettext__
AnsiString Lang;
LCID WinLang;

  if(getenv("LANGUAGE")!=NULL) return; //LANGUAGE is already set

  WinLang = GetUserDefaultLCID();
	// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/63d3d639-7fd2-4afb-abbe-0d5b5551eef8
  switch(WinLang)
	{
//	case 0x0409:Lng=L"EN";break;		//English (United States);
	case 0x0419:Lang="RU";break;		//Russian
	case 0x0405:Lang="CS";break;		//Czech
	case 0x0802:Lang="ZH";break;		//Chinese
	case 0x1007:				// German (Luxembourg)
	case 0x0C07:				// German (Austria)
	case 0x0807:				// German (Switzerland)
	case 0x0407:Lang="DE";break;		//German (Standard)
	//default:swprintf(BUFF,L"%d",WinLang);
	//	Lng=BUFF;
    }

  if(Lang != "")	//Windows Language was detected
    {
    Lang = EnvLanguage + Lang;
    putenv(strdup(Lang.c_str())); //The string must not be released!!!
    }
#endif // __gettext__
}


void DumpStr(FILE *F, const char *str)
{
 if(str==NULL || *str==0) return;

 if(strchr(str,'\n')==NULL && strchr(str,'\r')==NULL)
 {
   fprintf(F,"puts(_(\"%s\"));\n",str);
   return;
 }

 fprintf(F,"puts(_(\"");
 while(*str!=0)
 {
   switch(*str)
   {
     case '\n': fprintf(F,"\\n"); break;
     case '\r': fprintf(F,"\\r"); break;
     default:   fputc(*str,F);
   }
   str++;
 }
 fprintf(F,"\"));\n");
}


/** This function dumps all available strins in forms passed in argument.
 * @param[in]	FormCount	Amount of form passed through ellipsis. */
void DumpStrings(int FormCount, const TForm *Frm, ...)
{
#ifdef __gettext__
FILE *F;
int i,j;
const TComponent *pComp;
int numcontrols;
va_list ap;

  if(Frm==NULL) return;
  F = fopen("dump.c","wb");
  if(F==NULL) return;

  va_start(ap, Frm);

  fprintf(F,"/*Please do not edit this file, it contains extracted strings from GUI version, created %s %s.*/\n", __DATE__, __TIME__);
  fprintf(F,"#include <stdio.h>\nvoid main(void)\n{\n");

  while(FormCount>0)
  {
    DumpStr(F,Frm->Caption.c_str());
    numcontrols = Frm->ComponentCount;
    for(i=0; i<numcontrols; i++)
    {
      pComp=Frm->Components[i];

#ifdef USE_TnT
      {
        const TTntButton *pB = dynamic_cast<const TTntButton *>(pComp);
        if(pB!=NULL)
	{
          const AnsiString AS(pB->Caption);
          DumpStr(F,AS.c_str());
          continue;
	}
      }
      {
        const TTntLabel *Lbl = dynamic_cast<const TTntLabel *>(pComp);
        if(Lbl)
        {
          const AnsiString AS(Lbl->Caption);
          DumpStr(F,AS.c_str());
          continue;
        }
      }
      {
        const TTntBitBtn *BitBtn = dynamic_cast<const TTntBitBtn *>(pComp);
        if(BitBtn)
        {
          const AnsiString AS(BitBtn->Caption);
          DumpStr(F,AS.c_str());
          continue;
        }
      }
#endif
      {
        const TBitBtn *BitBtn = dynamic_cast<const TBitBtn *>(pComp);
        if(BitBtn)
          {DumpStr(F,BitBtn->Caption.c_str());continue;}
      }
      {
        const TButton *pB = dynamic_cast<const TButton *>(pComp);
        if(pB!=NULL) {DumpStr(F,pB->Caption.c_str());continue;}
      }
      {
        const TCheckBox *pCB=dynamic_cast<const TCheckBox *>(pComp);
        if(pCB!=NULL) {DumpStr(F,pCB->Caption.c_str());continue;}
      }
      {
        const TLabel *Lbl = dynamic_cast<const TLabel *>(pComp);
        if(Lbl) {DumpStr(F,Lbl->Caption.c_str());continue;}
      }
      {
        const TRadioButton *pRadBtn = dynamic_cast<const TRadioButton *>(pComp);
        if(pRadBtn) {DumpStr(F,pRadBtn->Caption.c_str());continue;}
      }
      {
        const TStaticText *STxt = dynamic_cast<const TStaticText *>(pComp);
        if(STxt) {DumpStr(F,STxt->Caption.c_str());continue;}
      }
      {
        const TTabControl *pTabC = dynamic_cast<const TTabControl *>(pComp);
        if(pTabC)
        {
	  for(j=0;j<pTabC->Tabs->Count;j++)
              DumpStr(F,pTabC->Tabs->Strings[j].c_str());
          continue;
        }
      }
      {
        const TGroupBox *pGrBox = dynamic_cast<const TGroupBox *>(pComp);
        if(pGrBox) {DumpStr(F,pGrBox->Caption.c_str());continue;}
      }
#if defined(TabnotbkHPP) && defined(USE_TabnotbkHPP)
      {
        const TTabbedNotebook *TabNtb = dynamic_cast<const TTabbedNotebook *>(pComp);
        if(TabNtb)
	{
	  for(j=0; j<TabNtb->Pages->Count; j++)
	      DumpStr(F,TabNtb->Pages->Strings[j].c_str());
	  continue;
	}
      }
#endif
      {
        const TMenuItem *pMenuIt = dynamic_cast<const TMenuItem *>(pComp);
        if(pMenuIt!=NULL)
          if(pMenuIt->Caption!=NULL && pMenuIt->Caption.Length()>0)
              {DumpStr(F,pMenuIt->Caption.c_str());continue;}
      }
    }

    if(--FormCount>0)
      {
      Frm = va_arg(ap,TForm *);
      }
    }

  fprintf(F,"}\n");
  fclose(F);
#endif
}


char *StrStr(char *str1, char *str2)
{
 if((str1==NULL)||(str2==NULL)) return(NULL);
 return(strstr(str1,str2));
}


AnsiString copy(const AnsiString &s, int from, int len)
{
  if(from>s.Length() || len<=0 || from<0 || s.Length()==0)
    {
    return AnsiString();
    }

  AnsiString temp(s.c_str()+from);
  temp.SetLength(len);
return temp;
}


AnsiString expand_spec(AnsiString name)
{
//#ifdef DEBUG
//  fprintf(cq->log,"\n!expand_spec(%s) ",name.ch);fflush(cq->log);
//#endif
int pos,end;
AnsiString temp;

while(StrStr(name.c_str(),"$(")!=NULL)
	{
	pos = StrStr(name.c_str(),"$(") - name.c_str();
	end = pos+2;
	while (name[end]!=0)
	   {
	   if (name[end]==')') break;
	   end++;
	   }
	if (name[end]==0) break;
	temp = copy(name,pos+2,end-pos-2);

        if (StrStr(temp.c_str(),"$(")!=NULL)
                 temp=expand_spec(temp);
//puts(temp.ch);
        if(temp!="")
		{
                temp=getenv(temp.c_str());
		}
//puts(temp.ch);
        name = copy(name,0,pos) + temp + copy(name,end+1,name.Length()-end-1);

        temp=name;//!!!!
//puts(temp.ch);
	}

return(temp);
}


/**This function reads one line from the text file and store it to the string.*/
AnsiString & fGets2(FILE *f, AnsiString & pstr, long MaxStrSize=0)
{
  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;
     if(MaxStrSize>0)
        if(--MaxStrSize==0) break;
     }

 return(pstr);
}


void Proces_File(const AnsiString filename)
{
//#ifdef DEBUG
//  fprintf(cq->log,"\n!Proces_File(%s) ",filename.ch);fflush(cq->log);
//#endif
FILE *f;
AnsiString line, temp;
int i;

if((f=fopen(filename.c_str(),"r"))==NULL) return;
while(!feof(f))
  {
  fGets2(f, line);
  line = line.Trim();
  if(line.Length()==0 || line=="")     continue;
  if(line[0]=='#') continue;

  temp="";
  for(i=0; i<line.Length(); i++)
           {
           if(isalpha(line[i])) temp+=line[i];
                           else break;
           }
  if(line[i++] != '=') continue;
  temp+='=';
  for(; i<line.Length(); i++)
           {
           if(!isalnum(line[i]) || (line[i]!='$') ||
                 (line[i]!='(') || (line[i]!=')') || (line[i]!='/') ||
                 (line[i]!='\\')|| (line[i]!=':')) temp+=line[i];
                                              else break;
           }
  if(temp!="") putenv(strdup(temp.c_str())); //The string must not be released!!!
//puts(temp.ch);
  }
fclose(f);
}


int GetPath(const char *FullName)
{
unsigned LastPos;
const char *NameStep;

 if(FullName==NULL) return(0);

 LastPos=0;
 NameStep=FullName;
 while(*NameStep!=0)
	{
	if(*NameStep==':')	/* Patch for MsDOS x: */
		{
		if(NameStep-FullName == 1)
			LastPos=NameStep-FullName;
		}
	if(*NameStep=='/' || *NameStep=='\\')
			LastPos=NameStep-FullName;

	NameStep++;
	}

return(LastPos);
}


/** This procedure provides inicialization  of gettext library.
  Its main task is to find a proper LOCALEDIR */
void InitGettext(const char *GtxtDomain)
{
//#ifdef DEBUG
//  fprintf(cq->log,"\n!InitGettext() ");fflush(cq->log);
//#endif
  AnsiString Locale_Dir;

#if defined(__DJGPP__) || defined(_WIN32)
  Proces_File(expand_spec("$(DJGPP)"));
#endif

  Locale_Dir = expand_spec("$(LOCALEDIR)");

#if defined(__DJGPP__) || defined(_WIN32)
      /* The default localedir for DJGPP is: $(DJDIR)/share/locale/  */
  if (Locale_Dir=="")
      {
      Locale_Dir = expand_spec("$(DJDIR)");
      if (Locale_Dir=="%:/>DJGPP%")
  	 {
  	 Locale_Dir=getenv("DJGPP");
  	 Locale_Dir=copy(Locale_Dir,0,GetPath(Locale_Dir.c_str()));
  	 }
      if (Locale_Dir!="")
         {
	 Locale_Dir+="/share/locale";
         }
      else Locale_Dir = "c:/usr/local/share/locale";
      }
#else           /* The default localedir for UNIX is: /usr/share/locale/  */
if (Locale_Dir=="")
      {
//    Locale_Dir = "/usr/local/share/locale";
      Locale_Dir = "/usr/share/locale";
      }
#endif

//putenv("LANGUAGE=cs");
//puts(Locale_Dir());
#if !defined(__BORLANDC__) || (__BORLANDC__<0x0540)
  setlocale(LC_ALL & ~LC_NUMERIC,"");				// This crashes on BCC6.
  //{char pokus[64];
  //snprintf(pokus,sizeof(pokus),"Ahoj %8f",rand()/100.0);}	// SetLocale sanity test.
#endif

  setlocale(LC_NUMERIC,"POSIX");
#ifdef __gettext__
  bindtextdomain(GtxtDomain,Locale_Dir.c_str());  //"g:/gcc/share/locale"
  textdomain(GtxtDomain);
#endif
  return;
}


void CheckGettext(const char *PROG_NAME, const char *GtxtDomain)
{
#ifdef __gettext__
int i;
AnsiString ProgPath;

  Empty2Info = _(GET_LOCALE_INFO);  //when gettext is active "" is expanded to the localisation details
  if(Empty2Info==NULL || *Empty2Info==0)
     {					// Try to bind locale to local directory
     i = GetPath(PROG_NAME);
     if(i<=0) ProgPath=".\\locale";
     else {
          if(*PROG_NAME=='"')
            ProgPath = copy(PROG_NAME,1,i-1)+"\\locale";
          else
	    ProgPath = copy(PROG_NAME,0,i)+"\\locale";
	  }
     bindtextdomain(GtxtDomain,ProgPath.c_str());
     Empty2Info = _(GET_LOCALE_INFO);
     }

  if(Empty2Info!=NULL && *Empty2Info!=0)
  {
    if(strstr(Empty2Info,"charset=UTF-8")!=NULL)
    {
      CodePage = UTF8;
      TranslateX = TranslateUTF8;
    }
    else
      TranslateX = Translate;
  }
  else
      TranslateX = Translate;
#endif
}

