#pragma once
#include "GameFramework/GameState.h"
#include "GameStateBase.Generated.h"

/*
	Lobby state
*/

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerStateChange, class APlayerControllerBase*, playerController);

USTRUCT()
struct FLobbyStatePlayer
{
	GENERATED_USTRUCT_BODY()
public:
	int32 team;
	TSharedPtr<const FUniqueNetId> netID;
};

UCLASS()
class ALobbyState : public AInfo
{
	GENERATED_BODY()
public:
	UPROPERTY()
	TArray<FLobbyStatePlayer> players;
};

/*
	Base game state, keeping all the players in the current game and keeping their assigned teams
*/
UCLASS()
class AGameStateBase : public AGameState
{
	GENERATED_BODY()
	friend class AMenuGameMode;

public:
	AGameStateBase();
	virtual void BeginPlay() override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);

	virtual void RegisterPlayer(class APlayerControllerBase* player);
	virtual void DeregisterPlayer(class APlayerControllerBase* player);

	// Tells all the players to go to the lobby screen
	void EnterLobby();

	TArray<class APlayerStateBase*>& GetPlayers();
	template<typename T> TArray<T*> GetPlayers()
	{
		return reinterpret_cast<TArray<T*>&>(GetPlayers());
	}
	TArray<TArray<class APlayerStateBase*>> GetPlayersByTeam();
	template<typename T> TArray<TArray<T*>> GetPlayersByTeam()
	{
		auto r = GetPlayersByTeam(); 
		return reinterpret_cast<TArray<TArray<T*>>&>(r);
	}

	UFUNCTION(BlueprintCallable, Category = "Players")
	TArray<class APlayerControllerBase*> GetPlayerControllers()
	{
		TArray<class APlayerControllerBase*> result;
		m_playersByController.GenerateKeyArray(result);
		return result;
	}

	TArray<int32> GetTeamSizes();
	int32 GetOptimalAutoJoinTeam();

	UFUNCTION(BlueprintCallable, Category="Team")
	int32 GetMapTeamCount() const;

	UFUNCTION(BlueprintCallable, Category = "Map")
	UMapData* GetMapData() const;

	UPROPERTY(Replicated, ReplicatedUsing = m_OnRep_MapPath, BlueprintReadOnly, Category = "Map")
	FString mapPath;

	// Called when a player has joined/left or switched team
	UFUNCTION(NetMulticast, Reliable)
		void OnPlayerStateChange_Multi();
	UPROPERTY(BlueprintAssignable, Category = Events)
		FOnPlayerStateChange OnPlayerStateChange;

	TMap<class APlayerControllerBase*, class APlayerStateBase*> GetPlayersByController();

private:
	// Tries to load the current map data
	bool m_LoadActiveMapInfo();
	UFUNCTION()
	void m_OnRep_MapPath();

	UPROPERTY(Replicated)
	int32 m_mapTeamCount;
	UPROPERTY()
	class UMapData* m_mapData;
	// For debugging
	bool m_hasWarnedFrank;

	UPROPERTY()
	TMap<class APlayerControllerBase*, class APlayerStateBase*> m_playersByController;
	UPROPERTY(Replicated)
	TArray<class APlayerStateBase*> m_players;
};