using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Planning;
/// <summary>Only original active trade initiatives, exact destination identities and confirmed goods admit a CG sale edge.</summary>
public sealed class CommunityGoalSupplyScope
{
private readonly Dictionary<long,HashSet<string>> accepted;
private CommunityGoalSupplyScope(IReadOnlyList<CommunityGoalInfo> goals,Dictionary<long,HashSet<string>> accepted,string? issue)
{Goals=goals;this.accepted=accepted;Issue=issue;}
public IReadOnlyList<CommunityGoalInfo> Goals{get;}
public string? Issue{get;}
public bool Available=>accepted.Values.Any(goods=>goods.Count>0);
public static CommunityGoalSupplyScope Create(MarketBook book,IReadOnlyList<CommunityGoalInfo>? catalogue,DateTimeOffset now,IReadOnlyCollection<string>? commodityFilter=null)
{
var confirmed=(catalogue??[]).GroupBy(goal=>goal.Id).Where(group=>group.Key is >0 and <=uint.MaxValue&&group.Count()==1).Select(group=>group.Single())
.Where(goal=>goal.Active&&goal.Expires>now&&goal.MarketId>0&&goal.GoalType=="trade"&&goal.Activity.StartsWith("trade",StringComparison.Ordinal)
&&!string.IsNullOrWhiteSpace(goal.SystemName)&&!string.IsNullOrWhiteSpace(goal.StationName)
&&goal.AcceptedCommodities.Count>0&&goal.AcceptedCommodities.All(goods=>!string.IsNullOrWhiteSpace(CommodityCatalog.Key(goods))))
.ToArray();
var byMarket=book.Stations.GroupBy(station=>station.MarketId).ToDictionary(group=>group.Key,group=>group.ToArray());
var matches=confirmed.Where(goal=>byMarket.TryGetValue(goal.MarketId,out var stations)&&stations.Length==1
&&stations[0].SystemName.Equals(goal.SystemName,StringComparison.OrdinalIgnoreCase)&&stations[0].Name.Equals(goal.StationName,StringComparison.OrdinalIgnoreCase)).ToArray();
var accepted=matches.GroupBy(goal=>goal.MarketId).ToDictionary(group=>group.Key,group=>group.SelectMany(goal=>goal.AcceptedCommodities)
.Select(goods=>CommodityCatalog.Key(goods)).ToHashSet(StringComparer.Ordinal));
var filter=TradeSearchConstraints.NormalizeCommodityFilter(commodityFilter).ToHashSet(StringComparer.Ordinal);
if(filter.Count>0)foreach(var goods in accepted.Values)goods.IntersectWith(filter);
var issue=matches.Length>0?accepted.Values.Any(goods=>goods.Count>0)?null:"商品筛选与有效 CG 的接收商品没有交集;请清空商品筛选或选择活动接收的商品。未回退普通跑商。":confirmed.Length>0
?"当前行情范围内没有身份匹配的 CG 交付站;请调整中心或半径并补全活动站行情。未回退普通跑商。"
:"未确认仍有效的贸易 CG、交付站与接收商品,暂不能推荐 CG 供货路线。未回退普通跑商。";
return new(matches,accepted,issue);
}
public bool AllowsSale(CommodityQuote quote)=>accepted.TryGetValue(quote.MarketId,out var goods)&&goods.Contains(CommodityCatalog.Key(quote.Commodity));
public static bool MatchesRoute(IReadOnlyList<CommunityGoalInfo> catalogue,RoutePlan plan,DateTimeOffset now)
{
var stations=plan.Legs.SelectMany(leg=>new[]{leg.From,leg.To}).DistinctBy(station=>station.MarketId).ToArray();
var scope=Create(new(stations,[]),catalogue,now,plan.Request.CommunityGoalCommodityFilter);
var deliveries=plan.Legs.Where(leg=>leg.Orders.Count>0).ToArray();
// Empty return legs are logistics. Every monetary delivery must serve
// a currently valid CG; a single accepted item cannot bless unrelated cargo.
return deliveries.Length>0&&deliveries.All(leg=>leg.Orders.All(order=>order.Quantity>0&&scope.accepted.TryGetValue(leg.To.MarketId,out var allowed)
&&allowed.Contains(CommodityCatalog.Key(order.Commodity))&&scope.Goals.Any(goal=>goal.MarketId==leg.To.MarketId
&&goal.SystemName.Equals(leg.To.SystemName,StringComparison.OrdinalIgnoreCase)&&goal.StationName.Equals(leg.To.Name,StringComparison.OrdinalIgnoreCase)
&&goal.AcceptedCommodities.Select(goods=>CommodityCatalog.Key(goods)).Contains(CommodityCatalog.Key(order.Commodity),StringComparer.Ordinal))));
}
}
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Planning;
/// <summary>Only original active trade initiatives, exact destination identities and confirmed goods admit a CG sale edge.</summary>
public sealed class CommunityGoalSupplyScope
{
private readonly Dictionary<long,HashSet<string>> accepted;
private CommunityGoalSupplyScope(IReadOnlyList<CommunityGoalInfo> goals,Dictionary<long,HashSet<string>> accepted,string? issue)
{Goals=goals;this.accepted=accepted;Issue=issue;}
public IReadOnlyList<CommunityGoalInfo> Goals{get;}
public string? Issue{get;}
public bool Available=>accepted.Values.Any(goods=>goods.Count>0);
public static CommunityGoalSupplyScope Create(MarketBook book,IReadOnlyList<CommunityGoalInfo>? catalogue,DateTimeOffset now,IReadOnlyCollection<string>? commodityFilter=null)
{
var confirmed=(catalogue??[]).GroupBy(goal=>goal.Id).Where(group=>group.Key is >0 and <=uint.MaxValue&&group.Count()==1).Select(group=>group.Single())
.Where(goal=>goal.Active&&goal.Expires>now&&goal.MarketId>0&&goal.GoalType=="trade"&&goal.Activity.StartsWith("trade",StringComparison.Ordinal)
&&!string.IsNullOrWhiteSpace(goal.SystemName)&&!string.IsNullOrWhiteSpace(goal.StationName)
&&goal.AcceptedCommodities.Count>0&&goal.AcceptedCommodities.All(goods=>!string.IsNullOrWhiteSpace(CommodityCatalog.Key(goods))))
.ToArray();
var byMarket=book.Stations.GroupBy(station=>station.MarketId).ToDictionary(group=>group.Key,group=>group.ToArray());
var matches=confirmed.Where(goal=>byMarket.TryGetValue(goal.MarketId,out var stations)&&stations.Length==1
&&stations[0].SystemName.Equals(goal.SystemName,StringComparison.OrdinalIgnoreCase)&&stations[0].Name.Equals(goal.StationName,StringComparison.OrdinalIgnoreCase)).ToArray();
var accepted=matches.GroupBy(goal=>goal.MarketId).ToDictionary(group=>group.Key,group=>group.SelectMany(goal=>goal.AcceptedCommodities)
.Select(goods=>CommodityCatalog.Key(goods)).ToHashSet(StringComparer.Ordinal));
var filter=TradeSearchConstraints.NormalizeCommodityFilter(commodityFilter).ToHashSet(StringComparer.Ordinal);
if(filter.Count>0)foreach(var goods in accepted.Values)goods.IntersectWith(filter);
var issue=matches.Length>0?accepted.Values.Any(goods=>goods.Count>0)?null:"商品筛选与有效 CG 的接收商品没有交集;请清空商品筛选或选择活动接收的商品。未回退普通跑商。":confirmed.Length>0
?"当前行情范围内没有身份匹配的 CG 交付站;请调整中心或半径并补全活动站行情。未回退普通跑商。"
:"未确认仍有效的贸易 CG、交付站与接收商品,暂不能推荐 CG 供货路线。未回退普通跑商。";
return new(matches,accepted,issue);
}
public bool AllowsSale(CommodityQuote quote)=>accepted.TryGetValue(quote.MarketId,out var goods)&&goods.Contains(CommodityCatalog.Key(quote.Commodity));
public static bool MatchesRoute(IReadOnlyList<CommunityGoalInfo> catalogue,RoutePlan plan,DateTimeOffset now)
{
var stations=plan.Legs.SelectMany(leg=>new[]{leg.From,leg.To}).DistinctBy(station=>station.MarketId).ToArray();
var scope=Create(new(stations,[]),catalogue,now,plan.Request.CommunityGoalCommodityFilter);
var deliveries=plan.Legs.Where(leg=>leg.Orders.Count>0).ToArray();
// Empty return legs are logistics. Every monetary delivery must serve
// a currently valid CG; a single accepted item cannot bless unrelated cargo.
return deliveries.Length>0&&deliveries.All(leg=>leg.Orders.All(order=>order.Quantity>0&&scope.accepted.TryGetValue(leg.To.MarketId,out var allowed)
&&allowed.Contains(CommodityCatalog.Key(order.Commodity))&&scope.Goals.Any(goal=>goal.MarketId==leg.To.MarketId
&&goal.SystemName.Equals(leg.To.SystemName,StringComparison.OrdinalIgnoreCase)&&goal.StationName.Equals(leg.To.Name,StringComparison.OrdinalIgnoreCase)
&&goal.AcceptedCommodities.Select(goods=>CommodityCatalog.Key(goods)).Contains(CommodityCatalog.Key(order.Commodity),StringComparer.Ordinal))));
}
}