EliteBGS/BGS/Report.cs

236 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using EDJournal;
namespace EliteBGS.BGS {
public class Report {
private List<Objective> objectives = new List<Objective>();
public delegate void OnLogDelegate(string log);
public event OnLogDelegate OnLog;
public List<Objective> Objectives {
get { return objectives; }
set { objectives = value; }
}
public bool AddObjective(Objective objective) {
var found = objectives.Find(x => x.CompareTo(objective) == 0);
bool added = false;
if (found == null) {
objectives.Add(objective);
added = true;
}
return added;
}
private bool IsRelevant(Entry e) {
return e.Is(Events.MissionCompleted) ||
e.Is(Events.MissionFailed) ||
e.Is(Events.MissionAccepted) ||
e.Is(Events.Docked) ||
e.Is(Events.FSDJump) ||
e.Is(Events.Location) ||
e.Is(Events.MultiSellExplorationData) ||
e.Is(Events.SellMicroResources) ||
e.Is(Events.RedeemVoucher) ||
e.Is(Events.FactionKillBond) ||
e.Is(Events.MarketBuy) ||
e.Is(Events.MarketSell)
;
}
public void Scan(PlayerJournal journal, DateTime start, DateTime end) {
var entries = from file in journal.Files
where file.NormalisedTimestamp >= start && file.NormalisedTimestamp <= end
select file.Entries
;
var relevant = from log in entries.SelectMany(array => array)
where IsRelevant(log)
select log
;
Dictionary<int, MissionAcceptedEntry> acceptedMissions = new Dictionary<int, MissionAcceptedEntry>();
Dictionary<string, int> buyCost = new Dictionary<string, int>();
string current_system = null;
string current_station = null;
string controlling_faction = null;
objectives.ForEach(x => x.Clear());
foreach (var e in relevant) {
List<LogEntry> results = new List<LogEntry>();
bool collate = false;
if (e.Is(Events.Docked)) {
DockedEntry docked = e as DockedEntry;
/* gleem the current station from this message
*/
current_station = docked.StationName;
current_system = docked.StarSystem;
controlling_faction = docked.StationFaction;
} else if (e.Is(Events.FSDJump)) {
/* Gleem current system and controlling faction from this message.
*/
current_system = (e as FSDJumpEntry).StarSystem;
controlling_faction = (e as FSDJumpEntry).SystemFaction;
} else if (e.Is(Events.Location)) {
/* Get current system, faction name and station from Location message
*/
LocationEntry location = e as LocationEntry;
current_system = location.StarSystem;
if (!string.IsNullOrEmpty(location.SystemFaction)) {
controlling_faction = location.SystemFaction;
}
if (!string.IsNullOrEmpty(location.StationName)) {
current_station = location.StationName;
}
} else if (e.Is(Events.MissionCompleted)) {
var completed = e as MissionCompletedEntry;
results.Add(new MissionCompleted(completed) {
System = current_system,
Station = current_station
});
if (completed.HumanReadableNameWasGenerated) {
/* If the human readable name was generated, we send a log message. Because the
* generated names all sort of suck, we should have more human readable names in
* in the lookup dictionary.
*/
OnLog?.Invoke("Human readable name for mission \"" +
completed.Name +
"\" was generated, please report this.");
}
} else if (e.Is(Events.MissionAccepted)) {
MissionAcceptedEntry accepted = e as MissionAcceptedEntry;
acceptedMissions[accepted.MissionID] = accepted;
} else if (e.Is(Events.MissionFailed)) {
var failed = e as MissionFailedEntry;
MissionAcceptedEntry accepted = null;
if (!acceptedMissions.TryGetValue(failed.MissionID, out accepted)) {
OnLog?.Invoke("A mission failed which wasn't accepted in the given time frame. " +
"Please adjust start date to when the mission was accepted to include it in the list.");
continue;
}
results.Add(new MissionFailed(accepted) { Failed = failed, System = current_system });
if (failed.HumanReadableName == null) {
OnLog?.Invoke("Human readable name for mission \"" +
failed.Name +
"\" was not recognised");
}
/* Mission failed should be collated if they are in the same system/station
*/
collate = true;
} else if (e.Is(Events.MultiSellExplorationData)) {
/* For multi-sell-exploraton-data only the controlling faction of the station sold to matters.
*/
results.Add(new Cartographics(e as MultiSellExplorationDataEntry) {
System = current_system,
Station = current_station,
Faction = controlling_faction
});
} else if (e.Is(Events.RedeemVoucher)) {
/* Same for selling combat vouchers. Only the current controlling faction matters here.
*/
results.Add(new Vouchers(e as RedeemVoucherEntry) {
System = current_system,
Station = current_station,
Faction = (e as RedeemVoucherEntry).Factions.FirstOrDefault() ?? "",
ControllingFaction = controlling_faction,
});
collate = true;
} else if (e.Is(Events.SellMicroResources)) {
results.Add(new SellMicroResources(e as SellMicroResourcesEntry) {
Faction = controlling_faction,
Station = current_station,
System = current_system
});
} else if (e.Is(Events.MarketBuy)) {
MarketBuyEntry buy = e as MarketBuyEntry;
if (string.IsNullOrEmpty(buy.Type) || buy.BuyPrice == 0) {
continue;
}
buyCost[buy.Type] = buy.BuyPrice;
} else if (e.Is(Events.MarketSell)) {
MarketSellEntry sell = e as MarketSellEntry;
int profit = 0;
if (!buyCost.ContainsKey(sell.Type)) {
OnLog?.Invoke("Could not find buy order for the given commodity. Please adjust profit manually.");
} else {
int avg = buyCost[sell.Type];
profit = sell.TotalSale - (avg * sell.Count);
}
results.Add(new SellCargo(e as MarketSellEntry) {
Faction = controlling_faction,
Station = current_station,
System = current_system,
Profit = profit
});
}
if (results == null || results.Count <= 0) {
continue;
}
foreach (LogEntry entry in results) {
/* Find all objectives that generally match.
*/
var matches = objectives
.Where(x => x.Matches(entry) > 0)
.OrderBy(x => x.Matches(entry))
;
Objective objective = null;
if (matches != null && matches.Count() > 0) {
/* Then select the one that matches the most.
*/
objective = matches
.OrderBy(x => x.Matches(entry))
.Reverse()
.First()
;
} else {
/* create a new objective if we don't have one */
objective = new Objective() {
Station = entry.Station,
Faction = entry.Faction,
System = entry.System,
};
objectives.Add(objective);
}
LogEntry existing = null;
existing = objective.LogEntries.Find(x => {
try {
return x.CompareTo(entry) == 0;
} catch (NotImplementedException) {
return false;
}
});
if (collate && existing != null) {
existing.Entries.Add(e);
} else if (!collate || existing == null) {
objective.LogEntries.Add(entry);
}
}
}
}
public void Scan(PlayerJournal journal) {
Scan(journal, DateTime.Now, DateTime.Now);
}
}
}