#include <all_far.h>


#include "Int.h"
#include "Crypt.inc"

//---------------------------------------------------------------------------------
void FTP::LongBeepEnd(BOOL DoNotBeep /*= FALSE*/)
{
	if(LongBeep)
	{
		if(FP_PeriodEnd(LongBeep) && !DoNotBeep)
		{
			//MessageBeep(MB_ICONASTERISK);
		}

		FP_PeriodDestroy(LongBeep);
		LongBeep = NULL;
	}
}
void FTP::LongBeepCreate(void)
{
	if(Opt.LongBeepTimeout)
	{
		LongBeepEnd(FALSE);
		LongBeep = FP_PeriodCreate(Opt.LongBeepTimeout*1000);
	}
}
//---------------------------------------------------------------------------------
void FTP::SaveUsedDirNFile(void)
{
	PROC(("SaveUsedDirNFile","was:(%s,%s)",Host.Home,SelectFile.c_str()))
	PanelInfo pi;

	//Save current file to restore
	if(!ShowHosts && hConnect)
	{
		String s;
		FtpGetCurrentDirectory(hConnect,s);
		StrCpy(Host.Home, s.c_str(), ARRAYSIZE(Host.Home));
	}

	//Save current file to restore
	if(FP_Info->Control(this,FCTL_GETPANELINFO,&pi))
	{
		if(pi.ItemsNumber > 0 && pi.CurrentItem < pi.ItemsNumber)
		{
			SelectFile = FTP_FILENAME(&pi.PanelItems[pi.CurrentItem]);
			Log(("SetLastHost: [%s]", SelectFile.c_str()));
		}

		Log(("Saved (%s,%s)",Host.Home,SelectFile.c_str()));
	}
}

void FTP::GetCurPath(char *buff,int bsz)
{
	if(ShowHosts)
	{
		StrCpy(buff, HostsPath, bsz);
		return;
	}

	String s;

	if(!FtpGetCurrentDirectory(hConnect,s))
	{
		buff[0] = 0;
		return;
	}

	StrCpy(buff, s.c_str(), bsz);
}

void FTP::GetCurPath(String& buff)
{
	if(ShowHosts)
		buff = HostsPath;
	else if(!FtpGetCurrentDirectory(hConnect,buff))
		buff.Null();
}

void FTP::FTP_FixPaths(LPCSTR base, PluginPanelItem *p, int cn, BOOL FromPlugin)
{
	String str;

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

	for(; cn--; p++)
	{
		char *CurName = FTP_FILENAME(p);

		if(StrCmp(CurName,"..") == 0 ||
		        StrCmp(CurName,".") == 0)
			continue;

		str.printf("%s%c%s", base, '/', CurName);
		StrCpy(p->FindData.cFileName, str.c_str(), ARRAYSIZE(p->FindData.cFileName));

		if(str.Length() >= (int)ARRAYSIZE(p->FindData.cFileName))
			FPIL_ADDSET(p, str.Length()+1, strdup(str.c_str()));
		else
			FPIL_ADDSET(p, 0, NULL);
	}
}

int FTP::ExpandList(PluginPanelItem *pi,int icn,FP_SizeItemList* il,BOOL FromPlugin,ExpandListCB cb,LPVOID Param)
{
	PROC(("ExpandList","cn:%d, ilcn:%d/%d, %s, cb:%08X",icn,il ? il->Count() : 0,il ? il->MaxCount : 0,FromPlugin?"PLUGIN":"LOCAL",cb))
	BOOL             pSaved  = Host.Home[0] && SelectFile.Length();
	BOOL             old_ext = hConnect ? hConnect->Host.ExtCmdView : FALSE;
	FTPCurrentStates olds = CurrentState;
	int              rc;
	{
		FTPConnectionBreakable _brk(hConnect,FALSE);
		CurrentState  = fcsExpandList;

		if(hConnect)
		{
			hConnect->Host.ExtCmdView = FALSE;
			hConnect->CurrentState    = fcsExpandList;
		}

		if(!pSaved)
			SaveUsedDirNFile();
		
		rc = ExpandListINT(pi,icn,il,FromPlugin,cb,Param);

		if(hConnect)
		{
			hConnect->Host.ExtCmdView = old_ext;
			hConnect->CurrentState    = olds;
		}

		CurrentState  = olds;
	}
	Log(("ExpandList rc=%d",rc));
#if defined(__FILELOG__)

	if(rc)
	{
		Log(("Expand succ ends containing:"));

		if(il)
			LogPanelItems(il->Items(), il->Count());
		else
			Log(("Files list does not contains files"));
	}

#endif

	if(!pSaved)
	{
		if(!rc)
		{
			SaveLastError _err;

			if(Host.Home[0])
			{
				char str[MAX_PATH];
				GetCurPath(str, ARRAYSIZE(str));

				if(StrCmpI(str, Host.Home) != 0)
					SetDirectory(Host.Home,FP_LastOpMode);
			}
		}
		else
		{
			SelectFile   = "";
			Host.Home[0] = 0;
		}
	}

	return rc;
}

//---------------------------------------------------------------------------------
BOOL FTP::FTP_SetDirectory(LPCSTR dir,BOOL FromPlugin)
{
	PROC(("FTP::FTP_SetDirectory", "%s,%d", dir,FromPlugin))
	if(FromPlugin)
		return SetDirectory(dir,OPM_SILENT) == TRUE;
	else
		return sdc_chdir(dir) == 0;
}

BOOL FTP::FTP_GetFindData(PluginPanelItem **PanelItem,int *ItemsNumber,BOOL FromPlugin)
{
	if(FromPlugin)
	{
		Log(("PLUGIN GetFindData"));
		return GetFindData(PanelItem,ItemsNumber,OPM_SILENT);
	}

	PROC(("LOCAL GetFindData", NULL))
	FP_SizeItemList il(FALSE);
	WIN32_FIND_DATA fd = {};
	PluginPanelItem p;
	HANDLE          h = WINPORT(FindFirstFile)(L"*.*",&fd);

	if(h == INVALID_HANDLE_VALUE)
	{
#if defined(__FILELOG__)
		char path[MAX_PATH];
		path[GetCurrentDirectory(ARRAYSIZE(path),path)] = 0;
		Log(("Files in [%s] not found: %s", path, __WINError()));
#endif
		*PanelItem   = NULL;
		*ItemsNumber = 0;
		return TRUE;
	}
	else
	{
#if defined(__FILELOG__)
		char path[MAX_PATH];
		path[GetCurrentDirectory(ARRAYSIZE(path),path)] = 0;
		Log(("Files in [%s] are found", path));
#endif
	}

	do
	{
		if(wcscmp(fd.cFileName, L"..") == 0 || wcscmp(fd.cFileName, L".") == 0)
			continue;

		const std::string &CurName = Wide2MB(fd.cFileName);

		Log(("Found: [%s]%d", CurName.c_str(), fd.dwFileAttributes));
		//Reset plugin structure
		memset(&p,0,sizeof(PluginPanelItem));
		//Copy win32 data
		p.FindData.dwFileAttributes = fd.dwFileAttributes;
		p.FindData.ftLastAccessTime = fd.ftLastAccessTime;
		p.FindData.ftLastWriteTime = fd.ftLastWriteTime;
		p.FindData.nFileSize = fd.nFileSize;
		p.FindData.nPhysicalSize = 0; // ...because its used by plugin in a singular way
		p.FindData.dwUnixMode = fd.dwUnixMode;
		strncpy(p.FindData.cFileName, CurName.c_str(), ARRAYSIZE(p.FindData.cFileName) - 1);

		if(!il.Add(&p,1))
			return FALSE;
	}
	while(WINPORT(FindNextFile)(h,&fd));

	WINPORT(FindClose)(h);
	*PanelItem   = il.Items();
	*ItemsNumber = il.Count();
	return TRUE;
}

void FTP::FTP_FreeFindData(PluginPanelItem *PanelItem,int ItemsNumber,BOOL FromPlugin)
{
	if(PanelItem && ItemsNumber)
	{
		if(FromPlugin)
			FreeFindData(PanelItem,ItemsNumber);
		else
			free(PanelItem);
	}
}

//---------------------------------------------------------------------------------
int FTP::ExpandListINT(PluginPanelItem *pi,int icn,FP_SizeItemList* il,BOOL FromPlugin,ExpandListCB cb,LPVOID Param)
{
	PluginPanelItem *DirPanelItem;
	int              DirItemsNumber,res;
	char            *CurName,*m;
	int              n,num;
	int64_t          lSz=0,lCn=0;
	String           curp;

	if(!icn)
		return TRUE;

	if(CheckForEsc(FALSE,TRUE))
	{
		Log(("ESC: ExpandListINT cancelled"));
		WINPORT(SetLastError)(ERROR_CANCELLED);
		return FALSE;
	}

	for(n = 0; n < icn; n++)
	{
		CurName = FTP_FILENAME(&pi[n]);

		if(StrCmp(CurName,"..") == 0 ||
		        StrCmp(CurName,".") == 0)
			continue;

//============
//FILE
		if(!IS_FLAG(pi[n].FindData.dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY))
		{
			m = PointToName(CurName);

			if(IncludeMask[0] && !FP_InPattern(IncludeMask,m))
			{
				Log(("INC File [%s] was filtered out by [%s]", CurName, IncludeMask));
				continue;
			}

			if(ExcludeMask[0] && FP_InPattern(ExcludeMask,m))
			{
				Log(("EXC File [%s] was filtered out by [%s]", CurName, ExcludeMask));
				continue;
			}

			if(cb && !cb(&pi[n],Param))
				return FALSE;

			if(il)
			{
				il->TotalFullSize += pi[n].FindData.nFileSize;
				il->TotalFiles++;
				//Add
				PluginPanelItem *tmp = il->Add(&pi[n]);
				//Reset special plugin field
				tmp->CRC32 = 0;
			}

			continue;
		}
//============
//DIR
		//Get name
		m = strrchr(CurName, '/');

		if(m) m = m+1;
		else m = CurName;

		//Remember current
		GetCurPath(curp);

		//Set dir
		if(!FTP_SetDirectory(m,FromPlugin))
			return FALSE;

		if(FromPlugin)
		{
			String newp;
			GetCurPath(newp);

			if(curp == newp)
				continue;
		}

		//Get subdir list
		if(hConnect && !cb)
		{
			String str;
			char   digit[ 50 ];

			if(il)
				str.printf("%sb in %u: %s",
				           FDigit(digit,il->TotalFullSize,-1),
				           (int)il->TotalFiles,
				           CurName);
			else
				str = CurName;

			FtpConnectMessage(hConnect, MScaning, str.c_str());
		}
		else if(hConnect)
			FtpConnectMessage(hConnect, MScaning, PointToName(CurName));

		if(il)
		{
			num = il->Count();
			il->Add(&pi[n]);
		}
		else
			num = -1;

		if(FTP_GetFindData(&DirPanelItem,&DirItemsNumber,FromPlugin))
		{
			FTP_FixPaths(CurName, DirPanelItem, DirItemsNumber, FromPlugin);

			if(num != -1)
			{
				lSz = il->TotalFullSize;
				lCn = il->TotalFiles;
			}

			res = ExpandListINT(DirPanelItem,DirItemsNumber,il,FromPlugin,cb,Param);

			if(num != -1 && res)
			{
				lSz = il->TotalFullSize - lSz;
				lCn = il->TotalFiles    - lCn;
				il->Item(num)->FindData.nFileSize = lSz;
				il->Item(num)->FindData.nPhysicalSize = 0;
				il->Item(num)->CRC32&= 0x80000000;
				il->Item(num)->CRC32|= DWORD(lCn) & 0x7fffffff;
			}

			FTP_FreeFindData(DirPanelItem,DirItemsNumber,FromPlugin);
		}
		else
			return FALSE;

		if(!res) return FALSE;

		if(!FTP_SetDirectory("..", FromPlugin))
			return FALSE;

		if(cb && !cb(&pi[n],Param))
			return FALSE;
	}

	return TRUE;
}
//---------------------------------------------------------------------------------
void FTP::Invalidate(void)
{
	FP_Info->Control(this,FCTL_UPDATEPANEL,NULL);
	FP_Info->Control(this,FCTL_REDRAWPANEL,NULL);
}

//---------------------------------------------------------------------------------
BOOL FTP::Reread(void)
{
	PanelInfo pi;
	String    oldp, newp, cur;
	GetCurPath(oldp);
	//Save current file to restore
	FP_Info->Control(this, FCTL_GETPANELINFO, &pi);

	if(pi.ItemsNumber > 0 && pi.CurrentItem < pi.ItemsNumber)
		cur = FTP_FILENAME(&pi.PanelItems[pi.CurrentItem]);

	//Reread
	if(!ShowHosts)
		ResetCache = TRUE;

	FP_Info->Control(this, FCTL_UPDATEPANEL, (void*)1);
	//Redraw
	GetCurPath(newp);
	int rc = oldp == newp;

	if(rc)
	{
		SelectFile = cur;
		Log(("SetLastHost: [%s]", SelectFile.c_str()));
	}

	FP_Info->Control(this, FCTL_REDRAWPANEL, NULL);
	return rc;
}
//---------------------------------------------------------------------------------
void FTP::CopyNamesToClipboard(void)
{
	String     s, FullName, CopyData;
	PanelInfo  pi;
	int        CopySize, n;
	FP_Info->Control(this, FCTL_GETPANELINFO, &pi);
	FtpGetCurrentDirectory(hConnect, s);
	Host.MkUrl(FullName, s.c_str(), "");

	for(CopySize = n = 0; n < pi.SelectedItemsNumber; n++)
		CopySize += FullName.Length() +
		            1/* / */ +
		            static_cast<int>(strlen(FTP_FILENAME(&pi.SelectedItems[n]))) +
		            2/*quote*/ +
		            2/* \r\n */;

	if(!CopyData.Alloc(CopySize+2))
		return;

	for(n = 0; n < pi.SelectedItemsNumber; n++)
	{
		s = FullName;
		AddEndSlash(s, '/');
		s.Add(FTP_FILENAME(&pi.SelectedItems[n]));

		if(Opt.QuoteClipboardNames)
			QuoteStr(s);

		CopyData.Add(s);
		CopyData.Add("\r\n");
	}

	if(CopyData.Length())
		FP_CopyToClipboard(CopyData.c_str(), CopyData.Length());
}

//---------------------------------------------------------------------------------
void FTP::BackToHosts(void)
{
	int num = FP_GetRegKey("LastHostsMode",2);
	Log(("BackToHosts: [%s] h:[%s] md:%d sf: %s", Host.RegKey, Host.Host, num, Host.RegKey));
	SelectFile       = Host.RegKey;
	SwitchingToFTP   = FALSE;
	RereadRequired   = TRUE;
	ShowHosts        = TRUE;
	Host.HostName[0] = 0;
	delete hConnect;
	hConnect = NULL;
	FP_Info->Control(this,FCTL_SETVIEWMODE,&num);
}
