// Called by P4API on "change -i" command. OutBuffer is filled with changelist specification text
	void InputData(StrBuf* OutBuffer, Error* OutError)
	{
#if USE_P4_API
		FString OutputDesc;
		OutputDesc += TEXT("Change:\tnew\n\n");
		OutputDesc += TEXT("Client:\t");
		OutputDesc += TO_TCHAR(P4Client.GetClient().Text(), bIsUnicodeServer);
		OutputDesc += TEXT("\n\n");
		OutputDesc += TEXT("User:\t");
		OutputDesc += TO_TCHAR(P4Client.GetUser().Text(), bIsUnicodeServer);
		OutputDesc += TEXT("\n\n");
		OutputDesc += TEXT("Status:\tnew\n\n");
		OutputDesc += TEXT("Description:\n");
		{
			TArray<FString> DescLines;
			Description.ToString().ParseIntoArray(DescLines, TEXT("\n"), false);
			for (const FString& DescLine : DescLines)
			{
				OutputDesc += TEXT("\t");
				OutputDesc += DescLine;
				OutputDesc += TEXT("\n");
			}
		}
		OutputDesc += TEXT("\n");
		OutputDesc += TEXT("Files:\n\n");

		OutBuffer->Append(FROM_TCHAR(*OutputDesc, bIsUnicodeServer));
#endif
	}
	// Called by P4API when the changelist is created.
	void OutputInfo(ANSICHAR Level, const ANSICHAR *Data)
	{
		const int32 ChangeTextLen = FCString::Strlen(TEXT("Change "));
		if (FString(TO_TCHAR(Data, bIsUnicodeServer)).StartsWith(TEXT("Change ")))
		{
			ChangelistNumber = FCString::Atoi(TO_TCHAR(Data + ChangeTextLen, bIsUnicodeServer));
		}
	}
	// Called by P4API when the results from running a command are ready
	void OutputStat(StrDict* VarList)
	{
		FP4Record Record;
		StrRef Var, Value;

		// Iterate over each variable and add to records
		for (int32 Index = 0; VarList->GetVar(Index, Var, Value); Index++)
		{
			Record.Add(TO_TCHAR(Var.Text(), bIsUnicodeServer), TO_TCHAR(Value.Text(), bIsUnicodeServer));
		}

		Records.Add(Record);
	}
	void HandleError(Error *InError)
	{
#if USE_P4_API
		StrBuf ErrorMessage;
		InError->Fmt(&ErrorMessage);
		OutErrorMessages.Add(FText::FromString(FString(TO_TCHAR(ErrorMessage.Text(), bIsUnicodeServer))));
#endif
	}
	// Called by P4API on "change -i" command. OutBuffer is filled with changelist specification text
	void InputData(StrBuf* OutBuffer, Error* OutError)
	{
		FString OutputDesc;
		OutputDesc += TEXT("Change:\tnew\n\n");
		OutputDesc += TEXT("Client:\t");
		OutputDesc += TO_TCHAR(P4Client.GetClient().Text(), bIsUnicodeServer);
		OutputDesc += TEXT("\n\n");
		OutputDesc += TEXT("User:\t");
		OutputDesc += TO_TCHAR(P4Client.GetUser().Text(), bIsUnicodeServer);
		OutputDesc += "\n\n";
		OutputDesc += "Status:\tnew\n\n";
		OutputDesc += "Description:\n\t";
		OutputDesc += Description;
		OutputDesc += TEXT("\n\n");
		OutputDesc += TEXT("Files:\n\n");

		OutBuffer->Append(FROM_TCHAR(*OutputDesc, bIsUnicodeServer));
	}
void FPerforceConnection::Disconnect()
{
#if USE_P4_API
	Error P4Error;

	P4Client.Final(&P4Error);

	if (P4Error.Test())
	{
		StrBuf ErrorMessage;
		P4Error.Fmt(&ErrorMessage);
		UE_LOG(LogSourceControl, Error, TEXT("P4ERROR: Failed to disconnect from Server."));
		UE_LOG(LogSourceControl, Error, TEXT("%s"), TO_TCHAR(ErrorMessage.Text(), bIsUnicode));
	}
#endif
}
	void HandleError(Error *InError)
	{
		StrBuf ErrorMessage;
		InError->Fmt(&ErrorMessage);
		OutErrorMessages.Add(TO_TCHAR(ErrorMessage.Text(), bIsUnicodeServer));
	}
bool FPerforceConnection::EnsureValidConnection(FString& InOutServerName, FString& InOutUserName, FString& InOutWorkspaceName, const FPerforceConnectionInfo& InConnectionInfo)
{
	bool bIsUnicodeServer = false;
	bool bConnectionOK = false;
#if USE_P4_API
	FMessageLog SourceControlLog("SourceControl");

	FString NewServerName = InOutServerName;
	FString NewUserName = InOutUserName;
	FString NewClientSpecName = InOutWorkspaceName;

	ClientApi TestP4;
	TestP4.SetProtocol("tag", "");
	TestP4.SetProtocol("enableStreams", "");

	if (NewServerName.Len() && NewUserName.Len() && NewClientSpecName.Len())
	{
		//attempt connection with given settings
		TestP4.SetPort(TCHAR_TO_ANSI(*NewServerName));

		if(InConnectionInfo.Password.Len() > 0)
		{
			TestP4.SetPassword(TCHAR_TO_ANSI(*InConnectionInfo.Password));
		}

		if(InConnectionInfo.HostOverride.Len() > 0)
		{
			TestP4.SetHost(TCHAR_TO_ANSI(*InConnectionInfo.HostOverride));
		}
	}

	Error P4Error;
	TestP4.Init(&P4Error);

	bConnectionOK = !P4Error.Test();
	if (!bConnectionOK)
	{
		//Connection FAILED
		StrBuf ErrorMessage;
		P4Error.Fmt(&ErrorMessage);
		SourceControlLog.Error(LOCTEXT("P4ErrorConnection", "P4ERROR: Failed to connect to source control provider."));
		SourceControlLog.Error(FText::FromString(ANSI_TO_TCHAR(ErrorMessage.Text())));
		FFormatNamedArguments Arguments;
		Arguments.Add( TEXT("PortName"), FText::FromString(NewServerName) );
		Arguments.Add( TEXT("UserName"), FText::FromString(NewUserName) );
		Arguments.Add( TEXT("ClientSpecName"), FText::FromString(NewClientSpecName) );
		Arguments.Add( TEXT("Ticket"), FText::FromString(InConnectionInfo.Ticket) );
		SourceControlLog.Error(FText::Format(LOCTEXT("P4ErrorConnection_Details", "Port={PortName}, User={UserName}, ClientSpec={ClientSpecName}, Ticket={Ticket}"), Arguments));
	}

	// run an info command to determine unicode status
	if(bConnectionOK)
	{
		TArray<FText> ErrorMessages;

		bConnectionOK = CheckUnicodeStatus(TestP4, bIsUnicodeServer, ErrorMessages);
		if(!bConnectionOK)
		{
			SourceControlLog.Error(LOCTEXT("P4ErrorConnection", "P4ERROR: Could not determine server unicode status."));
			SourceControlLog.Error(ErrorMessages.Num() > 0 ? ErrorMessages[0] : LOCTEXT("P4ErrorConnection_Unknown error", "Unknown error"));
			FFormatNamedArguments Arguments;
			Arguments.Add( TEXT("PortName"), FText::FromString(NewServerName) );
			Arguments.Add( TEXT("UserName"), FText::FromString(NewUserName) );
			Arguments.Add( TEXT("ClientSpecName"), FText::FromString(NewClientSpecName) );
			Arguments.Add( TEXT("Ticket"), FText::FromString(InConnectionInfo.Ticket) );
			SourceControlLog.Error(FText::Format(LOCTEXT("P4ErrorConnection_Details", "Port={PortName}, User={UserName}, ClientSpec={ClientSpecName}, Ticket={Ticket}"), Arguments));
		}
		else
		{
			if(bIsUnicodeServer)
			{
				// set translation mode. From here onwards we need to use UTF8 when using text args
				TestP4.SetTrans(CharSetApi::UTF_8);
			}

			// now we have determined unicode status, we can set the values that can be specified in non-ansi characters
			TestP4.SetCwd(FROM_TCHAR(*FPaths::RootDir(), bIsUnicodeServer));
			TestP4.SetUser(FROM_TCHAR(*NewUserName, bIsUnicodeServer));
			TestP4.SetClient(FROM_TCHAR(*NewClientSpecName, bIsUnicodeServer));

			if(InConnectionInfo.Ticket.Len())
			{
				TestP4.SetPassword(FROM_TCHAR(*InConnectionInfo.Ticket, bIsUnicodeServer));
			}
		}
	}

	if (bConnectionOK)
	{
		// If a client spec was not specified, attempt to auto-detect it here. If the detection is not successful, neither is this connection since we need a client spec.
		if ( NewClientSpecName.IsEmpty() )
		{
			FPerforceConnectionInfo AutoCredentials = InConnectionInfo;
			AutoCredentials.Port = TO_TCHAR(TestP4.GetPort().Text(), bIsUnicodeServer);
			AutoCredentials.UserName = TO_TCHAR(TestP4.GetUser().Text(), bIsUnicodeServer);

		 	bConnectionOK = FPerforceConnection::AutoDetectWorkspace(AutoCredentials, NewClientSpecName);
			if ( bConnectionOK )
			{
				TestP4.SetClient(FROM_TCHAR(*NewClientSpecName, bIsUnicodeServer));
			}
		}
	}

	if (bConnectionOK)
	{
		TArray<FText> ErrorMessages;

		bConnectionOK = TestConnection(TestP4, NewClientSpecName, bIsUnicodeServer, ErrorMessages);
		if (!bConnectionOK)
		{
			//Login FAILED
			SourceControlLog.Error(LOCTEXT("P4ErrorConnection", "P4ERROR: Failed to connect to source control provider."));
			SourceControlLog.Error(ErrorMessages.Num() > 0 ? ErrorMessages[0] : LOCTEXT("P4ErrorConnection_InvalidWorkspace", "Invalid workspace"));
			FFormatNamedArguments Arguments;
			Arguments.Add( TEXT("PortName"), FText::FromString(NewServerName) );
			Arguments.Add( TEXT("UserName"), FText::FromString(NewUserName) );
			Arguments.Add( TEXT("ClientSpecName"), FText::FromString(NewClientSpecName) );
			Arguments.Add( TEXT("Ticket"), FText::FromString(InConnectionInfo.Ticket) );
			SourceControlLog.Error(FText::Format(LOCTEXT("P4ErrorConnection_Details", "Port={PortName}, User={UserName}, ClientSpec={ClientSpecName}, Ticket={Ticket}"), Arguments));
		}
	}

	//whether successful or not, disconnect to clean up
	TestP4.Final(&P4Error);
	if (bConnectionOK && P4Error.Test())
	{
		//Disconnect FAILED
		bConnectionOK = false;
		StrBuf ErrorMessage;
		P4Error.Fmt(&ErrorMessage);
		SourceControlLog.Error(LOCTEXT("P4ErrorFailedDisconnect", "P4ERROR: Failed to disconnect from Server."));
		SourceControlLog.Error(FText::FromString(TO_TCHAR(ErrorMessage.Text(), bIsUnicodeServer)));
	}

	//if never specified, take the default connection values
	if (NewServerName.Len() == 0)
	{
		NewServerName = TO_TCHAR(TestP4.GetPort().Text(), bIsUnicodeServer);
	}
	if (NewUserName.Len() == 0)
	{
		NewUserName = TO_TCHAR(TestP4.GetUser().Text(), bIsUnicodeServer);
	}
	if (NewClientSpecName.Len() == 0)
	{
		NewClientSpecName = TO_TCHAR(TestP4.GetClient().Text(), bIsUnicodeServer);
		if (NewClientSpecName == TO_TCHAR(TestP4.GetHost().Text(), bIsUnicodeServer))
		{
			// If the client spec name is the same as host name, assume P4 couldn't get the actual
			// spec name for this host and let GetPerforceLogin() try to find a proper one.
			bConnectionOK = false;
		}
	}

	if (bConnectionOK)
	{
		InOutServerName = NewServerName;
		InOutUserName = NewUserName;
		InOutWorkspaceName = NewClientSpecName;
	}
#endif

	return bConnectionOK;
}