//---------------------------------------------------------------------------
//File name:    lang.c
//---------------------------------------------------------------------------
#include "launchelf.h"

#define lang(id, name, value) u8 Str_##name [] = value;
#include "lang.h"
#undef lang

Language Lang_Default[]={
#define lang(id, name, value) {Str_##name},
#include "lang.h"
#undef lang
	{NULL}
};

Language Lang_String[sizeof(Lang_Default) / sizeof(Lang_Default[0])];
Language Lang_Extern[sizeof(Lang_Default) / sizeof(Lang_Default[0])];

Language *External_Lang_Buffer = NULL;

//---------------------------------------------------------------------------
// get_LANG_string is the main parser called for each language dependent
// string in a language header file. (eg: "Francais.h" or "Francais.lng")
// Call values for all input arguments should be addresses of string pointers
// LANG_p_p is for file to be scanned, moved to point beyond scanned data.
// id_p_p is for a string defining the index value (suitable for 'atoi')
// value_p_p is for the string value itself (not NUL-terminated)
// The function returns the length of each string found, but -1 at EOF,
// and various error codes less than -1 (-2 etc) for various syntax errors,
// which also applies to EOF occurring where valid macro parts are expected.
//---------------------------------------------------------------------------
int	get_LANG_string(u8 **LANG_p_p, u8 **id_p_p, u8 **value_p_p)
{
	u8 *cp, *ip, *vp, *tp = *LANG_p_p;
	int ret, length;

	ip = NULL;
	vp = NULL;
	ret = -1;

start_line:
	while(*tp<=' ' && *tp>'\0') tp+=1;     //Skip leading whitespace, if any
	if(*tp=='\0')	goto exit;               //but exit at EOF
	//Current pos is potential "lang(" entry, but we must verify this
	if(tp[0]=='/' && tp[1]=='/')           //It may be a comment line
	{                                      //We must skip a comment line
		while(*tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1;  //Seek line end
		goto start_line;                     //Go back to try next line
	}
	ret = -2;
	//Here tp points to a non-zero string that is not a comment
	if(strncmp(tp, "lang", 4)) goto exit;  //Return error if not 'lang' macro
	tp+=4;                                 //but if it is, step past that name
	ret = -3;
	while(*tp<=' ' && *tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1; //skip inline whitespace
	if(*tp=='\0')	goto exit;               //but exit at EOF
	ret = -4;
	//Here tp points to a non-zero string that should be an opening parenthesis
	if(*tp!='(') goto exit;                //Return error if no opening parenthesis
	tp+=1;                                 //but if it is, step past this character
	ret = -5;
	while(*tp<=' ' && *tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1; //skip inline whitespace
	if(*tp=='\0')	goto exit;               //but exit at EOF
	ret = -6;
	//Here tp points to a non-zero string that should be an index number
	if(*tp<'0' || *tp>'9') goto exit;      //Return error if it's not a number
	ip = tp;                               //but if it is, save this pos as id start
	while(*tp>='0' && *tp<='9') tp+=1;     //skip past the index number
	ret = -7;
	while(*tp<=' ' && *tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1; //skip inline whitespace
	if(*tp=='\0')	goto exit;               //but exit at EOF
	ret = -8;
	//Here tp points to a non-zero string that should be a comma
	if(*tp!=',') goto exit;                //Return error if no comma after index
	tp+=1;                                 //but if present, step past that comma
	ret = -9;
	while(*tp<=' ' && *tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1; //skip inline whitespace
	if(*tp=='\0')	goto exit;               //but exit at EOF
	ret = -10;
	//Here tp points to a non-zero string that should be a symbolic string name
	//But we don't need to process this for language switch purposes, so we ignore it
	//This may be changed later, to use the name for generating error messages
	while(*tp!=',' && *tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1; //seek inline comma
	if(*tp!=',') goto exit;                //Return error if no comma after string name
	tp+=1;                                 //but if present, step past that comma
	ret = -11;
	while(*tp<=' ' && *tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1; //skip inline whitespace
	if(*tp=='\0')	goto exit;               //but exit at EOF
	ret = -12;
	//Here tp points to a non-zero string that should be the opening quote character
	if(*tp!='\"') goto exit;               //Return error if no opening quote
	tp+=1;                                 //but if present, step past that quote
	ret = -13;
	vp = tp;                               //save this pos as value start
close_quote:
	while(*tp!='\"' && *tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1; //seek inline quote
	if(*tp!='\"') return -13;              //Return error if no closing quote
  cp = tp-1;                             //save previous pos as check pointer
  tp+=1;                                 //step past the quote character
  if(*cp=='\\') goto close_quote;        //if this was an 'escaped' quote, try again
  //Here tp points to the character after the closing quote.
  length = (tp-1) - vp;                  //prepare string length for return value
	ret = -14;
	while(*tp<=' ' && *tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1; //skip inline whitespace
	if(*tp=='\0')	goto exit;               //but exit at EOF
	ret = -15;
	//Here tp points to a non-zero string that should be closing parenthesis
	if(*tp!=')') goto exit;                //Return error if no closing parenthesis
	tp+=1;                                 //but if present, step past the parenthesis
	ret = -16;
	while(*tp<=' ' && *tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1; //skip inline whitespace
	if(*tp=='\0')	goto exit;              //but exit at EOF
	//Here tp points to a non-zero string that should be line end or a comment
	if(tp[0]!='/' || tp[1]!='/') goto finish_line; //if no comment, go handle line end
	ret = -17;
	while(*tp!='\r' && *tp!='\n' && *tp>'\0') tp+=1;  //Seek line end
	if(*tp=='\0')	goto exit;              //but exit at EOF
finish_line:
	ret = -18;
	if(*tp!='\r' && *tp!='\n') goto exit;  //Return error if not valid line end
	if(tp[0]=='\r' && tp[1]=='\n') tp+=1;  //Step an extra pos for CR+LF
	tp+=1;                                 //Step past valid line end
  //Here tp points beyond the line of the processed string, so we're done
  ret = length;

exit:
	*LANG_p_p = tp;                        //return new LANG file position
	*id_p_p = ip;                          //return found index
	*value_p_p = vp;                       //return found string value
	return ret;                           //return control to caller
}
//Ends get_LANG_string
//---------------------------------------------------------------------------
void Init_Default_Language(void)
{
	memcpy(Lang_String, Lang_Default, sizeof(Lang_String));
}
//Ends Init_Default_Language
//---------------------------------------------------------------------------
void Load_External_Language(void)
{
	int error_id = -1;
	int test = 0;
	u32 index = 0;
	char filePath[MAX_PATH];
	u8 *file_bp, *file_tp, *lang_bp, *lang_tp, *oldf_tp=NULL;
	u8 *id_p, *value_p;
	int lang_size = 0;
	int fd;

	if(External_Lang_Buffer!=NULL){  //if an external buffer was allocated before
		free(External_Lang_Buffer);    //release that buffer before the new attempt
		External_Lang_Buffer = NULL;
	}

	Language* Lang = Lang_Default;
	memcpy(Lang_String, Lang, sizeof(Lang_String));

	if(strlen(setting->lang_file)!=0){ //if language file string set

		error_id = -2;
		genFixPath(setting->lang_file, filePath);
		fd = genOpen(filePath, O_RDONLY);
		if(fd >= 0){                         //if file opened OK
    	int file_size = genLseek(fd, 0, SEEK_END);

			error_id = -3;
    	if (file_size > 0) {               //if file size OK
				error_id = -4;
 	 			file_bp = (u8*) malloc(file_size + 1);
 	 			if(file_bp == NULL)
 	 				goto aborted_1;

				error_id = -5;
				genLseek(fd, 0, SEEK_SET);
				if(genRead(fd, file_bp, file_size) != file_size)
					goto release_1;
				file_bp[file_size] = '\0'; //enforce termination at buffer end

				error_id = -6;
				file_tp = file_bp;
				while(1){
					oldf_tp = file_tp;
					test = get_LANG_string(&file_tp, &id_p, &value_p);
					if(test==-1)                   //if EOF reached without other error
						break;                         //break from the loop normally
					if(test<0)                     //At any fatal error result
						goto release_1;                //go release file buffer
					index = atoi(id_p);            //get the string index
					if(index>=LANG_COUNT)          //At any fatal error result
						goto release_1;                //go release file buffer
					lang_size += test + 1;         //Include terminator space for total size
				}
				//Here lang_size is the space needed for real language buffer,

				error_id = -7;
 	 			lang_bp = (u8*) malloc(lang_size + 1); //allocate real language buffer
 	 			if(lang_bp == NULL)
 	 				goto release_1;

				//We're ready to read language strings, but must first init all pointers
				//to use default strings, for any indexes left undefined by the file
				memcpy(Lang_Extern, Lang, sizeof(Lang_Extern));

 	 			file_tp = file_bp;
 	 			lang_tp = lang_bp;
				while((test = get_LANG_string(&file_tp, &id_p, &value_p)) >= 0){
					index = atoi(id_p);                  //get the string index
					Lang_Extern[index].String = lang_tp; //save pointer to this string base
					strncpy(lang_tp, value_p, test);     //transfer the string
					lang_tp[test] = '\0';                //transfer a terminator
					lang_tp += test + 1;                 //move dest pointer past this string
				}
				External_Lang_Buffer = (Language*) lang_bp;  //Save base pointer for releases
				Lang = Lang_Extern;
				error_id = 0;
release_1:
				free(file_bp);
			}  // end if clause for file size OK
aborted_1:
			genClose( fd );
		}  // end if clause for file opened OK
	} // end if language file string set

	if(error_id < -1){
		u8 tmp_s[80*8], t1_s[102], t2_s[102];
		int pos=0, stp=0;
		sprintf(tmp_s,
			"LNG loading failed with error_id==%d and test==%d\n"
			"The latest string index (possibly invalid) was %d\n"
			"%n"
			, error_id, test, index, &stp
		);
		pos += stp;
		if(error_id==-2) {//if file open failure
			sprintf(tmp_s+pos,
				"This was a failure to open the file:\n"
				"\"%s\"\n"
				, filePath
			);
		}
		if(error_id==-6) {//if parsing error
			strncpy(t1_s, oldf_tp, 100);
			t1_s[100] = '\0';
			strncpy(t2_s, file_tp, 100);
			t2_s[100] = '\0';
			sprintf(tmp_s+pos,
				"This was a parsing error when trying to parse the text:\n"
				"\"%s\"\n"
				"That attempt failed somehow, after reaching this point:\n"
				"\"%s\"\n"
				, t1_s, t2_s
			);
		}
		strcat(tmp_s, "Use either OK or CANCEL to continue (no diff)");
		ynDialog(tmp_s);
	}

	memcpy(Lang_String, Lang, sizeof(Lang_String));

	int i;
	char *tmp;

	if(strlen(setting->Misc)>0){
		for(i=0; i<16; i++){	//Loop to rename the ELF paths with new language for launch keys
			if((i<12) || (setting->LK_Flag[i]!=0)){
				if(!strncmp(setting->LK_Path[i], setting->Misc, strlen(setting->Misc))){
					tmp  = strrchr(setting->LK_Path[i], '/');
					if(!strcmp(tmp+1, setting->Misc_PS2Disc+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(PS2Disc));
					else if(!strcmp(tmp+1, setting->Misc_FileBrowser+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(FileBrowser));
					else if(!strcmp(tmp+1, setting->Misc_PS2Browser+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(PS2Browser));
					else if(!strcmp(tmp+1, setting->Misc_PS2Net+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(PS2Net));
					else if(!strcmp(tmp+1, setting->Misc_PS2PowerOff+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(PS2PowerOff));
					else if(!strcmp(tmp+1, setting->Misc_HddManager+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(HddManager));
					else if(!strcmp(tmp+1, setting->Misc_TextEditor+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(TextEditor));
					else if(!strcmp(tmp+1, setting->Misc_JpgViewer+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(JpgViewer));
					else if(!strcmp(tmp+1, setting->Misc_Configure+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(Configure));
					else if(!strcmp(tmp+1, setting->Misc_Load_CNFprev+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(Load_CNFprev));
					else if(!strcmp(tmp+1, setting->Misc_Load_CNFnext+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(Load_CNFnext));
					else if(!strcmp(tmp+1, setting->Misc_Set_CNF_Path+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(Set_CNF_Path));
					else if(!strcmp(tmp+1, setting->Misc_Load_CNF+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(Load_CNF));
					else if(!strcmp(tmp+1, setting->Misc_ShowFont+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(ShowFont));
					else if(!strcmp(tmp+1, setting->Misc_Debug_Info+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(Debug_Info));
					else if(!strcmp(tmp+1, setting->Misc_About_uLE+strlen(setting->Misc)))
						sprintf(setting->LK_Path[i], "%s/%s", LNG(MISC), LNG(About_uLE));
				} // end if Misc
			} // end if LK assigned
		} // end for
	} // end if Misc Initialized

	sprintf(setting->Misc              , "%s/",   LNG(MISC));
	sprintf(setting->Misc_PS2Disc      , "%s/%s", LNG(MISC), LNG(PS2Disc));
	sprintf(setting->Misc_FileBrowser  , "%s/%s", LNG(MISC), LNG(FileBrowser));
	sprintf(setting->Misc_PS2Browser   , "%s/%s", LNG(MISC), LNG(PS2Browser));
	sprintf(setting->Misc_PS2Net       , "%s/%s", LNG(MISC), LNG(PS2Net));
	sprintf(setting->Misc_PS2PowerOff  , "%s/%s", LNG(MISC), LNG(PS2PowerOff));
	sprintf(setting->Misc_HddManager   , "%s/%s", LNG(MISC), LNG(HddManager));
	sprintf(setting->Misc_TextEditor   , "%s/%s", LNG(MISC), LNG(TextEditor));
	sprintf(setting->Misc_JpgViewer    , "%s/%s", LNG(MISC), LNG(JpgViewer));
	sprintf(setting->Misc_Configure    , "%s/%s", LNG(MISC), LNG(Configure));
	sprintf(setting->Misc_Load_CNFprev , "%s/%s", LNG(MISC), LNG(Load_CNFprev));
	sprintf(setting->Misc_Load_CNFnext , "%s/%s", LNG(MISC), LNG(Load_CNFnext));
	sprintf(setting->Misc_Set_CNF_Path , "%s/%s", LNG(MISC), LNG(Set_CNF_Path));
	sprintf(setting->Misc_Load_CNF     , "%s/%s", LNG(MISC), LNG(Load_CNF));
	sprintf(setting->Misc_ShowFont     , "%s/%s", LNG(MISC), LNG(ShowFont));
	sprintf(setting->Misc_Debug_Info   , "%s/%s", LNG(MISC), LNG(Debug_Info));
	sprintf(setting->Misc_About_uLE    , "%s/%s", LNG(MISC), LNG(About_uLE));

}
//Ends Load_External_Language
//---------------------------------------------------------------------------
//End of file:  lang.c
//---------------------------------------------------------------------------
