/*

'Language Pack Manager'-Plugin for Miranda IM

Copyright (C) 2005-2007 H. Herkenrath

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 (LangMan-License.txt); if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "common.h"

/************************* Load ***************************************/

static void TrimString(char *str)
{
	int len,start;
	len = lstrlenA(str);
	while (str[0]!='\0' && (unsigned char)str[len-1]<=' ') str[--len]=0;
	for (start=0; str[start] && (unsigned char)str[start]<=' '; ++start);
	MoveMemory(str,str+start,len-start+1);
}

static BOOL IsEmpty(const char *str)
{
	int i;
	for(i=0;str[i]!='\0';i++)
		if(str[i]!=' ' && str[i]!='\r' && str[i]!='\n')
			return FALSE;
	return TRUE;
}

static void CleanupLanguage(char *szLanguage)
{
	char *p;
	/* remove any appended ' (default)' */
	p=strstr(szLanguage," (default)");
	if(p!=NULL) *p='\0';
}

static void CleanupAuthors(char *szAuthors)
{
	char *p,*p2;
	/* remove trailing dot (if any) */
	p=&szAuthors[lstrlenA(szAuthors)-1];
	if(*p=='.') *p='\0';
	/* remove any extra info in parentheses, which is ok
	 * but makes the list very long for some packs */
	for(;;) {
		p=strchr(szAuthors,'(');
		p2=strchr(szAuthors,')');
		if(p==NULL || p2==NULL) {
			p=strchr(szAuthors,'[');
			p2=strchr(szAuthors,']');
			if(p==NULL || p2==NULL) break;
		}
		if(*(p-1)==' ') --p;
		MoveMemory(p,p2+1,lstrlenA(p2+1)+1);
	}
}

static void CleanupEmail(char *szAuthorEmail)
{
	char c,*p,*pAt;
	/* replace ' dot ' with '.' (may be removed) */
	p=strstr(szAuthorEmail," dot ");
	if(p!=NULL) {
		*p='.';
		MoveMemory(p+1,p+5,lstrlenA(p+5)+1);
	}
	/* also allow ' at ' instead of '@' for obfuscation */
	p=strstr(szAuthorEmail," at ");
	if(p!=NULL) {
		*p='@';
		MoveMemory(p+1,p+4,lstrlenA(p+4)+1);
	}
	/* is valid? */
	pAt=strchr(szAuthorEmail,'@');
	if(pAt==NULL) {
		szAuthorEmail[0]='\0';
		return;
	}
	/* strip-off extra text except exactly one email address
	 * this is needed as a click on the email addres brings up the mail client */
	for(c=' ';;c=',') {
		p=strchr(pAt,c);
		if(p!=NULL) *p='\0';
		p=strrchr(szAuthorEmail,c);
		if(p!=NULL) MoveMemory(szAuthorEmail,p+1,lstrlenA(p+1)+1);
		if(c==',') break;
	}
	p=strstr(szAuthorEmail,"__");
	if(p!=NULL) MoveMemory(szAuthorEmail,p+2,lstrlenA(p+2)+1);
	/* lower case */
	CharLowerA(szAuthorEmail);
	/* 'none' specified */
	if(!lstrcmpiA(szAuthorEmail,"none")) szAuthorEmail[0]='\0';
}

static void CleanupLastModifiedUsing(char *szLastModifiedUsing,int nSize)
{
	char *p;
	/* remove 'Unicode', as it doesn't matter */
	p=strstr(szLastModifiedUsing," Unicode");
	if(p!=NULL) MoveMemory(p,p+8,lstrlenA(p+8)+1);
	/* use 'Miranda IM' instead of 'Miranda' */
	p=strstr(szLastModifiedUsing,"Miranda");
	if(p!=NULL && strncmp(p+7," IM",3)) {
		MoveMemory(p+10,p+7,lstrlenA(p+7)+1);
		CopyMemory(p+7," IM",3);
	}
	/* use 'Plugin' instead of 'plugin' */
	p=strstr(szLastModifiedUsing," plugin");
	if(p!=NULL) CopyMemory(p," Plugin",7);
	/* remove 'v' prefix */
	p=strstr(szLastModifiedUsing," v0.");
	if(p!=NULL) MoveMemory(p+1,p+2,lstrlenA(p+2)+1);
	/* default if empty */
	if(!szLastModifiedUsing[0]) {
		lstrcpynA(szLastModifiedUsing,MIRANDANAME" ",nSize);
		CallService(MS_SYSTEM_GETVERSIONTEXT,nSize-lstrlenA(szLastModifiedUsing),(LPARAM)szLastModifiedUsing+lstrlenA(szLastModifiedUsing));
	}
}

// pack struct should be initialized to zero before call
// pack->szFileName needs to be filled in before call
static BOOL LoadPackData(LANGPACK_INFO *pack,BOOL fEnabledPacks,const char *pszFileVersionHeader)
{
	FILE *fp;
	TCHAR szFileName[MAX_PATH];
	char line[4096],*pszColon,*buf;
	char szLanguageA[64]; /* same size as pack->szLanguage */
	/*
		Miranda Language Pack Version 1
		Language: (optional)
		Locale: 0809
		Authors: Miranda IM Development Team (multiple tags allowed)
		Author-email: project-info at miranda-im.org (" at " instead of "@" allowed)
		Last-Modified-Using: Miranda IM 0.7
		Plugins-included: (multiple tags allowed)
		X-FLName: name as used on the file listing (non-standard extension)
		X-Version: 1.2.3.4 (non-standard extension)
		see 'LangMan-Translation.txt' for some header quidelines 
	*/
	if ( !GetPackPath( szFileName, SIZEOF(szFileName), fEnabledPacks, pack->szFileName))
		return FALSE;

	fp = _tfopen(szFileName,_T("rt"));
	if (fp == NULL)
		return FALSE;

	fgets(line, sizeof(line), fp);
	TrimString(line);
	buf = line;

	if (strlen(line) >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf') {
		pack->codepage = CP_UTF8;
		buf += 3;
	}

	if ( lstrcmpA(buf, pszFileVersionHeader )) {
		fclose(fp);
		return FALSE;
	}
	pack->flags = LPF_NOLOCALE;
	szLanguageA[0] = '\0';
	while( !feof( fp )) {
		if ( fgets(line, sizeof(line), fp) == NULL) break;
		TrimString(line);
		if ( IsEmpty(line) || line[0]==';' || line[0]=='\0') continue;
		if ( line[0]=='[' ) break;
		pszColon = strchr(line,':');
		if ( pszColon == NULL ) continue;
		*pszColon = '\0';
		TrimString(pszColon+1);
		if ( !lstrcmpA(line,"Language") && !pack->szLanguage[0] )
			lstrcpynA(szLanguageA, pszColon+1, sizeof(szLanguageA)); /* buffer safe */
		else if ( !lstrcmpA(line, "Last-Modified-Using") && !pack->szLastModifiedUsing[0] )
			lstrcpynA(pack->szLastModifiedUsing, pszColon+1, sizeof(pack->szLastModifiedUsing)); /* buffer safe */
		else if ( !lstrcmpA(line, "Authors")) {
			buf=pack->szAuthors+lstrlenA(pack->szAuthors); /* allow multiple tags */
			if((sizeof(pack->szAuthors)-lstrlenA(pack->szAuthors))>0) /* buffer safe */
				mir_snprintf(buf,sizeof(pack->szAuthors)-lstrlenA(pack->szAuthors),(pack->szAuthors[0]=='\0')?"%s":" %s",pszColon+1);
		} else if ( !lstrcmpA(line, "Author-email") && !pack->szAuthorEmail[0])
			lstrcpynA(pack->szAuthorEmail, pszColon+1, sizeof(pack->szAuthorEmail)); /* buffer safe */
		else if ( !lstrcmpA(line,"Locale") && (pack->flags & LPF_NOLOCALE)) {
			pack->Locale = MAKELCID((USHORT)strtol(pszColon+1, NULL, 16), SORT_DEFAULT);
			if(pack->Locale) pack->flags &= ~LPF_NOLOCALE;
		}
		else if ( !lstrcmpA(line,"Plugins-included")) {
			buf = pack->szPluginsIncluded + lstrlenA(pack->szPluginsIncluded); /* allow multiple tags */
			if (( sizeof(pack->szPluginsIncluded)-lstrlenA(pack->szPluginsIncluded)) > 0 ) /* buffer safe */
				mir_snprintf(buf, sizeof(pack->szPluginsIncluded)-lstrlenA(pack->szPluginsIncluded),(pack->szPluginsIncluded[0]=='\0')?"%s":", %s",CharLowerA(pszColon+1));
		}
		else if ( !lstrcmpA(line,"X-Version") && !pack->szVersion[0] )
			lstrcpynA(pack->szVersion, pszColon+1, sizeof(pack->szVersion)); /* buffer safe */
		else if ( !lstrcmpA(line,"X-FLName") && !pack->szFLName[0] )
			lstrcpynA(pack->szFLName, pszColon+1, sizeof(pack->szFLName)); /* buffer safe */
	}
	CleanupLanguage(szLanguageA);
	CleanupAuthors(pack->szAuthors);
	CleanupEmail(pack->szAuthorEmail);
	CleanupLastModifiedUsing(pack->szLastModifiedUsing,sizeof(pack->szLastModifiedUsing));
	/* codepage */
	if(!(pack->flags&LPF_NOLOCALE))
		if(GetLocaleInfoA(pack->Locale,LOCALE_IDEFAULTANSICODEPAGE,line,6))
			pack->codepage=(WORD)atoi(line); /* CP_ACP on error */
	/* language */
#if defined(_UNICODE)
	MultiByteToWideChar(pack->codepage,0,szLanguageA,-1,pack->szLanguage,SIZEOF(pack->szLanguage));
#else
	lstrcpyA(pack->szLanguage,szLanguageA); /* buffer safe */
#endif
	/* ensure the pack always has a language name */
	if(!pack->szLanguage[0] && !GetLocaleInfo(pack->Locale,LOCALE_SENGLANGUAGE,pack->szLanguage,SIZEOF(pack->szLanguage))) {
		TCHAR *p;
		lstrcpyn(pack->szLanguage,pack->szFileName,SIZEOF(pack->szLanguage)); /* buffer safe */
		p=_tcsrchr(pack->szLanguage,_T('.'));
		if(p!=NULL) *p='\0';
	}
	/* ensure the pack always has a filelisting name */
	if(!pack->szFLName[0])
		lstrcatA(lstrcpyA(pack->szFLName,szLanguageA)," Language Pack"); /* buffer safe */
	fclose(fp);
	return TRUE;
}

/************************* Enum ***************************************/

BOOL GetPackPath(TCHAR *pszPath,int nSize,BOOL fEnabledPacks,const TCHAR *pszFile)
{
	TCHAR *p;
	/* main path */
	if(!GetModuleFileName(NULL,pszPath,nSize)) return FALSE;
	p=_tcsrchr(pszPath,_T('\\'));
	if(p!=NULL) *(p+1)=_T('\0');
	/* subdirectory */
	if(!fEnabledPacks) {
		if(nSize<(lstrlen(pszPath)+10)) return FALSE;
		lstrcat(pszPath,_T("Plugins\\Language\\"));
	}
	/* file name */
	if(pszFile!=NULL) {
		if(nSize<(lstrlen(pszFile)+11)) return FALSE;
		lstrcat(pszPath,pszFile);
	}
	return TRUE;
}

// callback is allowed to be NULL
// returns TRUE if any pack exists except default
BOOL EnumPacks(ENUM_PACKS_CALLBACK callback,const TCHAR *pszFilePattern,const char *pszFileVersionHeader,BOOL fEnglishDefault,WPARAM wParam,LPARAM lParam)
{
	BOOL fPackFound=FALSE;
	BOOL res=FALSE;
	LANGPACK_INFO pack;
	WIN32_FIND_DATA wfd;	
	HANDLE hFind;

	/* enabled packs */
	if(GetPackPath(pack.szFileName,SIZEOF(pack.szFileName),TRUE,pszFilePattern)) {
		hFind=FindFirstFile(pack.szFileName,&wfd);
		if(hFind!=INVALID_HANDLE_VALUE) {
			do {
				if(wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) continue;
				if((lstrlen(wfd.cFileName)<4) || wfd.cFileName[lstrlen(wfd.cFileName)-4]!=_T('.')) continue;
				/* get data */
				ZeroMemory(&pack,sizeof(pack));
				lstrcpy(pack.szFileName,CharLower(wfd.cFileName)); /* buffer safe */
				if(LoadPackData(&pack,TRUE,pszFileVersionHeader)) {
					pack.ftFileDate=wfd.ftLastWriteTime;
					/* enabled? */
					if(!fPackFound) pack.flags|=LPF_ENABLED;
					fPackFound=TRUE;
					/* callback */
					if(callback!=NULL) res=callback(&pack,wParam,lParam);
					if(!res) { FindClose(hFind); return FALSE; }
				}
			} while(FindNextFile(hFind,&wfd));
			FindClose(hFind);
		}
	}

	/* default: English (GB) */
	if(fEnglishDefault && callback!=NULL) {
		ZeroMemory(&pack,sizeof(pack));
		pack.Locale=LOCALE_USER_DEFAULT; /* miranda uses default locale in this case */
		lstrcpy(pack.szLanguage,_T("English (default)")); /* buffer safe */
		lstrcpyA(pack.szAuthors,"Miranda IM Development Team"); /* buffer safe */
		lstrcpyA(pack.szAuthorEmail,"project-info at miranda-im.org"); /* buffer safe */
		CleanupEmail(pack.szAuthorEmail); /* correct " at " */
		CleanupLastModifiedUsing(pack.szLastModifiedUsing,sizeof(pack.szLastModifiedUsing));
		/* file date */
		if(GetModuleFileName(NULL,pack.szFileName,SIZEOF(pack.szFileName))) {
			HANDLE hFile;
			hFile=CreateFile(pack.szFileName,0,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
			if(hFile!=INVALID_HANDLE_VALUE) {
				GetFileTime(hFile,NULL,NULL,&pack.ftFileDate);
				CloseHandle(hFile);
			}
		}
		pack.flags=LPF_NOLOCALE|LPF_DEFAULT;
		if(!fPackFound) pack.flags|=LPF_ENABLED;
		/* callback */
		if(!callback(&pack,wParam,lParam)) return FALSE;
	}

	/* disabled packs */
	if(GetPackPath(pack.szFileName,SIZEOF(pack.szFileName),FALSE,pszFilePattern)) {
		hFind=FindFirstFile(pack.szFileName, &wfd);
		if(hFind!=INVALID_HANDLE_VALUE) {
			do {
				if(wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) continue;
				if(lstrlen(wfd.cFileName)<4 || wfd.cFileName[lstrlen(wfd.cFileName)-4]!=_T('.')) continue;
				/* get data */
				ZeroMemory(&pack,sizeof(pack));
				lstrcpy(pack.szFileName,CharLower(wfd.cFileName)); /* buffer safe */
				if(LoadPackData(&pack,FALSE,pszFileVersionHeader)) {
					pack.ftFileDate=wfd.ftLastWriteTime;
					fPackFound=TRUE;
					/* callback */
					if(callback!=NULL) res=callback(&pack,wParam,lParam);
					if(!res) { FindClose(hFind); return FALSE; }
				}
			} while(FindNextFile(hFind,&wfd));
			FindClose(hFind);
		}
	}
	return fPackFound;
}

BOOL IsPluginIncluded(const LANGPACK_INFO *pack,char *pszFileBaseName)
{
	char *p;
	if(!lstrcmpiA(pszFileBaseName,"png2dib") || !lstrcmpiA(pszFileBaseName,"loadavatars"))
		return TRUE; /* workaround: does not need no translation */
	for(p=(char*)pack->szPluginsIncluded;;) {
		p=strstr(p,CharLowerA(pszFileBaseName));
		if(p==NULL) return FALSE;
		if(p==pack->szPluginsIncluded || *(p-1)==' ' || *(p-1)==',') {
			p+=lstrlenA(pszFileBaseName)+1;
			if(*p==',' || *p==' ' || *p==0) return TRUE;
		}
		else p+=lstrlenA(pszFileBaseName)+1;
	}
}

/************************* Switch *************************************/

BOOL EnablePack(const LANGPACK_INFO *pack,const TCHAR *pszFilePattern)
{
	TCHAR szFrom[MAX_PATH],szDest[MAX_PATH];
	HANDLE hFind;
	WIN32_FIND_DATA wfd;
	
	/* disable previous pack */
	if(GetPackPath(szFrom,SIZEOF(szFrom),TRUE,pszFilePattern)) {
		hFind=FindFirstFile(szFrom,&wfd);
		if(hFind!=INVALID_HANDLE_VALUE) {
			do {
				if(wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) continue;
				if(lstrlen(wfd.cFileName)<4 || wfd.cFileName[lstrlen(wfd.cFileName)-4]!=_T('.')) continue;
				/* ensure dir exists */
				if(GetPackPath(szFrom,SIZEOF(szFrom),FALSE,NULL))
					CreateDirectory(szFrom,NULL);
				/* move file */
				if(GetPackPath(szFrom,SIZEOF(szFrom),TRUE,wfd.cFileName))
					if(GetPackPath(szDest,SIZEOF(szDest),FALSE,wfd.cFileName))
						if(!MoveFile(szFrom,szDest) && GetLastError()==ERROR_ALREADY_EXISTS) {
							DeleteFile(szDest);
							MoveFile(szFrom,szDest);
						}
				break;
			} while(FindNextFile(hFind,&wfd));
			FindClose(hFind);
		}
	}

	/* enable current pack */
	if(pack->flags&LPF_DEFAULT) return TRUE;
	if(GetPackPath(szFrom,SIZEOF(szFrom),FALSE,pack->szFileName))
		if(GetPackPath(szDest,SIZEOF(szDest),TRUE,pack->szFileName)) 
			return MoveFile(szFrom,szDest);
	return FALSE;
}

void CorrectPacks(const TCHAR *pszFilePattern,BOOL fDisableAll)
{
	TCHAR szFrom[MAX_PATH],szDest[MAX_PATH],szDir[MAX_PATH],*pszFile;
	BOOL fDirCreated=FALSE;
	HANDLE hFind;
	WIN32_FIND_DATA wfd;

	/* main path */
	if(!GetModuleFileName(NULL,szDir,SIZEOF(szDir))) return;
	pszFile=_tcsrchr(szDir,_T('\\'));
	if(pszFile!=NULL) *pszFile=_T('\0');

	/* move wrongly placed packs from 'Plugins' to 'Language' */
	mir_sntprintf(szFrom,SIZEOF(szFrom),_T("%s\\Plugins\\%s"),szDir,pszFilePattern);
	hFind=FindFirstFile(szFrom,&wfd);
	if(hFind!=INVALID_HANDLE_VALUE) {
		do {
			if(wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) continue;
			if(lstrlen(wfd.cFileName)<4 || wfd.cFileName[lstrlen(wfd.cFileName)-4]!=_T('.')) continue;
			/* ensure dir exists */
			if(!fDirCreated && GetPackPath(szFrom,SIZEOF(szFrom),FALSE,NULL))
				fDirCreated=CreateDirectory(szFrom,NULL);
			/* move file */
			if(GetPackPath(szDest,SIZEOF(szDest),FALSE,wfd.cFileName)) {
				mir_sntprintf(szFrom,SIZEOF(szFrom),_T("%s\\Plugins\\%s"),szDir,wfd.cFileName);
				if(!MoveFile(szFrom,szDest) && GetLastError()==ERROR_ALREADY_EXISTS) {
					DeleteFile(szDest);
					MoveFile(szFrom,szDest);
				}
			}
		} while(FindNextFile(hFind,&wfd));
		FindClose(hFind);
	}

	/* disable all packs except one */
	if(GetPackPath(szFrom,SIZEOF(szFrom),TRUE,pszFilePattern)) {
		hFind=FindFirstFile(szFrom,&wfd);
		if(hFind!=INVALID_HANDLE_VALUE) {
			do {
				if(wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) continue;
				if(lstrlen(wfd.cFileName)<4 || wfd.cFileName[lstrlen(wfd.cFileName)-4]!=_T('.')) continue;
				/* skip first file */
				if(!fDisableAll) { fDisableAll=TRUE; continue; }
				/* ensure dir exists */
				if(!fDirCreated && GetPackPath(szFrom,SIZEOF(szFrom),FALSE,NULL))
					fDirCreated=CreateDirectory(szFrom,NULL);
				/* move file */
				if(GetPackPath(szFrom,SIZEOF(szFrom),TRUE,wfd.cFileName))
					if(GetPackPath(szDest,SIZEOF(szDest),FALSE,wfd.cFileName)) {
						if(!MoveFile(szFrom,szDest) && GetLastError()==ERROR_ALREADY_EXISTS) {
							DeleteFile(szDest);
							MoveFile(szFrom,szDest);
						}
					}
			} while(FindNextFile(hFind,&wfd));
			FindClose(hFind);
		}
	}
}