#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Cluster/IDisplayClusterClusterManager.h"
#include "Cluster/DisplayClusterClusterEvent.h"

#include "UserInputSync.generated.h"

UCLASS()
class AUserInputSync : public AActor
{
  GENERATED_BODY()
	
public:	

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

  UFUNCTION(BlueprintCallable)
  void OnUserInput();

  void ReactOnUserInput();

private:	

  FOnClusterEventListener ClusterEventListenerDelegate;
  void HandleClusterEvent(const FDisplayClusterClusterEvent& Event);

};


// In UserInputSync.cpp:
#include "UserInputSync.h"

#include "IUniversalLogging.h" //just here to give some feedback on the user's action
#include "Engine.h"
#include "IDisplayCluster.h"


void AUserInputSync::BeginPlay() {
  Super::BeginPlay();
  //register the listener
  IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
  if (ClusterManager && !ClusterEventListenerDelegate.IsBound())
  {
    ClusterEventListenerDelegate = FOnClusterEventListener::CreateUObject(this, &AUserInputSync::HandleClusterEvent);
    ClusterManager->AddClusterEventListener(ClusterEventListenerDelegate);
  }
  
  //so the log shows on all slaves on screen
  UniLog.GetDefaultLogStream()->SetLogOnScreenOnMaster(true);
  UniLog.GetDefaultLogStream()->SetLogOnScreenOnSlaves(true);
}

void AUserInputSync::EndPlay(const EEndPlayReason::Type EndPlayReason) {
  //de-register the listener again
  IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
  if (ClusterManager && ClusterEventListenerDelegate.IsBound())
  {
    ClusterManager->RemoveClusterEventListener(ClusterEventListenerDelegate);
  }
  Super::EndPlay(EndPlayReason);
}

void AUserInputSync::ReactOnUserInput() {
  //some action you want to perform on all nodes, e.g.:
  UniLog.Log("Input received!");
}

void AUserInputSync::OnUserInput() {
  IDisplayClusterClusterManager* const Manager = IDisplayCluster::Get().GetClusterMgr();
  if (Manager)
  {
    if (Manager->IsStandalone()) {
      //in standalone (e.g., desktop editor play) cluster events are not executed....
      ReactOnUserInput();
    }
    else {
      // else create a cluster event to react to
      FDisplayClusterClusterEvent cluster_event;
      cluster_event.Name = "Some Custom Name, e.g., G pressed";
      Manager->EmitClusterEvent(cluster_event, true);
    }
  }
}

void AUserInputSync::HandleClusterEvent(const FDisplayClusterClusterEvent& Event) {
  if (Event.Name == "Some Custom Name, e.g., G pressed") {
    //now we actually react on all cluster nodes:
    ReactOnUserInput();
  }
}