using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using RemoteTech.Modules;
namespace RemoteTech
{
///
/// Class keeping track of RemoteTech satellites.
/// Acts as a list of vessels managed by RemoteTech.
///
public class SatelliteManager : IEnumerable, IDisposable
{
public event Action OnRegister = delegate { };
public event Action OnUnregister = delegate { };
public int Count => _satelliteCache.Count;
public VesselSatellite this[Guid g] => GetSatelliteById(g);
public VesselSatellite this[Vessel v] => v == null ? null : GetSatelliteById(v.id);
private readonly Dictionary> _loadedSpuCache =
new Dictionary>();
private readonly Dictionary _satelliteCache =
new Dictionary();
public SatelliteManager()
{
GameEvents.onVesselCreate.Add(OnVesselCreate);
GameEvents.onVesselDestroy.Add(OnVesselDestroy);
GameEvents.onVesselGoOnRails.Add(OnVesselOnRails);
OnRegister += vs => RTLog.Notify("SatelliteManager: OnRegister({0})", vs);
OnUnregister += vs => RTLog.Notify("SatelliteManager: OnUnregister({0})", vs);
}
///
/// Registers a signal processor for the vessel.
///
/// The vessel.
/// The signal processor.
/// Guid key under which the signal processor was registered.
public Guid Register(Vessel vessel, ISignalProcessor spu)
{
RTLog.Notify("SatelliteManager: Register({0})", spu);
var key = vessel.id;
if (!_loadedSpuCache.ContainsKey(key))
{
UnregisterProto(vessel.id);
_loadedSpuCache[key] = new List();
}
// Add if non duplicate
var signalProcessor = _loadedSpuCache[key].Find(x => x == spu);
if (signalProcessor != null)
return key;
_loadedSpuCache[key].Add(spu);
// Create a new satellite if it's the only loaded signal processor.
if (_loadedSpuCache[key].Count != 1)
return key;
_satelliteCache[key] = new VesselSatellite(_loadedSpuCache[key]);
OnRegister(_satelliteCache[key]);
return key;
}
///
/// Unregisters the specified signal processor.
///
/// The key the signal processor was registered under.
/// The signal processor.
public void Unregister(Guid key, ISignalProcessor spu)
{
RTLog.Notify("SatelliteManager: Unregister({0})", spu);
// Return if nothing to unregister.
if (!_loadedSpuCache.ContainsKey(key)) return;
// Find instance of the signal processor.
var instanceId = _loadedSpuCache[key].FindIndex(x => x == spu);
if (instanceId == -1)
return;
// Remove satellite if no signal processors remain.
if (_loadedSpuCache[key].Count == 1)
{
if (_satelliteCache.ContainsKey(key))
{
VesselSatellite sat = _satelliteCache[key];
OnUnregister(sat);
_satelliteCache.Remove(key);
}
_loadedSpuCache[key].RemoveAt(instanceId);
_loadedSpuCache.Remove(key);
// search vessel by id
var vessel = RTUtil.GetVesselById(key);
if (vessel != null)
{
// trigger the onRails on more time
// to re-register the satellite as a protoSat
OnVesselOnRails(vessel);
}
}
else
{
_loadedSpuCache[key].RemoveAt(instanceId);
}
}
///
/// Registers a protosatellite compiled from the unloaded vessel data.
///
/// The vessel.
public void RegisterProto(Vessel vessel)
{
Guid key = vessel.protoVessel.vesselID;
RTLog.Notify("SatelliteManager: RegisterProto({0}, {1})", vessel.vesselName, key);
// Return if there are still signal processors loaded.
if (_loadedSpuCache.ContainsKey(vessel.id)) {
_loadedSpuCache.Remove(vessel.id);
}
var spu = vessel.GetSignalProcessor();
if (spu == null)
return;
var protos = new List {spu};
_satelliteCache[key] = new VesselSatellite(protos);
OnRegister(_satelliteCache[key]);
}
///
/// Unregisters the protosatellite which was compiled from the unloaded vessel data.
///
public void UnregisterProto(Guid key)
{
RTLog.Notify("SatelliteManager: UnregisterProto({0})", key);
// Return if there are still signal processors loaded.
if (_loadedSpuCache.ContainsKey(key))
return;
// Unregister satellite if it exists.
if (!_satelliteCache.ContainsKey(key))
return;
OnUnregister(_satelliteCache[key]);
_satelliteCache.Remove(key);
}
private VesselSatellite GetSatelliteById(Guid key)
{
VesselSatellite result;
return _satelliteCache.TryGetValue(key, out result) ? result : null;
}
public IEnumerable FindCommandStations()
{
return _satelliteCache.Values.Where(vs => vs.IsCommandStation).Cast();
}
private void OnVesselOnRails(Vessel v)
{
if (v.parts.Count == 0)
{
RegisterProto(v);
}
}
private void OnVesselCreate(Vessel v)
{
RTLog.Notify("SatelliteManager: OnVesselCreate({0}, {1})", v.id, v.vesselName);
}
private void OnVesselDestroy(Vessel v)
{
RTLog.Notify("SatelliteManager: OnVesselDestroy({0}, {1})", v.id, v.vesselName);
UnregisterProto(v.id);
}
public void Dispose()
{
GameEvents.onVesselCreate.Remove(OnVesselCreate);
GameEvents.onVesselDestroy.Remove(OnVesselDestroy);
GameEvents.onVesselGoOnRails.Remove(OnVesselOnRails);
}
public IEnumerator GetEnumerator()
{
return _satelliteCache.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public static partial class RTUtil
{
public static bool IsSignalProcessor(this ProtoPartModuleSnapshot ppms)
{
return ppms.GetBool("IsRTSignalProcessor");
}
public static bool IsSignalProcessor(this PartModule pm)
{
return pm.Fields.GetValue("IsRTSignalProcessor");
}
public static ISignalProcessor GetSignalProcessor(this Vessel v)
{
RTLog.Notify("GetSignalProcessor({0}): Check", v.vesselName);
ISignalProcessor result = null;
if (v.loaded && v.parts.Count > 0)
{
var partModuleList = v.Parts.SelectMany(p => p.Modules.Cast()).Where(pm => pm.IsSignalProcessor()).ToList();
// try to look for a moduleSPU
result = partModuleList.FirstOrDefault(pm => pm.moduleName == "ModuleSPU") as ISignalProcessor ??
partModuleList.FirstOrDefault() as ISignalProcessor;
}
else
{
var protoPartList = v.protoVessel.protoPartSnapshots.SelectMany(x => x.modules).Where(ppms => ppms.IsSignalProcessor()).ToList();
// try to look for a moduleSPU on a unloaded vessel
var protoPartProcessor = protoPartList.FirstOrDefault(ppms => ppms.moduleName == "ModuleSPU") ??
protoPartList.FirstOrDefault();
// convert the found protoPartSnapshots to a ProtoSignalProcessor
if (protoPartProcessor != null)
{
result = new ProtoSignalProcessor(protoPartProcessor, v);
}
}
return result;
}
public static bool IsCommandStation(this ProtoPartModuleSnapshot ppms)
{
return ppms.GetBool("IsRTCommandStation");
}
public static bool IsCommandStation(this PartModule pm)
{
return pm.Fields.GetValue("IsRTCommandStation");
}
public static bool HasCommandStation(this Vessel v)
{
RTLog.Notify("HasCommandStation({0})", v.vesselName);
if (v.loaded && v.parts.Count > 0)
{
return v.Parts.SelectMany(p => p.Modules.Cast()).Any(pm => pm.IsCommandStation());
}
return v.protoVessel.protoPartSnapshots.SelectMany(x => x.modules).Any(pm => pm.IsCommandStation());
}
}
}