void SProjectLauncherLaunchPage::HandleProfileManagerProfileSelected( const ILauncherProfilePtr& SelectedProfile, const ILauncherProfilePtr& PreviousProfile )
{
	if (PreviousProfile.IsValid())
	{
		PreviousProfile->OnProjectChanged().RemoveAll(this);
	}
	if (SelectedProfile.IsValid())
	{
		SelectedProfile->OnProjectChanged().AddSP(this, &SProjectLauncherLaunchPage::HandleProfileProjectChanged);
	}
	Refresh();
}
FReply SSessionLauncherDeployRepositorySettings::HandleBrowseButtonClicked( )
{
	IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
	if ( DesktopPlatform )
	{
		void* ParentWindowWindowHandle = NULL;

		FString FolderName;
		const FString Title = LOCTEXT("RepositoryBrowseTitle", "Choose a repository location").ToString();
		const bool bFolderSelected = DesktopPlatform->OpenDirectoryDialog(
			0,
			Title,
			RepositoryPathTextBox->GetText().ToString(),
			FolderName
			);

		if ( bFolderSelected )
		{
			if ( !FolderName.EndsWith(TEXT("/")) )
			{
				FolderName += TEXT("/");
			}

			RepositoryPathTextBox->SetText(FText::FromString(FolderName));
			ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

			if(SelectedProfile.IsValid())
			{
				SelectedProfile->SetPackageDirectory(FolderName);
			}
		}
	}

	return FReply::Handled();
}
void FLauncherProfileManager::LoadProfiles( )
{
	TArray<FString> ProfileFileNames;

	IFileManager::Get().FindFiles(ProfileFileNames, *(GetProfileFolder() / TEXT("*.ulp")), true, false);
	
	for (TArray<FString>::TConstIterator It(ProfileFileNames); It; ++It)
	{
		FString ProfileFilePath = GetProfileFolder() / *It;
		FArchive* ProfileFileReader = IFileManager::Get().CreateFileReader(*ProfileFilePath);

		if (ProfileFileReader != nullptr)
		{
			ILauncherProfilePtr LoadedProfile = LoadProfile(*ProfileFileReader);
			delete ProfileFileReader;

			if (LoadedProfile.IsValid())
			{
				AddProfile(LoadedProfile.ToSharedRef());
			}
			else
			{
				IFileManager::Get().Delete(*ProfileFilePath);
			}
		}
	}
}
FReply SProjectLauncherDeployRepositorySettings::HandleBrowseButtonClicked( )
{
	IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
	if ( DesktopPlatform )
	{
		TSharedPtr<SWindow> ParentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());
		void* ParentWindowHandle = (ParentWindow.IsValid() && ParentWindow->GetNativeWindow().IsValid()) ? ParentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;

		FString FolderName;
		const bool bFolderSelected = DesktopPlatform->OpenDirectoryDialog(
			ParentWindowHandle,
			LOCTEXT("RepositoryBrowseTitle", "Choose a repository location").ToString(),
			RepositoryPathTextBox->GetText().ToString(),
			FolderName
			);

		if ( bFolderSelected )
		{
			if ( !FolderName.EndsWith(TEXT("/")) )
			{
				FolderName += TEXT("/");
			}

			RepositoryPathTextBox->SetText(FText::FromString(FolderName));
			ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

			if(SelectedProfile.IsValid())
			{
				SelectedProfile->SetPackageDirectory(FolderName);
			}
		}
	}

	return FReply::Handled();
}
FText SSessionLauncherCookPage::HandleCookModeComboButtonContentText( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		ELauncherProfileCookModes::Type CookMode = SelectedProfile->GetCookMode();

		if (CookMode == ELauncherProfileCookModes::ByTheBook)
		{
			return LOCTEXT("CookModeComboButton_ByTheBook", "By the book");
		}

		if (CookMode == ELauncherProfileCookModes::DoNotCook)
		{
			return LOCTEXT("CookModeComboButton_DoNotCook", "Do not cook");
		}

		if (CookMode == ELauncherProfileCookModes::OnTheFly)
		{
			return LOCTEXT("CookModeComboButton_OnTheFly", "On the fly");
		}

		return LOCTEXT("CookModeComboButtonDefaultText", "Select...");
	}

	return FText();
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION


void SProjectLauncherLaunchPage::Refresh( )
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		if (SelectedProfile->GetCookMode() == ELauncherProfileCookModes::ByTheBook)
		{
			AvailableCultures = SelectedProfile->GetCookedCultures();
		}
		else
		{
			FInternationalization::Get().GetCultureNames(AvailableCultures);
		}

		AvailableMaps = FGameProjectHelper::GetAvailableMaps(SelectedProfile->GetProjectBasePath(), SelectedProfile->SupportsEngineMaps(), true);

		DefaultRoleEditor->Refresh(SelectedProfile->GetDefaultLaunchRole());
	}
	else
	{
		AvailableCultures.Reset();
		AvailableMaps.Reset();

		DefaultRoleEditor->Refresh(NULL);
	}
}
FText SProjectLauncherLaunchPage::HandleLaunchModeComboButtonContentText( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		ELauncherProfileLaunchModes::Type LaunchMode = SelectedProfile->GetLaunchMode();

		if (LaunchMode == ELauncherProfileLaunchModes::CustomRoles)
		{
			return LOCTEXT("LaunchModeComboButtonCustomRolesText", "Using custom roles");
		}

		if (LaunchMode == ELauncherProfileLaunchModes::DefaultRole)
		{
			return LOCTEXT("LaunchModeComboButtonDefaultRoleText", "Using default role");
		}

		if (LaunchMode == ELauncherProfileLaunchModes::DoNotLaunch)
		{
			return LOCTEXT("LaunchModeComboButtonDoNotLaunchText", "Do not launch");
		}

		return LOCTEXT("LaunchModeComboButtonSelectText", "Select...");
	}

	return FText::GetEmpty();
}
FText SProjectLauncherPackagePage::HandlePackagingModeComboButtonContentText( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		ELauncherProfilePackagingModes::Type PackagingMode = SelectedProfile->GetPackagingMode();

		if (PackagingMode == ELauncherProfilePackagingModes::DoNotPackage)
		{
			return LOCTEXT("DoNotPackageAction", "Do not package");
		}

		if (PackagingMode == ELauncherProfilePackagingModes::Locally)
		{
			return LOCTEXT("LocallyAction", "Package & store locally");
		}

		if (PackagingMode == ELauncherProfilePackagingModes::SharedRepository)
		{
			return LOCTEXT("SharedRepositoryAction", "Package & store in repository");
		}

		return LOCTEXT("PackagingModeComboButtonDefaultText", "Select...");
	}

	return FText::GetEmpty();
}
void SSessionLauncherCookPage::HandleCookModeMenuEntryClicked( ELauncherProfileCookModes::Type CookMode )
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		SelectedProfile->SetCookMode(CookMode);
	}
}
void SProjectLauncherDeployToDeviceSettings::HandleIncrementalCheckBoxCheckStateChanged( ECheckBoxState NewState )
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		SelectedProfile->SetIncrementalDeploying(NewState == ECheckBoxState::Checked);
	}
}
void SProjectLauncherPackagePage::HandlePackagingModeMenuEntryClicked( ELauncherProfilePackagingModes::Type PackagingMode )
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		SelectedProfile->SetPackagingMode(PackagingMode);
	}
}
void SProjectLauncherDeployFileServerSettings::HandleHideWindowCheckBoxCheckStateChanged( ESlateCheckBoxState::Type NewState )
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		SelectedProfile->SetHideFileServerWindow(NewState == ESlateCheckBoxState::Checked);
	}
}
void SProjectLauncherDeployRepositorySettings::OnTextChanged(const FText& InText)
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if(SelectedProfile.IsValid())
	{
		SelectedProfile->SetPackageDirectory(InText.ToString());
	}
}
void SProjectLauncherPackagePage::HandlePackagingModeMenuEntryClicked( ELauncherProfilePackagingModes::Type PackagingMode )
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		SelectedProfile->SetPackagingMode(PackagingMode);

		check(ProjectLauncherPackagingSettings.IsValid());
		ProjectLauncherPackagingSettings->UpdateDirectoryPathText();
	}
}
void SProjectLauncherDeployRepositorySettings::OnTextCommitted( const FText& InText, ETextCommit::Type CommitInfo)
{
	if (CommitInfo == ETextCommit::OnEnter)
	{
		ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

		if(SelectedProfile.IsValid())
		{
			SelectedProfile->SetPackageDirectory(InText.ToString());
		}
	}
}
FText SProjectLauncherCookOnTheFlySettings::HandleCookOptionsTextBlockText() const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	FText result;

	if (SelectedProfile.IsValid())
	{
		result = FText::FromString(SelectedProfile->GetCookOptions());
	}

	return result;
}
EVisibility SProjectLauncherSimpleCookPage::HandleCookByTheBookSettingsVisibility( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		if (SelectedProfile->GetCookMode() == ELauncherProfileCookModes::ByTheBook)
		{
			return EVisibility::Visible;
		}
	}

	return EVisibility::Collapsed;
}
EVisibility SProjectLauncherCookOnTheFlySettings::HandleValidationErrorIconVisibility( ELauncherProfileValidationErrors::Type Error ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		if (SelectedProfile->HasValidationError(Error))
		{
			return EVisibility::Visible;
		}
	}

	return EVisibility::Hidden;
}
EVisibility SProjectLauncherLaunchPage::HandleValidationErrorIconVisibility( ELauncherProfileValidationErrors::Type Error ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		if (SelectedProfile->HasValidationError(Error))
		{
			return EVisibility::Visible;
		}
	}

	return EVisibility::Collapsed;
}
ESlateCheckBoxState::Type SProjectLauncherDeployFileServerSettings::HandleHideWindowCheckBoxIsChecked( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		if (SelectedProfile->IsFileServerHidden())
		{
			return ESlateCheckBoxState::Checked;
		}
	}

	return ESlateCheckBoxState::Unchecked;
}
EVisibility SProjectLauncherLaunchPage::HandleLaunchModeBoxVisibility( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		if (SelectedProfile->GetDeploymentMode() != ELauncherProfileDeploymentModes::DoNotDeploy)
		{
			return EVisibility::Visible;
		}
	}

	return EVisibility::Collapsed;
}
ECheckBoxState SProjectLauncherDeployToDeviceSettings::HandleIncrementalCheckBoxIsChecked( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		if (SelectedProfile->IsDeployingIncrementally())
		{
			return ECheckBoxState::Checked;
		}
	}

	return ECheckBoxState::Unchecked;
}
ESlateCheckBoxState::Type SProjectLauncherCookOnTheFlySettings::HandleIncrementalCheckBoxIsChecked( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		if (SelectedProfile->IsCookingIncrementally())
		{
			return ESlateCheckBoxState::Checked;
		}
	}

	return ESlateCheckBoxState::Unchecked;
}
EVisibility SSessionLauncherCookPage::HandleCookOnTheFlySettingsVisibility( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		if (SelectedProfile->GetCookMode() == ELauncherProfileCookModes::OnTheFly)
		{
			return EVisibility::Visible;
		}
	}

	return EVisibility::Collapsed;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION


/* SProjectLauncherCookOnTheFlySettings callbacks
 *****************************************************************************/

void SProjectLauncherCookOnTheFlySettings::HandleIncrementalCheckBoxCheckStateChanged( ESlateCheckBoxState::Type NewState )
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		SelectedProfile->SetIncrementalCooking(NewState == ESlateCheckBoxState::Checked);
	}
}
EVisibility SProjectLauncherPackagePage::HandlePackagingSettingsAreaVisibility( ) const
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		ELauncherProfilePackagingModes::Type PackagingMode = SelectedProfile->GetPackagingMode();

		if ((PackagingMode == ELauncherProfilePackagingModes::Locally) || (PackagingMode == ELauncherProfilePackagingModes::SharedRepository))
		{
			return EVisibility::Visible;
		}
	}

	return EVisibility::Collapsed;
}
void SProjectLauncherCookOnTheFlySettings::HandleCookerOptionsCommitted(const FText& NewText, ETextCommit::Type CommitType)
{
	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();

	if (SelectedProfile.IsValid())
	{
		FString useOptions = NewText.ToString();
		switch (CommitType)
		{
		case ETextCommit::Default:
		case ETextCommit::OnCleared:
			useOptions = TEXT("");
			break;
		default:
			break;
		}
		SelectedProfile->SetCookOptions(useOptions);
	}
}
void FLauncherProfileManager::AddProfile( const ILauncherProfileRef& Profile )
{
	if (!SavedProfiles.Contains(Profile))
	{
		// replace the existing profile
		ILauncherProfilePtr ExistingProfile = GetProfile(Profile->GetId());

		if (ExistingProfile.IsValid())
		{
			RemoveProfile(ExistingProfile.ToSharedRef());
		}

		if (!Profile->GetDeployedDeviceGroup().IsValid())
		{
			Profile->SetDeployedDeviceGroup(AddNewDeviceGroup());
		}

		// add the new profile
		SavedProfiles.Add(Profile);
		AllProfiles.Add(Profile);

		ProfileAddedDelegate.Broadcast(Profile);
	}
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SProjectLauncherLaunchPage::Construct( const FArguments& InArgs, const FProjectLauncherModelRef& InModel )
{
	Model = InModel;

	// create launch modes menu
	FMenuBuilder LaunchModeMenuBuilder(true, NULL);
	{
		FUIAction DefaultRoleAction(FExecuteAction::CreateSP(this, &SProjectLauncherLaunchPage::HandleLaunchModeMenuEntryClicked, ELauncherProfileLaunchModes::DefaultRole));
		LaunchModeMenuBuilder.AddMenuEntry(LOCTEXT("DefaultRoleAction", "Using default role"), LOCTEXT("DefaultRoleActionHint", "Launch with the default role on all deployed devices."), FSlateIcon(), DefaultRoleAction);
		
		FUIAction CustomRolesAction(FExecuteAction::CreateSP(this, &SProjectLauncherLaunchPage::HandleLaunchModeMenuEntryClicked, ELauncherProfileLaunchModes::CustomRoles));
		LaunchModeMenuBuilder.AddMenuEntry(LOCTEXT("CustomRolesAction", "Using custom roles"), LOCTEXT("CustomRolesActionHint", "Launch with per-device custom roles."), FSlateIcon(), CustomRolesAction);

		FUIAction DoNotLaunchAction(FExecuteAction::CreateSP(this, &SProjectLauncherLaunchPage::HandleLaunchModeMenuEntryClicked, ELauncherProfileLaunchModes::DoNotLaunch));
		LaunchModeMenuBuilder.AddMenuEntry(LOCTEXT("DoNotCookAction", "Do not launch"), LOCTEXT("DoNotCookActionHint", "Do not launch the build at this time."), FSlateIcon(), DoNotLaunchAction);
	}

	ChildSlot
	[
		SNew(SVerticalBox)

		+ SVerticalBox::Slot()
			.AutoHeight()
			[
				SNew(SHorizontalBox)
					.Visibility(this, &SProjectLauncherLaunchPage::HandleLaunchModeBoxVisibility)

				+ SHorizontalBox::Slot()
					.FillWidth(1.0)
					.VAlign(VAlign_Center)
					[
						SNew(STextBlock)
							.Text(LOCTEXT("HowToLaunchText", "How would you like to launch?"))
					]

				+ SHorizontalBox::Slot()
					.AutoWidth()
					.Padding(8.0, 0.0, 0.0, 0.0)
					[
						// launch mode menu
						SNew(SComboButton)
							.ButtonContent()
							[
								SNew(STextBlock)
									.Text(this, &SProjectLauncherLaunchPage::HandleLaunchModeComboButtonContentText)
							]
							.ContentPadding(FMargin(6.0, 2.0))
							.MenuContent()
							[
								LaunchModeMenuBuilder.MakeWidget()
							]
					]
			]

		+ SVerticalBox::Slot()
			.AutoHeight()
			.Padding(0.0f, 8.0f, 0.0f, 0.0f)
			[
				SNew(SBorder)
					.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
					.Padding(8.0)
					.Visibility(this, &SProjectLauncherLaunchPage::HandleValidationErrorIconVisibility, ELauncherProfileValidationErrors::CustomRolesNotSupportedYet)
					[
						SNew(SHorizontalBox)

						+ SHorizontalBox::Slot()
							.AutoWidth()
							[
								SNew(SImage)
									.Image(FEditorStyle::GetBrush(TEXT("Icons.Error")))
							]

						+ SHorizontalBox::Slot()
							.AutoWidth()
							.Padding(4.0f, 0.0f)
							.VAlign(VAlign_Center)
							[
								SNew(STextBlock)
									.Text(LOCTEXT("CopyToDeviceRequiresCookByTheBookText", "Custom launch roles are not supported yet."))
							]
					]
			]

		+ SVerticalBox::Slot()
			.AutoHeight()
			.Padding(0.0, 8.0, 0.0, 0.0)
			[
				SNew(SExpandableArea)
					.AreaTitle(LOCTEXT("DefaultRoleAreaTitle", "Default Role"))
					.InitiallyCollapsed(false)
					.Padding(8.0)
					.Visibility(this, &SProjectLauncherLaunchPage::HandleLaunchSettingsVisibility)
					.BodyContent()
					[
						// launch settings area
						SAssignNew(DefaultRoleEditor, SProjectLauncherLaunchRoleEditor)
							.AvailableCultures(&AvailableCultures)
							.AvailableMaps(&AvailableMaps)
					]
			]

		+ SVerticalBox::Slot()
			.AutoHeight()
			.Padding(0.0, 4.0, 0.0, 0.0)
			[
				SNew(STextBlock)
					.Text(LOCTEXT("CannotLaunchText", "The build is not being deployed and cannot be launched."))
					.Visibility(this, &SProjectLauncherLaunchPage::HandleCannotLaunchTextBlockVisibility)
			]
	];

	Model->OnProfileSelected().AddSP(this, &SProjectLauncherLaunchPage::HandleProfileManagerProfileSelected);

	ILauncherProfilePtr SelectedProfile = Model->GetSelectedProfile();
	if (SelectedProfile.IsValid())
	{
		SelectedProfile->OnProjectChanged().AddSP(this, &SProjectLauncherLaunchPage::HandleProfileProjectChanged);
	}

	Refresh();
}
void FLauncherProfileManager::LoadProfiles( )
{
	TArray<FString> ProfileFileNames;

	//load and move legacy profiles
	{
		IFileManager::Get().FindFilesRecursive(ProfileFileNames, *GetLegacyProfileFolder(), TEXT("*.ulp"), true, false);
		for (TArray<FString>::TConstIterator It(ProfileFileNames); It; ++It)
		{
			FString ProfileFilePath = *It;
			FArchive* ProfileFileReader = IFileManager::Get().CreateFileReader(*ProfileFilePath);

			if (ProfileFileReader != nullptr)
			{
				ILauncherProfilePtr LoadedProfile = LoadProfile(*ProfileFileReader);
				delete ProfileFileReader;

				//re-save profile to new location
				if (LoadedProfile.IsValid())
				{
					SaveProfile(LoadedProfile.ToSharedRef());
				}

				//delete legacy profile.
				IFileManager::Get().Delete(*ProfileFilePath);				
			}
		}
	}

	//load and re-save legacy profiles
	{
		IFileManager::Get().FindFilesRecursive(ProfileFileNames, *FLauncherProfile::GetProfileFolder(), TEXT("*.ulp"), true, false);
		for (TArray<FString>::TConstIterator It(ProfileFileNames); It; ++It)
		{
			FString ProfileFilePath = *It;
			FArchive* ProfileFileReader = IFileManager::Get().CreateFileReader(*ProfileFilePath);

			if (ProfileFileReader != nullptr)
			{
				ILauncherProfilePtr LoadedProfile = LoadProfile(*ProfileFileReader);
				delete ProfileFileReader;

				//re-save profile to the new format
				if (LoadedProfile.IsValid())
				{
					if (ProfileFilePath.Contains("NotForLicensees"))
					{
						LoadedProfile->SetNotForLicensees();
					}
					SaveJSONProfile(LoadedProfile.ToSharedRef());
				}

				//delete legacy profile.
				IFileManager::Get().Delete(*ProfileFilePath);
			}
		}
	}

	ProfileFileNames.Reset();
	IFileManager::Get().FindFilesRecursive(ProfileFileNames, *FLauncherProfile::GetProfileFolder(), TEXT("*.ulp2"), true, false);
	
	for (TArray<FString>::TConstIterator It(ProfileFileNames); It; ++It)
	{
		FString ProfileFilePath = *It;
		ILauncherProfilePtr LoadedProfile = LoadJSONProfile(*ProfileFilePath);

		if (LoadedProfile.IsValid())
		{
			if (ProfileFilePath.Contains("NotForLicensees"))
			{
				LoadedProfile->SetNotForLicensees();
			}
			AddProfile(LoadedProfile.ToSharedRef());
		}
		else
		{
			IFileManager::Get().Delete(*ProfileFilePath);
		}
	}
}