using System;
using System.Linq;
using Dalamud.Game.Gui.ContextMenu;
using Dalamud.Game.Text.SeStringHandling;
using LightlessSync.API.Data;
using LightlessSync.API.Data.Enum;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto.User;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using Microsoft.Extensions.Logging;
using LightlessSync.WebAPI;
namespace LightlessSync.PlayerData.Pairs;
///
/// ui wrapper around a pair connection
///
public class Pair
{
private readonly PairLedger _pairLedger;
private readonly ILogger _logger;
private readonly LightlessMediator _mediator;
private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly Lazy _apiController;
public Pair(
ILogger logger,
UserFullPairDto userPair,
PairLedger pairLedger,
LightlessMediator mediator,
ServerConfigurationManager serverConfigurationManager,
Lazy apiController)
{
_logger = logger;
UserPair = userPair;
_pairLedger = pairLedger;
_mediator = mediator;
_serverConfigurationManager = serverConfigurationManager;
_apiController = apiController;
}
private PairUniqueIdentifier PairIdent => UniqueIdent;
private IPairHandlerAdapter? TryGetHandler()
{
return _pairLedger.GetHandler(PairIdent);
}
private PairConnection? TryGetConnection()
{
return _pairLedger.TryGetEntry(PairIdent, out var entry) && entry is not null
? entry.Connection
: null;
}
public bool HasCachedPlayer => TryGetHandler() is not null;
public IndividualPairStatus IndividualPairStatus => UserPair.IndividualPairStatus;
public bool IsDirectlyPaired => IndividualPairStatus != IndividualPairStatus.None;
public bool IsOneSidedPair => IndividualPairStatus == IndividualPairStatus.OneSided;
public bool IsOnline => TryGetConnection()?.IsOnline ?? false;
public bool IsPaired => IndividualPairStatus == IndividualPairStatus.Bidirectional || UserPair.Groups.Any();
public bool IsPaused => UserPair.OwnPermissions.IsPaused();
public bool IsVisible => _pairLedger.IsPairVisible(PairIdent);
public CharacterData? LastReceivedCharacterData => TryGetHandler()?.LastReceivedCharacterData;
public string? PlayerName => TryGetHandler()?.PlayerName ?? UserPair.User.AliasOrUID;
public long LastAppliedDataBytes => TryGetHandler()?.LastAppliedDataBytes ?? -1;
public long LastAppliedDataTris => TryGetHandler()?.LastAppliedDataTris ?? -1;
public long LastAppliedApproximateVRAMBytes => TryGetHandler()?.LastAppliedApproximateVRAMBytes ?? -1;
public long LastAppliedApproximateEffectiveVRAMBytes => TryGetHandler()?.LastAppliedApproximateEffectiveVRAMBytes ?? -1;
public string Ident => TryGetHandler()?.Ident ?? TryGetConnection()?.Ident ?? string.Empty;
public uint PlayerCharacterId => TryGetHandler()?.PlayerCharacterId ?? uint.MaxValue;
public PairUniqueIdentifier UniqueIdent => new(UserData.UID);
public UserData UserData => UserPair.User;
public UserFullPairDto UserPair { get; set; }
public void AddContextMenu(IMenuOpenedArgs args)
{
var handler = TryGetHandler();
if (handler is null)
{
return;
}
if (args.Target is not MenuTargetDefault target || target.TargetObjectId != handler.PlayerCharacterId || IsPaused)
{
return;
}
var openProfileSeString = new SeStringBuilder().AddText("Open Profile").Build();
var reapplyDataSeString = new SeStringBuilder().AddText("Reapply last data").Build();
var cyclePauseState = new SeStringBuilder().AddText("Cycle pause state").Build();
var changePermissions = new SeStringBuilder().AddText("Change Permissions").Build();
args.AddMenuItem(new MenuItem
{
Name = openProfileSeString,
OnClicked = _ => _mediator.Publish(new ProfileOpenStandaloneMessage(this)),
UseDefaultPrefix = false,
PrefixChar = 'L',
PrefixColor = 708
});
args.AddMenuItem(new MenuItem
{
Name = reapplyDataSeString,
OnClicked = _ => ApplyLastReceivedData(forced: true),
UseDefaultPrefix = false,
PrefixChar = 'L',
PrefixColor = 708
});
args.AddMenuItem(new MenuItem
{
Name = changePermissions,
OnClicked = _ => _mediator.Publish(new OpenPermissionWindow(this)),
UseDefaultPrefix = false,
PrefixChar = 'L',
PrefixColor = 708
});
args.AddMenuItem(new MenuItem
{
Name = cyclePauseState,
OnClicked = _ =>
{
TriggerCyclePause();
},
UseDefaultPrefix = false,
PrefixChar = 'L',
PrefixColor = 708
});
}
public void ApplyData(OnlineUserCharaDataDto data)
{
_logger.LogTrace("Character data received for {Uid}; handler will process via registry.", UserData.UID);
}
private void TriggerCyclePause()
{
_ = _apiController.Value.CyclePauseAsync(this);
}
public void ApplyLastReceivedData(bool forced = false)
{
var handler = TryGetHandler();
if (handler is null)
{
_logger.LogTrace("ApplyLastReceivedData skipped for {Uid}: handler missing.", UserData.UID);
return;
}
handler.ApplyLastReceivedData(forced);
}
public void CreateCachedPlayer(OnlineUserIdentDto? dto = null)
{
var handler = TryGetHandler();
if (handler is null)
{
_logger.LogTrace("CreateCachedPlayer skipped for {Uid}: handler unavailable.", UserData.UID);
return;
}
if (!handler.Initialized)
{
handler.Initialize();
}
}
public string? GetNote()
{
return _serverConfigurationManager.GetNoteForUid(UserData.UID);
}
public string GetPlayerNameHash()
{
return TryGetHandler()?.PlayerNameHash ?? string.Empty;
}
public bool HasAnyConnection()
{
return UserPair.Groups.Any() || UserPair.IndividualPairStatus != IndividualPairStatus.None;
}
public void MarkOffline(bool wait = true)
{
_logger.LogTrace("MarkOffline invoked for {Uid} (wait: {Wait}). New registry handles handler disposal.", UserData.UID, wait);
}
public void SetNote(string note)
{
_serverConfigurationManager.SetNoteForUid(UserData.UID, note);
}
internal void SetIsUploading()
{
var handler = TryGetHandler();
if (handler is null)
{
return;
}
handler.SetUploading(true);
}
}