// Copyright Black Iron Spine 2026 All Rights Reserved.
#include "MapsBuyerCheckpoint.h"
#include "RMCNavigationSubsystem.h"
#include "GameFramework/PlayerController.h"
#include "Engine/LocalPlayer.h"
#include "Engine/World.h"
bool AMapsBuyerCheckpoint::Connect(APlayerController* OwningPlayer,FText& Failure)
{
    Failure=FText::GetEmpty();
    auto* Player=IsValid(OwningPlayer) ? OwningPlayer->GetLocalPlayer() : nullptr;
    auto* Next=Player ? Player->GetSubsystem<URMCNavigationSubsystem>() : nullptr;
    if(!IsValid(Next) || Next->GetTickableGameObjectWorld()!=GetWorld() || !GetWorld() || !GetWorld()->IsGameWorld())
    { Failure=FText::FromString(TEXT("Supply this world's owning local-player controller."));return false; }
    if(IsValid(Navigation) && Navigation!=Next)
    { Failure=FText::FromString(TEXT("Disconnect the previous owner before rebinding."));return false; }
    Navigation=Next;Navigation->OnWaypointsChanged.AddUniqueDynamic(this,&AMapsBuyerCheckpoint::ObserveWaypoints);return true;
}
bool AMapsBuyerCheckpoint::SetDestination(FVector WorldLocation,const FString& Name,FText& Failure)
{
    Failure=FText::GetEmpty();
    if(!IsValid(Navigation)){Failure=FText::FromString(TEXT("Connect navigation first."));return false;}
    if(DestinationId.IsValid()){Failure=FText::FromString(TEXT("Remove the retained destination before replacing it."));return false;}
    return Navigation->TryAddUserWaypoint(WorldLocation,Name,DestinationId,Failure);
}
bool AMapsBuyerCheckpoint::RenameDestination(const FString& Name,FText& Failure)
{
    Failure=FText::GetEmpty();
    if(!IsValid(Navigation) || !Navigation->RenameUserWaypoint(DestinationId,Name))
    { Failure=FText::FromString(TEXT("The retained ID/name is unavailable or progress is protected."));return false; }return true;
}
bool AMapsBuyerCheckpoint::RemoveDestination(FText& Failure)
{
    Failure=FText::GetEmpty();
    if(!IsValid(Navigation) || !Navigation->RemoveUserWaypoint(DestinationId))
    { Failure=FText::FromString(TEXT("The retained ID is unavailable or progress is protected."));return false; }
    DestinationId.Invalidate();return true;
}
bool AMapsBuyerCheckpoint::SaveProgress(FText& Failure)
{
    Failure=FText::GetEmpty();
    if(!IsValid(Navigation)){Failure=FText::FromString(TEXT("Connect navigation first."));return false;}
    return Navigation->TrySaveProgressVerified(Failure);
}
bool AMapsBuyerCheckpoint::ReadView(FVector& PlayerLocation,float& PlayerYaw,FVector2D& MapPan,FText& Failure) const
{
    PlayerLocation=FVector::ZeroVector;PlayerYaw=0;MapPan=FVector2D::ZeroVector;Failure=FText::GetEmpty();
    if(!IsValid(Navigation) || !Navigation->IsPlayerPoseReady()){Failure=FText::FromString(TEXT("Connect navigation and possess a rooted local pawn in this world before reading position and heading."));return false;}
    PlayerLocation=Navigation->GetPlayerLocation();PlayerYaw=Navigation->GetPlayerYaw();MapPan=Navigation->GetWorldMapPan();return true;
}
bool AMapsBuyerCheckpoint::ReadProgress(int32& ExploredCells,bool& ReadOnly,bool& SavePending,FText& Failure) const
{
    ExploredCells=0;ReadOnly=false;SavePending=false;Failure=FText::GetEmpty();
    if(!IsValid(Navigation) || !Navigation->IsProgressReady()){Failure=FText::FromString(TEXT("Connect navigation and wait for this level's first progress tick before reading its state."));return false;}
    ExploredCells=Navigation->GetExploredCellCount();ReadOnly=Navigation->IsProgressReadOnly();SavePending=Navigation->IsProgressSavePending();return true;
}
bool AMapsBuyerCheckpoint::ReadSettings(FRMCNavigationSettings& Settings,FString& SaveLabel,FText& Failure) const
{
    Settings=FRMCNavigationSettings();SaveLabel.Reset();Failure=FText::GetEmpty();
    if(!IsValid(Navigation)){Failure=FText::FromString(TEXT("Connect navigation first."));return false;}
    Settings=Navigation->GetNavigationSettings();SaveLabel=Navigation->GetSettingsSaveLabel();return true;
}
bool AMapsBuyerCheckpoint::ApplySettings(const FRMCNavigationSettings& Settings,FText& Failure)
{
    Failure=FText::GetEmpty();if(!IsValid(Navigation)){Failure=FText::FromString(TEXT("Connect navigation first."));return false;}
    Navigation->SetNavigationSettings(Settings);
    if(!Navigation->SaveSetupVerified()){Failure=FText::FromString(TEXT("Settings changed in memory, but disk receipt failed; retry."));return false;}return true;
}
bool AMapsBuyerCheckpoint::SetSurfaceState(bool WorldMapOpen,bool CompassVisible,bool ExternalCoordinatorActive,FText& Failure)
{
    Failure=FText::GetEmpty();if(!IsValid(Navigation)){Failure=FText::FromString(TEXT("Connect navigation first."));return false;}
    Navigation->SetWorldMapOpen(WorldMapOpen);Navigation->SetCompassVisible(CompassVisible);Navigation->SetExternalSuiteCoordinatorActive(ExternalCoordinatorActive);return true;
}
bool AMapsBuyerCheckpoint::Cancel(){return IsValid(Navigation) && Navigation->DismissTopSurface();}
void AMapsBuyerCheckpoint::ObserveWaypoints()
{
    if(!IsValid(Navigation))return;
    const auto Snapshot=Navigation->GetUserWaypoints();
    ObservedWaypointCount=Snapshot.Num();
    // Read-only quest/map UI refresh. Never create a waypoint or grant a reward here.
    UE_LOG(LogTemp,Display,TEXT("Buyer navigation now has %d waypoints."),Snapshot.Num());
}
void AMapsBuyerCheckpoint::Disconnect()
{
    if(IsValid(Navigation))Navigation->OnWaypointsChanged.RemoveDynamic(this,&AMapsBuyerCheckpoint::ObserveWaypoints);
    Navigation=nullptr;
    // Retained GUID is scoped to its owner/level. Reinitialize after selecting a different one.
    DestinationId.Invalidate();
}
void AMapsBuyerCheckpoint::EndPlay(const EEndPlayReason::Type Reason){Disconnect();Super::EndPlay(Reason);}
