#include <all_far.h>


#include "Int.h"

void SetupFileTimeNDescription(int OpMode,Connection *hConnect,LPCSTR nm)
{
	if(!IS_FLAG(OpMode,OPM_DESCR)) return;

	FILE *SrcFile=fopen(nm,"r+b");

	if(!SrcFile) return;

	int   FileSize = (int)Fsize(SrcFile);
	BYTE *Buf      = (BYTE*)malloc(sizeof(BYTE)*FileSize*3+1);
	size_t ReadSize = fread(Buf,1,FileSize,SrcFile);
	size_t WriteSize = ReadSize;//hConnect->FromOEM(Buf,ReadSize,sizeof(BYTE)*FileSize*3+1);
	fflush(SrcFile);
	fseek(SrcFile,0,SEEK_SET);
	fflush(SrcFile);
	fwrite(Buf,1,WriteSize,SrcFile);
	fflush(SrcFile);
	fclose(SrcFile);
	free(Buf);
}

/****************************************
   PROCEDURES
     FTP::GetFiles
 ****************************************/
int FTP::_FtpPutFile(LPCSTR lpszLocalFile,LPCSTR lpszNewRemoteFile,BOOL Reput,int AsciiMode)
{
	PROC(("FTP::_FtpPutFile", "\"%s\",\"%s\",%d,%d", lpszLocalFile,lpszNewRemoteFile,Reput,AsciiMode))
	int rc;
	FtpSetRetryCount(hConnect,0);

	do
	{
		WINPORT(SetLastError)(ERROR_SUCCESS);

		if(!hConnect)
			return FALSE;

		if((rc=FtpPutFile(hConnect,lpszLocalFile,lpszNewRemoteFile,Reput,AsciiMode)) != FALSE)
			return rc;

		if(WINPORT(GetLastError)() == ERROR_CANCELLED)
		{
			Log(("GetFileCancelled: op:%d",IS_SILENT(FP_LastOpMode)));
			return IS_SILENT(FP_LastOpMode) ? (-1) : FALSE;
		}

		if(hConnect->SysErr())
			return IS_SILENT(FP_LastOpMode) ? (-1) : FALSE;

		int num = FtpGetRetryCount(hConnect);

		if(Opt.RetryCount > 0 && num >= Opt.RetryCount)
			return FALSE;

		FtpSetRetryCount(hConnect,num+1);

		if(!hConnect->ConnectMessageTimeout(MCannotUpload,lpszNewRemoteFile,-MRetry))
			return FALSE;

		Reput = TRUE;

		if(FtpCmdLineAlive(hConnect) &&
		        FtpKeepAlive(hConnect))
			continue;

		SaveUsedDirNFile();

		if(!Connect())
			return FALSE;
	}
	while(true);
}

int FTP::PutFiles(struct PluginPanelItem *PanelItem,int ItemsNumber,int Move,int OpMode)
{
	PROC(("PutFiles",NULL))
	int rc;
	LogPanelItems(PanelItem,ItemsNumber);
	rc = PutFilesINT(PanelItem,ItemsNumber,Move,OpMode);
	FtpCmdBlock(hConnect, FALSE);

	if(rc == FALSE || rc == -1)
		LongBeepEnd(TRUE);

	IncludeMask[0] = 0;
	ExcludeMask[0] = 0;
	Log(("rc:%d", rc));
	return rc;
}

int FTP::PutFilesINT(struct PluginPanelItem *PanelItem,int ItemsNumber, int Move,int OpMode)
{
	FP_Screen         _scr;
	FTPCopyInfo       ci;
	FP_SizeItemList   il;
	String            DestName;
	char             *CurName;
	FILETIME          CurTime;
	DWORD             DestAttr;
	FILETIME          DestTime;
	FTPFileInfo       FindData;
	int               mTitle;
	DWORD             SrcAttr;
	int               rc;

	if(!ItemsNumber) return 0;

	if(ShowHosts)    return PutHostsFiles(PanelItem,ItemsNumber,Move,OpMode);

	if(!hConnect)    return 0;

	FtpGetCurrentDirectory(hConnect, ci.DestPath);
	ci.asciiMode       = Host.AsciiMode;
	ci.AddToQueque     = FALSE;
	ci.MsgCode         = IS_FLAG(OpMode,OPM_FIND) ? ocOverAll : OverrideMsgCode;
	ci.ShowProcessList = FALSE;
	ci.Download        = FALSE;
	ci.UploadLowCase   = Opt.UploadLowCase;

	if(Opt.ShowUploadDialog &&
	        !IS_SILENT(OpMode) && !IS_FLAG(OpMode,OPM_NODIALOG))
	{
		if(!CopyAskDialog(Move,FALSE,&ci))
			return FALSE;

		LastMsgCode = ci.MsgCode;
	}

	if(hConnect->Host.ServerType!=FTP_TYPE_MVS)
		AddEndSlash(ci.DestPath,'/');

	if(!ExpandList(PanelItem,ItemsNumber,&il,FALSE))
		return FALSE;

	if(ci.ShowProcessList && !ShowFilesList(&il))
		return FALSE;

	if(ci.AddToQueque)
	{
		Log(("Files added to queue [%d]",il.Count()));
		ListToQueque(&il,&ci);
		return TRUE;
	}

	if(LongBeep)
		FP_PeriodReset(LongBeep);

	hConnect->TrafficInfo->Init(hConnect, MStatusUpload, OpMode, &il);

	for(int I = 0; I < il.Count(); I++)
	{
		String tmp;
		SrcAttr = il.List[I].FindData.dwFileAttributes;
		tmp     = FTP_FILENAME(&il.List[I]);
		CurName = tmp.c_str();
		CurTime = il.List[I].FindData.ftLastWriteTime;
		Log(("PutFiles: list[%d], %d-th, att: %d, crc32:%08X \"%s\"",
		     il.Count(), I, SrcAttr, il.List[I].CRC32, CurName));

		/* File name may contain relative paths.
		   Local files use '\' as dir separator, so convert
		   it to '/' for remote side, BUT only in case relative paths.
		*/
		if(IsAbsolutePath(CurName))
			CurName = PointToName(CurName);

		FixFTPSlash(tmp);

		//Skip deselected files
		if(il.List[I].CRC32 & 0x80000000)
		{
			Log(("PutFiles: skip delselected \"%s\"", CurName));
			continue;
		}

/*		if(ci.UploadLowCase && !IS_FLAG(SrcAttr,FILE_ATTRIBUTE_DIRECTORY))
		{
			char *Name = PointToName(CurName);
			//if ( !IsCaseMixed(Name))
			LocalLower(Name);
		}*/

		if(hConnect->Host.ServerType!=FTP_TYPE_MVS)
			DestName.printf("%s%s", ci.DestPath.c_str(), CurName);
		else
		{
			DestName=CurName;
			//if(DestName.Length()>8)DestName.SetLength(8);
		}

		if(IS_FLAG(SrcAttr,FILE_ATTRIBUTE_DIRECTORY))
		{
			if(FTPCreateDirectory(CurName,OpMode))
				if(I < ItemsNumber && Opt.UpdateDescriptions)
					PanelItem[I].Flags|=PPIF_PROCESSDESCR;

			continue;
		}

		DestAttr = MAX_DWORD;
		DestTime.dwLowDateTime = 0; // Local files are considered newer by default
		DestTime.dwHighDateTime = 0; // Local files are considered newer by default
		memset(&FindData.FindData, 0, sizeof(FindData.FindData));
		int64_t sz;
		//Check file exist
		FtpSetRetryCount(hConnect,0);

		do
		{
			WINPORT(SetLastError)(ERROR_SUCCESS);

			if(FtpFindFirstFile(hConnect, DestName.c_str(), &FindData, nullptr))
			{
				if(strcmp(PointToName(FTP_FILENAME(&FindData)),
				           PointToName(DestName.c_str())) == 0)
					DestAttr = FindData.FindData.dwFileAttributes;

				DestTime = FindData.FindData.ftLastWriteTime;
				break;
			}
			else if(!FtpCmdLineAlive(hConnect))
			{
				;
			}
			else if((sz=FtpFileSize(hConnect,DestName.c_str())) != -1)
			{
				FindData.FindData = il.List[I].FindData;
				FindData.FindData.nFileSize = sz;
				FindData.FindData.nPhysicalSize = 0;
				DestAttr = 0;
				//Can't find file date using FTP commands - leave the default value
				//DestTime = ;
				break;
			}
			else if(!FtpCmdLineAlive(hConnect))
			{
				;
			}
			else
				break;

			Log(("Conn lost!"));

			if(!hConnect)
			{
				BackToHosts();
				Invalidate();
				return FALSE;
			}

			if(WINPORT(GetLastError)() == ERROR_CANCELLED)
				return IS_SILENT(FP_LastOpMode) ? (-1) : FALSE;

			int num = FtpGetRetryCount(hConnect);

			if(Opt.RetryCount > 0 && num >= Opt.RetryCount)
				return FALSE;

			FtpSetRetryCount(hConnect,num+1);

			if(!hConnect->ConnectMessageTimeout(MCannotUpload,DestName.c_str(),-MRetry))
				return FALSE;

			SaveUsedDirNFile();

			if(!Connect())
				return FALSE;
		}
		while(true);

		//Init transfer
		hConnect->TrafficInfo->InitFile(&il.List[I], FTP_FILENAME(&il.List[I]), DestName.c_str());

		//Ask over
		switch(ci.MsgCode)
		{
			case      ocOver:
			case      ocSkip:
			case    ocResume:
			case     ocNewer:
				ci.MsgCode = ocNone;
				break;
			default: break;
		}

		if(DestAttr != MAX_DWORD)
		{
			ci.MsgCode  = AskOverwrite(MUploadTitle, FALSE, &FindData.FindData, &il.List[I].FindData, ci.MsgCode,
			                           ((CurTime.dwLowDateTime || CurTime.dwHighDateTime) && (DestTime.dwLowDateTime || DestTime.dwHighDateTime)));
			LastMsgCode = ci.MsgCode;

			switch(ci.MsgCode)
			{
				case   ocOverAll:
				case      ocOver:
					break;
				case      ocSkip:
				case   ocSkipAll:
					hConnect->TrafficInfo->Skip();
					continue;
				case    ocResume:
				case ocResumeAll:
					break;
				case     ocNewer:
				case  ocNewerAll:

					if(WINPORT(CompareFileTime)(&CurTime, &DestTime) <= 0)
					{
						hConnect->TrafficInfo->Skip();
						continue;
					}

					break;
				case    ocCancel:
					return -1;
				default: break;
			}
		}

//Upload
		if((rc=_FtpPutFile(FTP_FILENAME(&il.List[I]),
		                   DestName.c_str(),
		                   DestAttr == MAX_DWORD ? FALSE : (ci.MsgCode == ocResume || ci.MsgCode == ocResumeAll),
		                   ci.asciiMode)) == TRUE)
		{
			/*! FAR has a bug, so PanelItem stored in internal structure.
			    Because of this flags PPIF_SELECTED and PPIF_PROCESSDESCR has no effect at all.

			      if ( I < ItemsNumber ) {
			        CLR_FLAG( PanelItem[I].Flags,PPIF_SELECTED );
			        if (Opt.UpdateDescriptions) SET_FLAG( PanelItem[I].Flags,PPIF_PROCESSDESCR );
			      }
			*/
			//Process description
			SetupFileTimeNDescription(OpMode,hConnect,CurName);

			//Move source
			if(Move)
			{
				//todo SetFileAttributes(CurName,0);
				remove(CurName);
			}

			mTitle = MOk;
		}
		else

//Cancelled
			if(rc == -1 || WINPORT(GetLastError)() == ERROR_CANCELLED)
				return rc;
			else
			{
//Error uploading
				mTitle = MCannotUpload;
			}

//Process current file finished
		//All OK
		if(mTitle == MOk || mTitle == MNone__)
			continue;

		//Connection lost
		if(!hConnect)
		{
			BackToHosts();
			Invalidate();
		}

		//Return error
		return FALSE;
	}

	if(Move)
		for(int I=il.Count()-1; I>=0; I--)
		{
			if(CheckForEsc(FALSE))
				return -1;

			if(IS_FLAG(il.List[I].FindData.dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY))
				if(rmdir(FTP_FILENAME(&il.List[I]))==0)
					if(I < ItemsNumber)
					{
						PanelItem[I].Flags&=~PPIF_SELECTED;

						if(Opt.UpdateDescriptions)
							PanelItem[I].Flags|=PPIF_PROCESSDESCR;
					}
		}

	return TRUE;
}
