#pragma once
#include "GameFramework/GameMode.h"

#include "GasaCommon.h"
#include "Networking/GasaNetLibrary_Inlines.h"
#include "Engine/Engine.h"

#include "GasaGameMode.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLogoutSig, AGasaPlayerController*, PC);

UCLASS( Blueprintable )
class GASA_API AGasaGameMode : public AGameMode
{
	GENERATED_BODY()
public:
	UPROPERTY(EditAnywhere, BlueprintAssignable)
	FOnLogoutSig Event_OnLogout;
	
#pragma region GameFramework
	UFUNCTION()
	void OnGameFrameworkInitialized();
	
	UFUNCTION()
	void OwningClient_OnGameFrameworkInitialized(AGasaPlayerController* PC);

	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta=(DisplayName = "On Game Framework Initialized"))
	void BP_OnGameFrameworkInitialized();
#pragma endregion GameFramework
	
#pragma region NetSlime
	// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
	FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
	FORCEINLINE bool IsClient()               const { return Gasa::IsClient( this ); }
	FORCEINLINE bool IsListenServer()         const { return Gasa::IsListenServer( this ); }
	FORCEINLINE bool IsNetOwner()             const { return Gasa::IsNetOwner( this ); }
	FORCEINLINE bool IsServer()               const { return Gasa::IsServer( this ); }
	FORCEINLINE bool IsSimulatedProxy()       const { return Gasa::IsSimulatedProxy( this ); }
	FORCEINLINE void NetLog(
		FString           Message,
		EGasaVerbosity    Verbosity = EGasaVerbosity::Log,
		FLogCategoryBase& Category  = LogGasaNet,
		bool              DumpStack = false,
		int32             Line      = __builtin_LINE(),
		ANSICHAR const*   File      = __builtin_FILE(),
		ANSICHAR const*   Func      = __builtin_FUNCTION()
	)
	{
		Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
	}
#pragma endregion NetSlime

#pragma region GameMode
	void HandleMatchAborted() override;
	void HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer) override;
	
	bool ReadyToStartMatch_Implementation() override;
#pragma endregion GameMode

#pragma region GameModeBase
	void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

	void FinishRestartPlayer(AController* NewPlayer, const FRotator& StartRotation) override;

	void GenericPlayerInitialization(AController* C) override;

	TSubclassOf<APlayerController> GetPlayerControllerClassToSpawnForSeamlessTravel(APlayerController* PreviousPlayerController) override;

	void HandleSeamlessTravelPlayer(AController*& C) override;

	void InitializeHUDForPlayer_Implementation(APlayerController* NewPlayer) override;
	void InitSeamlessTravelPlayer(AController* NewController) override;
	
	void Logout(AController* Exiting) override;
	
	void PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) override;
	void PostLogin(APlayerController* NewPlayer) override;
	void PostSeamlessTravel() override;

	void SetPlayerDefaults(APawn* PlayerPawn) override;
	void SetSeamlessTravelViewTarget(APlayerController* PC) override;
	
	void StartPlay() override;
	void StartToLeaveMap() override;

	void SwapPlayerControllers(APlayerController* OldPC, APlayerController* NewPC) override;

	APlayerController* SpawnPlayerControllerCommon(ENetRole InRemoteRole, FVector const& SpawnLocation, FRotator const& SpawnRotation, TSubclassOf<APlayerController> InPlayerControllerClass) override;
#pragma endregion GameModeBase
};

namespace Gasa
{
	inline AGasaGameMode* GetGameMode( UObject* Context )
	{
		UWorld* World = GEngine->GetWorldFromContextObject( Context, EGetWorldErrorMode::LogAndReturnNull );
		if ( World == nullptr )
		{
			Log( "World is null... are you running in a proper context?", ELogV::Error );
			return nullptr;
		}
		return Cast<AGasaGameMode>( World->GetAuthGameMode() );
	}
}
