#include <all_far.h>

#include "Int.h"

int AskDeleteQueue(void)
{
	LPCSTR MsgItems[] = {FMSG(MAttention), FMSG(MQDeleteItem), FMSG(MQSingleItem), FMSG(MQEntireList),
			FMSG(MCancel)};
	return FMessage(FMSG_WARNING, NULL, MsgItems, ARRAYSIZE(MsgItems), 3);
}

void FTP::ClearQueue(void)
{
	FTPUrl *p, *p1;

	for (p = UrlsList; p; p = p1) {
		p1 = p->Next;
		delete p;
	}

	UrlsList = UrlsTail = NULL;
	QuequeSize = 0;
}

BOOL FTP::WarnExecuteQueue(QueueExecOptions *op)
{
	InitDialogItem InitItems[] = {
			{DI_DOUBLEBOX, 3, 1, 72, 9, 0,                            0, 0, 0, FMSG(MQueueParam)},

			{DI_CHECKBOX,  5, 3, 0,  0, 0,                            0, 0, 0, FMSG(MQRestore)  },
			{DI_CHECKBOX,  5, 4, 0,  0, 0,                            0, 0, 0, FMSG(MQRemove)   },

			{DI_CHECKBOX,  5, 6, 0,  0, 0,                            0, 0, 0, FMSG(MQSave)     },

			{DI_TEXT,      3, 7, 3,  7, DIF_BOXCOLOR | DIF_SEPARATOR, 0, 0, 0, NULL             },
			{DI_BUTTON,    0, 8, 0,  0, DIF_CENTERGROUP,              0, 0, 1, FMSG(MOk)        },
			{DI_BUTTON,    0, 8, 0,  0, DIF_CENTERGROUP,              0, 0, 0, FMSG(MCancel)    },
	};
	FarDialogItem DialogItems[ARRAYSIZE(InitItems)];
	// Create items
	InitDialogItems(InitItems, DialogItems, ARRAYSIZE(DialogItems));
	// Set flags
	// Flags
	DialogItems[1].Selected = op->RestoreState;
	DialogItems[2].Selected = op->RemoveCompleted;

	// Dialog
	if (FDialog(76, 11, "FTPProcessQueue", DialogItems, ARRAYSIZE(DialogItems)) != 5)
		return FALSE;

	// Get paras
	// Flags
	op->RestoreState = DialogItems[1].Selected;
	op->RemoveCompleted = DialogItems[2].Selected;

	// Save to default
	if (DialogItems[3].Selected) {
		Opt.RestoreState = op->RestoreState;
		Opt.RemoveCompleted = op->RemoveCompleted;
		FP_SetRegKey("QueueRestoreState", Opt.RestoreState);
		FP_SetRegKey("QueueRemoveCompleted", Opt.RemoveCompleted);
	}

	return TRUE;
}

void FTP::SetupQOpt(QueueExecOptions *op)
{
	op->RestoreState = Opt.RestoreState;
	op->RemoveCompleted = Opt.RemoveCompleted;
}

LPCSTR FTP::InsertCurrentToQueue(void)
{
	PanelInfo pi, api;
	FP_SizeItemList backup, il;
	FTPCopyInfo ci;

	if (!FP_Info->Control(this, FCTL_GETPANELINFO, &pi)
			|| !FP_Info->Control(INVALID_HANDLE_VALUE, FCTL_GETANOTHERPANELSHORTINFO, &api))
		return FMSG(MErrGetPanelInfo);

	if (pi.SelectedItemsNumber <= 0
			|| (pi.SelectedItemsNumber == 1 && !IS_FLAG(pi.SelectedItems[0].Flags, PPIF_SELECTED)))
		return FMSG(MErrNoSelection);

	backup.Add(pi.SelectedItems, pi.SelectedItemsNumber);
	BOOL rc = ExpandList(backup.Items(), backup.Count(), &il, TRUE);
	FP_Screen::FullRestore();

	if (!rc)
		return WINPORT(GetLastError)() == ERROR_CANCELLED ? NULL : FMSG(MErrExpandList);

	ci.Download = TRUE;

	if (api.PanelType != PTYPE_FILEPANEL || api.Plugin)
		ci.DestPath = "";
	else
		ci.DestPath = api.CurDir;

	ListToQueque(&il, &ci);
	return NULL;
}

LPCSTR FTP::InsertAnotherToQueue(void)
{
	FP_SizeItemList backup, il;
	PanelInfo pi;
	FTPCopyInfo ci;

	if (!hConnect || ShowHosts)
		return FMSG(MQErrUploadHosts);

	if (!FP_Info->Control(INVALID_HANDLE_VALUE, FCTL_GETANOTHERPANELINFO, &pi))
		return FMSG(MErrGetPanelInfo);

	if (pi.SelectedItemsNumber <= 0
			|| (pi.SelectedItemsNumber == 1 && !IS_FLAG(pi.SelectedItems[0].Flags, PPIF_SELECTED)))
		return FMSG(MErrNoSelection);

	if (pi.PanelType != PTYPE_FILEPANEL || pi.Plugin)
		return FMSG(MErrNotFiles);

	backup.Add(pi.SelectedItems, pi.SelectedItemsNumber);
	BOOL rc = ExpandList(backup.Items(), backup.Count(), &il, FALSE);
	FP_Screen::FullRestore();

	if (!rc)
		return WINPORT(GetLastError)() == ERROR_CANCELLED ? NULL : FMSG(MErrExpandList);

	ci.Download = FALSE;
	GetCurPath(ci.DestPath);
	ListToQueque(&il, &ci);
	return NULL;
}

void FTP::InsertToQueue(void)
{
	static LPCSTR strings[] = {FMSG(MQISingle), FMSG(MQIFTP), FMSG(MQIAnother), NULL};
	FP_Menu mnu(strings);
	int sel;
	LPCSTR err;
	FTPUrl tmp;

	do {
		sel = mnu.Execute(FMSG(MQAddTitle), FMENU_WRAPMODE, NULL, "FTPQueueAddItem");

		if (sel == -1)
			return;

		err = NULL;

		switch (sel) {
			case 0:
				UrlInit(&tmp);

				if (EditUrlItem(&tmp)) {
					AddToQueque(&tmp);
					return;
				}

				break;
			case 1:
				err = InsertCurrentToQueue();

				if (!err && WINPORT(GetLastError)() != ERROR_CANCELLED)
					return;

				break;
			case 2:
				err = InsertAnotherToQueue();

				if (!err && WINPORT(GetLastError)() != ERROR_CANCELLED)
					return;

				break;
		}

		if (err) {
			static LPCSTR itms[] = {FMSG(MQErrAdding), NULL, FMSG(MOk)};
			itms[1] = err;
			FMessage(FMSG_WARNING, NULL, itms, 3, 1);
		}
	} while (true);
}

void FTP::QuequeMenu(void)
{
	int n, num;
	int Breaks[] = {VK_DELETE, VK_INSERT, VK_F4, VK_RETURN, 0}, BNumber;
	FarMenuItem *mi = NULL;
	FTPUrl *p, *p1;
	char str1[MAX_PATH], str2[MAX_PATH], str3[MAX_PATH];
	QueueExecOptions exOp;
	SetupQOpt(&exOp);
	num = -1;

	do {
		mi = (FarMenuItem *)realloc(mi, (QuequeSize + 1) * sizeof(FarMenuItem));
		memset(mi, 0, QuequeSize * sizeof(FarMenuItem));

		for (p = UrlsList, n = 0; p; p = p->Next, n++) {
			StrCpy(str1, p->SrcPath.c_str(), 20);
			StrCpy(str2, p->DestPath.c_str(), 20);
			StrCpy(str3, p->FileName.cFileName, 20);
			snprintf(mi[n].Text, ARRAYSIZE(mi[n].Text), "%c%c %-20s%c%-20s%c%-20s", p->Download ? '-' : '<',
					p->Download ? '>' : '-', str1, FAR_VERT_CHAR, str2, FAR_VERT_CHAR, str3);

			if (p->Error[0])
				mi[n].Checked = TRUE;
		}

		// Title
		char title[MAX_PATH];
		snprintf(title, ARRAYSIZE(title), "%s: %d %s", FP_GetMsg(MQMenuTitle), n, FP_GetMsg(MQMenuItems));

		// Menu
		if (num != -1 && num < QuequeSize)
			mi[num].Selected = TRUE;

		n = FP_Info->Menu(FP_Info->ModuleNumber, -1, -1, 0, FMENU_SHOWAMPERSAND, title,
				FP_GetMsg(MQMenuFooter), "FTPQueue", Breaks, &BNumber, mi, QuequeSize);

		// key ESC
		if (BNumber == -1 && n == -1)
			goto Done;

		// key Enter
		if (BNumber == -1) {
			//??
			goto Done;
		}

		// Set selected
		if (num != -1)
			mi[num].Selected = FALSE;

		num = n;

		// Process keys
		switch (BNumber) {
				/*DEL*/
			case 0:

				if (QuequeSize)
					switch (AskDeleteQueue()) {
						case -1:
						case 2:
							break;
						case 0:
							p = UrlItem(n, &p1);
							DeleteUrlItem(p, p1);
							break;
						case 1:
							ClearQueue();
							break;
					}

				break;
				/*Ins*/
			case 1:
				InsertToQueue();
				break;
				/*F4*/
			case 2:
				p = UrlItem(n, NULL);

				if (p)
					EditUrlItem(p);

				break;
				/*Return*/
			case 3:

				if (QuequeSize && WarnExecuteQueue(&exOp)) {
					ExecuteQueue(&exOp);

					if (!QuequeSize)
						goto Done;
				}

				break;
		}
	} while (true);

Done:
	free(mi);
}

void FTP::AddToQueque(FTPUrl *item, int pos /*-1*/)
{
	FTPUrl *p, *p1, *newi;
	newi = new FTPUrl;
	*newi = *item;

	if (pos == -1)
		pos = QuequeSize;

	p = UrlItem(pos, &p1);

	if (p1)
		p1->Next = newi;

	newi->Next = p;

	if (p == UrlsList)
		UrlsList = newi;

	QuequeSize++;
}

void FTP::AddToQueque(FAR_FIND_DATA *FileName, LPCSTR Path, BOOL Download)
{
	String str;
	char *m;
	int num;
	FTPUrl *p = new FTPUrl;
	memcpy(&p->Host, &Host, sizeof(Host));
	p->Download = Download;
	p->Next = NULL;
	p->FileName = *FileName;
	p->Error.Null();
	p->DestPath = Path;

	//	if(Download)
	m = strrchr(FileName->cFileName, '/');
	//	else
	//		m = strrchr(FileName->cFileName, '\\');

	if (m) {
		*m = 0;
		p->DestPath.Add(m);
		memmove(FileName->cFileName, m + 1, m - FileName->cFileName);
	}

	if (Download) {
		GetCurPath(p->SrcPath);
		AddEndSlash(p->SrcPath, '/');
		str.printf("%s%s", p->SrcPath.c_str(), FileName->cFileName);
		FixLocalSlash(p->DestPath);
		AddEndSlash(p->DestPath, '/');
		num = str.Chr('/');
	} else {
		PanelInfo pi;
		FP_Info->Control(this, FCTL_GETANOTHERPANELINFO, &pi);
		p->SrcPath = pi.CurDir;
		AddEndSlash(p->SrcPath, '/');
		str.printf("%s%s", p->SrcPath.c_str(), FileName->cFileName);
		FixLocalSlash(str);
		AddEndSlash(p->DestPath, '/');
		num = str.Chr('/');
	}

	if (num != -1) {
		StrCpy(p->FileName.cFileName, str.c_str() + num + 1, ARRAYSIZE(p->FileName.cFileName));
		str.SetLength(num);
		p->SrcPath = str;
	} else {
		StrCpy(p->FileName.cFileName, str.c_str(), ARRAYSIZE(p->FileName.cFileName));
		p->SrcPath.Null();
	}

	if (!UrlsList)
		UrlsList = p;

	if (UrlsTail)
		UrlsTail->Next = p;

	UrlsTail = p;
	QuequeSize++;
}

void FTP::ListToQueque(FP_SizeItemList *il, FTPCopyInfo *ci)
{
	for (int n = 0; n < il->Count(); n++) {
		FAR_FIND_DATA *p = &il->Item(n)->FindData;

		// Skip dirs
		if (IS_FLAG(p->dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
			continue;

		// Skip deselected in list
		if (il->Item(n)->CRC32 & 0x80000000)
			continue;

		AddToQueque(p, ci->DestPath.c_str(), ci->Download);
	}
}

void FTP::ExecuteQueue(QueueExecOptions *op)
{
	if (!QuequeSize)
		return;

	FTPHost oHost = Host;
	BOOL oShowHosts = ShowHosts;
	char oDir[MAX_PATH];
	GetCurPath(oDir, ARRAYSIZE(oDir));
	OverrideMsgCode = ocNone;
	ExecuteQueueINT(op);
	OverrideMsgCode = ocNone;

	// Restore plugin state
	if (op->RestoreState) {
		if (oShowHosts) {
			BackToHosts();
		} else if (!Host.CmpConnected(&oHost)) {
			Host = oHost;
			FullConnect();
			ResetCache = TRUE;
		}

		SetDirectory(oDir, 0);
		Invalidate();
	}
}

void FTP::ExecuteQueueINT(QueueExecOptions *op)
{
	PROC(("ExecuteQueueINT", "%d,%d", op->RestoreState, op->RemoveCompleted))
	FP_Screen _scr;
	String DefPath, LastPath, LastName;
	BOOL rc;
	BOOL needUpdate = FALSE;
	FTPUrl *prev, *p, *tmp;
	FTPCopyInfo ci;
	FAR_FIND_DATA fd, ffd;
	// Copy info
	ci.asciiMode = Host.AsciiMode;
	ci.ShowProcessList = FALSE;
	ci.AddToQueque = FALSE;
	ci.MsgCode = ocNone;
	ci.UploadLowCase = Opt.UploadLowCase;
	// Check other panel info
	PanelInfo pi;
	FP_Info->Control(INVALID_HANDLE_VALUE, FCTL_GETANOTHERPANELINFO, &pi);

	if (pi.PanelType != PTYPE_FILEPANEL || pi.Plugin)
		DefPath.Null();
	else
		DefPath = pi.CurDir;

	// DO full list
	prev = NULL;
	p = UrlsList;
	LastPath.Null();
	LastName.Null();

	while (p) {
		// Check current host the same
		Log(("Queue: Check current host the same"));

		if (!hConnect || !Host.CmpConnected(&p->Host)) {
			Host = p->Host;

			if (!FullConnect()) {
				if (WINPORT(GetLastError)() == ERROR_CANCELLED)
					break;

				p->Error.printf("%s: %s", FP_GetMsg(MQCanNotConnect), __WINError());
				goto Skip;
			}

			ResetCache = TRUE;
		}

		// Apply other parameters
		Log(("Queue: Apply other parameters"));
		Host = p->Host;
		hConnect->InitData(&Host, -1);
		hConnect->InitIOBuff();
		// Change local dir
		Log(("Queue: Change local dir"));

		do {
			char *m = p->Download ? p->DestPath.c_str() : p->SrcPath.c_str();

			if (!m[0])
				m = DefPath.c_str();

			if (!m[0]) {
				p->Error = FP_GetMsg(MQNotLocal);
				goto Skip;
			}

			if (sdc_chdir(m) == 0)
				break;

			if (DoCreateDirectory(m))
				if (sdc_chdir(m) == 0)
					break;

			p->Error.printf(FP_GetMsg(MQCanNotChangeLocal), m, __WINError());
			goto Skip;
		} while (0);

		// Check local file
		Log(("Queue: Check local file"));

		if (!p->Download) {
			if (!FRealFile(p->FileName.cFileName, &fd)) {
				p->Error.printf(FP_GetMsg(MQNotFoundSource), p->FileName.cFileName, __WINError());
				goto Skip;
			}
		}

		// IO file
		Log(("Queue: IO file"));
		// Last used FTP path and name
		LastPath = p->Download ? p->SrcPath : p->DestPath;
		LastName = PointToName(p->FileName.cFileName);

		// DOWNLOAD ------------------------------------------------
		if (p->Download) {
			ci.Download = TRUE;
			ci.SrcPath = p->SrcPath;
			AddEndSlash(ci.SrcPath, '/');
			ci.SrcPath.cat(p->FileName.cFileName);

			if (p->DestPath.Length()) {
				FixFileNameChars(p->DestPath);
				ci.DestPath = p->DestPath;
			} else
				ci.DestPath = DefPath;

			AddEndSlash(ci.DestPath, '/');
			ci.DestPath.cat(FixFileNameChars(p->FileName.cFileName, TRUE));
			int64_t fsz = FtpFileSize(hConnect, ci.SrcPath.c_str());
			hConnect->TrafficInfo->Init(hConnect, MStatusDownload, 0, NULL);
			hConnect->TrafficInfo->InitFile(fsz, ci.SrcPath.c_str(), ci.DestPath.c_str());

			if (FRealFile(ci.DestPath.c_str(), &fd)) {
				if (fsz != -1) {
					ffd = fd;
					ffd.nFileSize = fsz;
					ffd.nPhysicalSize = 0;
					ci.MsgCode = AskOverwrite(MDownloadTitle, TRUE, &fd, &ffd, ci.MsgCode, FALSE);
				} else
					ci.MsgCode = AskOverwrite(MDownloadTitle, TRUE, &fd, NULL, ci.MsgCode, FALSE);

				switch (ci.MsgCode) {
					case ocOverAll:
					case ocOver:
						break;
					case ocSkip:
					case ocSkipAll:
						goto Skip;
					case ocResume:
					case ocResumeAll:
						break;
					case ocNewer:
					case ocNewerAll:
						goto Skip;
					default:
						break;	// ocNone, ocCancel
				}

				if (ci.MsgCode == ocCancel) {
					WINPORT(SetLastError)(ERROR_CANCELLED);
					break;
				}
			}

			rc = _FtpGetFile(ci.SrcPath.c_str(), ci.DestPath.c_str(),
					ci.MsgCode == ocResume || ci.MsgCode == ocResumeAll, ci.asciiMode);
		} else {
			// UPLOAD -------------------------------------------------
			ci.Download = FALSE;
			ci.SrcPath = p->SrcPath;
			AddEndSlash(ci.SrcPath, '/');
			ci.SrcPath.cat(PointToName(p->FileName.cFileName));

			if (p->DestPath[0])
				ci.DestPath = p->DestPath;
			else
				GetCurPath(ci.DestPath);

			AddEndSlash(ci.DestPath, '/');
			ci.DestPath.cat(PointToName(p->FileName.cFileName));
			int64_t fsz = FtpFileSize(hConnect, ci.DestPath.c_str());
			hConnect->TrafficInfo->Init(hConnect, MStatusUpload, 0, NULL);
			hConnect->TrafficInfo->InitFile(&fd, ci.SrcPath.c_str(), ci.DestPath.c_str());

			if (fsz != -1) {
				ffd = fd;
				ffd.nFileSize = fsz;
				ffd.nPhysicalSize = 0;
				ci.MsgCode = AskOverwrite(MUploadTitle, FALSE, &ffd, &fd, ci.MsgCode, FALSE);

				switch (ci.MsgCode) {
					case ocOverAll:
					case ocOver:
						break;
					case ocSkip:
					case ocSkipAll:
						goto Skip;
					case ocResume:
					case ocResumeAll:
						break;
					case ocNewer:
					case ocNewerAll:
						goto Skip;
					default:
						break;	// ocNone, ocCancel
				}

				if (ci.MsgCode == ocCancel) {
					WINPORT(SetLastError)(ERROR_CANCELLED);
					break;
				}

				needUpdate = TRUE;
			}

			rc = _FtpPutFile(ci.SrcPath.c_str(), ci.DestPath.c_str(),
					ci.MsgCode == ocResume || ci.MsgCode == ocResumeAll, ci.asciiMode);
			needUpdate = needUpdate || rc == TRUE;
		}

		// IO completed
		if (rc == -1 || (!rc && WINPORT(GetLastError)() == ERROR_CANCELLED)) {
			WINPORT(SetLastError)(ERROR_CANCELLED);
			break;
		}

		if (!rc) {
			if (p->Download)
				p->Error.printf(FP_GetMsg(MQErrDownload), __WINError());
			else
				p->Error.printf(FP_GetMsg(MQErrUpload), __WINError());

			goto Skip;
		}

		// Done
		Log(("Queue: Done"));
		tmp = p->Next;

		if (op->RemoveCompleted)
			DeleteUrlItem(p, prev);

		p = tmp;
		continue;
	// Error
	Skip:
		Log(("Queue: Error"));
		prev = p;
		p = p->Next;
	}

	// Reread files on FTP in case files are uploaded
	if (!ShowHosts && hConnect && FtpCmdLineAlive(hConnect) && FtpKeepAlive(hConnect)) {
		if (!op->RestoreState) {
			if (LastPath.Length())
				SetDirectoryStepped(LastPath.c_str(), TRUE);

			if (LastName.Length())
				SelectFile = LastName;
		}

		FP_Screen::FullRestore();

		if (needUpdate)
			Reread();
		else
			Invalidate();
	}
}
