using System.Collections.Generic;
using System.Linq;
namespace StandardScene.Magnetic.Tasking
{
///
/// 断线重连后,用当前占点匹配 tasklist 链路的结果。
///
public sealed class Fass2ChainMatch
{
public int TaskId { get; set; }
public int TaskPriority { get; set; }
public int CurrentSiteId { get; set; }
public int TargetSiteId { get; set; }
public int IndexInPath { get; set; }
public int DistanceToTarget { get; set; }
public bool IsAtStartPoint { get; set; }
public bool IsAtEndPoint { get; set; }
public IReadOnlyList FullPath { get; set; } = new List();
public IReadOnlyList RemainingPath { get; set; } = new List();
public override string ToString()
{
var position = IsAtStartPoint ? "start" : (IsAtEndPoint ? "end" : "mid");
return $"task={TaskId} {CurrentSiteId}->{TargetSiteId} pos={position} remain={DistanceToTarget} prio={TaskPriority}";
}
}
///
/// 一站多链路时的固定裁决:原 goal 仍在剩余路径 → 起点 → 优先级 → 距目标更近。
///
public static class Fass2ReconnectChainSelector
{
public static bool TrySelect(
IReadOnlyList matches,
int? preferGoalSiteId,
out Fass2ChainMatch selected,
out string reason)
{
selected = null;
reason = "no chain";
if (matches == null || matches.Count == 0)
{
return false;
}
var candidates = matches.Where(m => m != null && m.TargetSiteId > 0).ToList();
if (candidates.Count == 0)
{
return false;
}
if (preferGoalSiteId != null && preferGoalSiteId.Value > 0)
{
var keepGoal = candidates
.Where(m => RemainingContains(m, preferGoalSiteId.Value))
.ToList();
if (keepGoal.Count == 1)
{
selected = keepGoal[0];
reason = $"preferGoal={preferGoalSiteId.Value}";
return true;
}
if (keepGoal.Count > 1)
{
candidates = keepGoal;
reason = $"preferGoal={preferGoalSiteId.Value} narrowed={keepGoal.Count}";
}
}
var ranked = candidates
.OrderByDescending(m => m.IsAtStartPoint ? 1 : 0)
.ThenByDescending(m => m.TaskPriority)
.ThenBy(m => m.DistanceToTarget)
.ThenBy(m => m.TaskId)
.ToList();
if (ranked.Count == 0)
{
return false;
}
var best = ranked[0];
if (ranked.Count > 1)
{
var second = ranked[1];
var tied = best.IsAtStartPoint == second.IsAtStartPoint &&
best.TaskPriority == second.TaskPriority &&
best.DistanceToTarget == second.DistanceToTarget;
if (tied && best.TargetSiteId != second.TargetSiteId)
{
reason = $"ambiguous {best} vs {second}";
return false;
}
}
selected = best;
if (string.IsNullOrEmpty(reason) || reason == "no chain")
{
reason = $"selected {best}";
}
else
{
reason = $"{reason}; selected {best}";
}
return true;
}
private static bool RemainingContains(Fass2ChainMatch match, int siteId)
{
if (match.RemainingPath != null)
{
for (var i = 0; i < match.RemainingPath.Count; i++)
{
if (match.RemainingPath[i] == siteId)
{
return true;
}
}
}
return match.TargetSiteId == siteId;
}
}
}