#include <all_far.h>

#include "Int.h"

//------------------------------------------------------------------------
FTPCopyInfo::FTPCopyInfo(void)
{
	asciiMode = FALSE;
	ShowProcessList = FALSE;
	AddToQueque = FALSE;
	MsgCode = ocNone;
	Download = FALSE;
	UploadLowCase = FALSE;
	FTPRename = FALSE;
}
//------------------------------------------------------------------------
void AddWait(time_t tm)
{
	for (int n = 0; n < 3; n++)
		if (FTPPanels[n] && FTPPanels[n]->hConnect && FTPPanels[n]->hConnect->IOCallback)
			FTPPanels[n]->hConnect->TrafficInfo->Waiting(tm);
}

//------------------------------------------------------------------------
BOOL WINAPI IsAbsolutePath(LPCSTR path)
{
	return *path == '/';
}
//------------------------------------------------------------------------
const char quotes[] = " \"%,;[]";

void WINAPI QuoteStr(char *str)
{
	char buff[1024], *m, *src;

	if (strpbrk(str, quotes) == NULL)
		return;

	m = buff;
	src = str;
	*m++ = '\"';

	for (size_t n = 0; n < ARRAYSIZE(buff) - 3 && *src; n++)
		if (*src == '\"') {
			*m++ = '\"';
			*m++ = '\"';
			n++;
			src++;
		} else
			*m++ = *src++;

	*m++ = '\"';
	*m = 0;
	strcpy(str, buff);
}

void WINAPI QuoteStr(String &str)
{
	String buff;

	if (str.Chr(quotes) == -1)
		return;

	buff.Add('\"');

	for (int n = 0; n < str.Length(); n++)
		if (str[n] == '\"') {
			buff.Add('\"');
			buff.Add('\"');
		} else
			buff.Add(str[n]);

	buff.Add('\"');
	str = buff;
}

//------------------------------------------------------------------------
#define SIZE_M 1024 * 1024
#define SIZE_K 1024

void WINAPI Size2Str(char *buff, DWORD sz)
{
	char letter = 0;
	double size = (double)sz;
	int rc;

	if (size >= SIZE_M) {
		size/= SIZE_M;
		letter = 'M';
	} else if (size >= SIZE_K) {
		size/= SIZE_K;
		letter = 'K';
	}

	if (!letter) {
		sprintf(buff, "%d", (int)sz);
		return;
	}

	sprintf(buff, "%f", size);
	rc = (int)strlen(buff);

	if (!rc || strchr(buff, '.') == NULL)
		return;

	for (rc--; rc && buff[rc] == '0'; rc--)
		;

	if (buff[rc] != '.')
		rc++;

	buff[rc] = letter;
	buff[rc + 1] = 0;
}

DWORD WINAPI Str2Size(char *str)
{
	int rc = (int)strlen(str);
	double sz;
	char letter;

	if (!rc)
		return 0;

	rc--;

	if (str[rc] == 'k' || str[rc] == 'K')
		letter = 'K';
	else if (str[rc] == 'm' || str[rc] == 'M')
		letter = 'M';
	else
		letter = 0;

	if (letter)
		str[rc] = 0;

	sz = atof(str);

	if (letter == 'K')
		sz*= SIZE_K;
	else if (letter == 'M')
		sz*= SIZE_M;

	return (DWORD)sz;
}
//------------------------------------------------------------------------
int WINAPI StrSlashCount(LPCSTR m)
{
	int cn = 0;

	if (m)
		for (; *m; m++)
			if (*m == '/')
				cn++;

	return cn;
}
//------------------------------------------------------------------------
BOOL WINAPI FTestOpen(LPCSTR nm)
{
	HANDLE f;
	BOOL rc;
	f = WINPORT(CreateFile)(MB2Wide(nm).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
			0, NULL);
	rc = f && f != INVALID_HANDLE_VALUE && WINPORT(GetFileType)(f) == FILE_TYPE_DISK;
	WINPORT(CloseHandle)(f);
	return rc;
}

BOOL WINAPI FRealFile(LPCSTR nm, FAR_FIND_DATA *fd)
{
	HANDLE f;
	BOOL rc;
	f = WINPORT(CreateFile)(MB2Wide(nm).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
			0, NULL);
	rc = f && f != INVALID_HANDLE_VALUE && WINPORT(GetFileType)(f) == FILE_TYPE_DISK;

	if (rc && fd) {
		strncpy(fd->cFileName, nm, ARRAYSIZE(fd->cFileName));
		fd->dwFileAttributes = WINPORT(GetFileAttributes)(MB2Wide(nm).c_str());
		fd->nFileSize = WINPORT(GetFileSize64)(f);
		fd->nPhysicalSize = 0;
		WINPORT(GetFileTime)(f, &fd->ftCreationTime, &fd->ftLastAccessTime, &fd->ftLastWriteTime);
	}

	WINPORT(CloseHandle)(f);
	return rc;
}

//------------------------------------------------------------------------
BOOL WINAPI DoCreateDirectory(char *directoryPath)
{
	PROC(("DoCreateDirectory", "[%s]", directoryPath));

	// Check directory
	if (!directoryPath || !*directoryPath) {
		Log(("Directory path is empty"));
		return TRUE;
	}

	if (sdc_mkdir(directoryPath, 0777) == 0 || errno == EEXIST)
		return TRUE;

	for (char *p = directoryPath + 1; *p; ++p) {
		if (*p == '/') {
			std::string tmp(directoryPath, p - directoryPath);
			sdc_mkdir(tmp.c_str(), 0777);
			// WINPORT(CreateDirectory)(tmp.c_str(), NULL);
		}
	}
	if (sdc_mkdir(directoryPath, 0777) == 0 || errno == EEXIST)
		return TRUE;

	Log(("CreateDirectory error: %d", WINPORT(GetLastError)()));
	return FALSE;

#if 0
	// Get full directory path
	char directoryPathFull[MAX_PATH], *lpFilePart;

	if(!GetFullPathName(directoryPath, MAX_PATH, directoryPathFull, &lpFilePart))
	{
		Log(("GetFullPathName error: %d", WINPORT(GetLastError)()));
		return FALSE;
	}

	// UNC network path flag
	bool isUncPath = false;
	// Folders only path
	char *directoriesPath = directoryPathFull;

	// Build folders only path
	if(StrCmp(directoriesPath, "\\\\", 2) == 0)
	{
		directoriesPath += 2;

		if(StrCmp(directoriesPath, "?\\", 2) == 0)
		{
			directoriesPath += 2;

			if(StrCmp(directoriesPath, "UNC\\", 4) == 0)
			{
				directoriesPath += 4;
				isUncPath = true;
			}
			else if(StrCmp(directoriesPath, "Volume{", 7) == 0)
			{
				directoriesPath += 7;
				directoriesPath = *directoriesPath ? strchr(++directoriesPath, '}') : NULL;

				if(!directoriesPath)
				{
					Log(("Volume name is not valid"));
					return FALSE;
				}

				directoriesPath++;
			}
			else if(*directoriesPath && StrCmp(directoriesPath+1, ":\\", 2) == 0)
			{
				directoriesPath += 3;
			}
		}
		else
		{
			isUncPath = true;
		}
	}
	else if(StrCmp(directoriesPath+1, ":\\", 2) == 0)
	{
		directoriesPath += 3;
	}

	if(isUncPath)
	{
		directoriesPath = strchr(directoriesPath, '/');

		if(!directoriesPath)
		{
			Log(("UNC path does not contains resource name"));
			return FALSE;
		}

		directoriesPath = strchr(++directoriesPath, '/');

		if(!directoriesPath)
		{
			return TRUE;
		}

		directoriesPath++;
	}

	// Root folder, no need to create
	if(!*directoriesPath)
		return TRUE;

	// Step by step create all directoriesPath structure (maybe SHCreateDirectoryEx?)
	char ch=0;

	do
	{
		directoriesPath = strchr(++directoriesPath, '/');

		if(directoriesPath)
		{
			ch = *directoriesPath;
			*directoriesPath = 0;
		}

		Log(("CreateDirectory: [%s]", directoryPathFull));

		if(!CreateDirectory(directoryPathFull, NULL))
		{
			if(WINPORT(GetLastError)() != ERROR_ALREADY_EXISTS)
			{
				Log(("CreateDirectory error: %d", WINPORT(GetLastError)()));
				return FALSE;
			}
		}

		if(directoriesPath)
		{
			*directoriesPath = ch;
		}
	}
	while(directoriesPath);
#endif
	return TRUE;
}

int64_t WINAPI Fsize(LPCSTR nm)
{
	struct stat s = {0};
	if (sdc_stat(nm, &s) < 0)
		return 0;

	return s.st_size;
}

int64_t WINAPI Fsize(HANDLE File)
{
	DWORD low, hi;
	low = WINPORT(GetFileSize)(File, &hi);

	if (low == MAX_DWORD)
		return 0;
	else
		return ((int64_t)hi) << 32 | low;
}

BOOL WINAPI Fmove(HANDLE file, int64_t restart)
{
	LONG lo = (DWORD)(restart & MAX_DWORD), hi = (DWORD)((restart >> 32) & MAX_DWORD);

	if (WINPORT(SetFilePointer)(file, lo, &hi, FILE_BEGIN) == 0xFFFFFFFF
			&& WINPORT(GetLastError)() != NO_ERROR)
		return FALSE;

	return TRUE;
}

void WINAPI Fclose(HANDLE file)
{
	if (file) {
		WINPORT(SetEndOfFile)(file);
		WINPORT(CloseHandle)(file);
	}
}

BOOL WINAPI Ftrunc(HANDLE h, DWORD move)
{
	if (move != FILE_CURRENT)
		if (WINPORT(SetFilePointer)(h, 0, NULL, move) == 0xFFFFFFFF)
			return FALSE;

	return WINPORT(SetEndOfFile)(h);
}

HANDLE WINAPI Fopen(LPCSTR nm, LPCSTR mode /*R|W|A[+]*/, DWORD attr)
{
	BOOL rd = toupper(mode[0]) == 'R';
	HANDLE h;

	if (rd)
		h = WINPORT(CreateFile)(MB2Wide(nm).c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
				OPEN_EXISTING, attr, NULL);
	else
		h = WINPORT(CreateFile)(MB2Wide(nm).c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, attr,
				NULL);

	if (!h || h == INVALID_HANDLE_VALUE)
		return NULL;

	do {
		if (toupper(mode[0]) == 'A' || mode[1] == '+') {
			LONG nHighPart = 0;

			if ((WINPORT(SetFilePointer)(h, 0, &nHighPart, FILE_END) == INVALID_SET_FILE_POINTER)
					&& (WINPORT(GetLastError)() != NO_ERROR))
				break;
		}

		if (!rd)
			WINPORT(SetEndOfFile)(h);	// Ignore SetEndOfFile result in case of use with CON, NUL and others

		return h;
	} while (0);

	WINPORT(CloseHandle)(h);
	return NULL;
}

int WINAPI Fwrite(HANDLE File, LPCVOID Buff, int Size)
{
	DWORD res;
	return WINPORT(WriteFile)(File, Buff, (DWORD)Size, &res, NULL) ? ((int)res) : (-1);
}

int WINAPI Fread(HANDLE File, LPVOID Buff, int Size)
{
	DWORD res;
	return WINPORT(ReadFile)(File, Buff, (DWORD)Size, &res, NULL) ? ((int)res) : (-1);
}

void DMessage(LPCSTR str, BOOL full, int color, int y)
{
	char err[MAX_PATH];

	if (full) {
		StrCpy(err, str, ARRAYSIZE(err));
		int len = (int)strlen(err), w = Min((int)ARRAYSIZE(err) - 1, FP_ConWidth() - 4);

		while (len < w)
			err[len++] = ' ';

		err[len] = 0;
		FP_Info->Text(2, y, color, err);
	} else
		FP_Info->Text(2, y, color, str);
}

void WINAPI IdleMessage(LPCSTR str, int color)
{
	static HANDLE hScreen;

	// Clear
	if (!str) {
		if (hScreen) {
			FP_Info->RestoreScreen(hScreen);
			hScreen = NULL;
		}

		return;
	}

	// Draw
	LPCSTR msg = FP_GetMsg(str);

	if (IS_FLAG(Opt.IdleMode, IDLE_CAPTION))
		SaveConsoleTitle::Text(msg);

	if (IS_FLAG(Opt.IdleMode, IDLE_CONSOLE)) {
		DWORD er = WINPORT(GetLastError)();
		BOOL err = er != ERROR_CANCELLED && er != ERROR_SUCCESS && er != ERROR_NO_MORE_FILES;

		if (!hScreen)
			hScreen = FP_Info->SaveScreen(0, 0, FP_ConWidth(), 2);

		DMessage(msg, err, color, 0);

		if (err)
			DMessage(__WINError(), err, color, 1);

		FP_Info->Text(0, 0, 0, NULL);
	}
}

int WINAPI FMessage(unsigned int Flags, LPCSTR HelpTopic, LPCSTR *Items, int ItemsNumber, int ButtonsNumber)
{
	time_t b = time(NULL);
	BOOL delayed;
	int rc;
	rc = FP_Message(Flags, HelpTopic, Items, ItemsNumber, ButtonsNumber, &delayed);

	if (delayed)
		AddWait(time(NULL) - b);

	return rc;
}

int WINAPI FDialog(int X2, int Y2, LPCSTR HelpTopic, struct FarDialogItem *Item, int ItemsNumber)
{
	time_t b = time(NULL);
	int rc;
	rc = FP_Info->Dialog(FP_Info->ModuleNumber, -1, -1, X2, Y2, HelpTopic, Item, ItemsNumber);
	AddWait(time(NULL) - b);
	return rc;
}

int WINAPI FDialogEx(int X2, int Y2, LPCSTR HelpTopic, struct FarDialogItem *Item, int ItemsNumber,
		DWORD Flags, FARWINDOWPROC DlgProc, LONG_PTR Param)
{
	time_t b = time(NULL);
	int rc;

	if (DlgProc == (FARWINDOWPROC)(size_t)-1)
		DlgProc = FP_Info->DefDlgProc;

	rc = FP_Info->DialogEx(FP_Info->ModuleNumber, -1, -1, X2, Y2, HelpTopic, Item, ItemsNumber, 0, Flags,
			DlgProc, Param);
	AddWait(time(NULL) - b);
	return rc;
}

void WINAPI AddEndSlash(String &p, char slash)
{
	if (!p.Length())
		return;

	if (!slash)
		slash = '/';

	if (p[p.Length() - 1] != slash)
		p.Add(slash);
}

void WINAPI AddEndSlash(char *Path, char slash, size_t ssz)
{
	size_t Length;

	if (!Path || !Path[0])
		return;

	Length = strlen(Path) - 1;

	if (Length <= 0 || Length >= ssz)
		return;

	if (!slash)
		slash = '/';

	if (Path[Length] != slash) {
		Path[Length + 1] = slash;
		Path[Length + 2] = 0;
	}
}

void WINAPI DelEndSlash(String &p, char shash)
{
	int len;

	if ((len = p.Length() - 1) >= 0 && p[len] == shash)
		p.SetLength(len);
}

void WINAPI DelEndSlash(char *Path, char shash)
{

	if (Path && *Path) {
		size_t len = strlen(Path);
		if (Path[len - 1] == shash) {
			Path[len - 1] = 0;
		}
	}
}

char *WINAPI TruncStr(char *Str, int MaxLength)
{
	int Length;

	if ((Length = static_cast<int>(strlen(Str))) > MaxLength) {
		if (MaxLength > 3) {
			char *TmpStr = new char[MaxLength + 5];
			sprintf(TmpStr, "...%s", Str + Length - MaxLength + 3);
			strcpy(Str, TmpStr);
			delete[] TmpStr;
		} else
			Str[MaxLength] = 0;
	}

	return (Str);
}

char *WINAPI PointToName(char *Path)
{
	char *NamePtr = Path;

	while (*Path) {
		if (*Path == '/')
			NamePtr = Path + 1;

		Path++;
	}

	return NamePtr;
}

BOOL WINAPI CheckForEsc(BOOL isConnection, BOOL IgnoreSilent)
{
	WORD ESCCode = VK_ESCAPE;
	BOOL rc;

	if (!IgnoreSilent && IS_FLAG(FP_LastOpMode, OPM_FIND))
		return FALSE;

	rc = CheckForKeyPressed(&ESCCode, 1);

	if (!rc)
		return FALSE;

	rc = !Opt.AskAbort || AskYesNo(FMSG(isConnection ? MTerminateConnection : MTerminateOp));

	if (rc) {
		Log(("ESC: cancel detected"));
	}

	return rc;
}

BOOL WINAPI IsDirExist(LPCSTR nm)
{
	struct stat s = {0};
	if (sdc_stat(nm, &s) < 0)
		return FALSE;

	if ((s.st_mode & S_IFMT) != S_IFDIR)
		return FALSE;

	return TRUE;
}

void WINAPI FixFTPSlash(String &s)
{
	FixFTPSlash((char *)s.c_str());
}
void WINAPI FixFTPSlash(char *s)
{
	if (!s)
		return;
}

void WINAPI FixLocalSlash(String &s)
{
	FixLocalSlash((char *)s.c_str());
}
void WINAPI FixLocalSlash(char *s) {}
