using Content.Shared.GameObjects.EntitySystems;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using System;
using System.Collections.Generic;
namespace Content.Server.GameObjects.Components.Power
{
///
/// Master class for group of powertransfercomponents, takes in and distributes power via nodes
///
public class Powernet
{
public Powernet()
{
var EntitySystemManager = IoCManager.Resolve();
var powerSystem = EntitySystemManager.GetEntitySystem();
powerSystem.Powernets.Add(this);
Uid = powerSystem.NewUid();
}
///
/// Unique identifier per powernet, used for debugging mostly.
///
public int Uid { get; }
///
/// The entities that make up the powernet's physical location and allow powernet connection
///
public readonly List WireList = new List();
///
/// Entities that connect directly to the powernet through above to add power or add power load
///
public readonly List NodeList = new List();
///
/// Subset of nodelist that adds a continuous power supply to the network
///
private readonly Dictionary GeneratorList = new Dictionary();
///
/// Subset of nodelist that draw power, stores information on current continuous powernet load
///
private readonly SortedSet DeviceLoadList = new SortedSet(new DevicePriorityCompare());
///
/// All the devices that have been depowered by this powernet or depowered prior to being absorted into this powernet
///
private readonly List DepoweredDevices = new List();
///
/// A list of the energy storage components that will feed the powernet if necessary, and if there is enough power feed itself
///
private readonly List PowerStorageSupplierList = new List();
///
/// A list of energy storage components that will never feed the powernet, will try to draw energy to feed themselves if possible
///
private readonly List PowerStorageConsumerList = new List();
///
/// Static counter of all continuous load placed from devices on this power network.
/// In Watts.
///
public float Load { get; private set; } = 0;
///
/// Static counter of all continiuous supply from generators on this power network.
/// In Watts.
///
public float Supply { get; private set; } = 0;
///
/// Variable that causes powernet to be regenerated from its wires during the next update cycle.
///
public bool Dirty { get; set; } = false;
public void Update(float frameTime)
{
float activesupply = Supply * frameTime;
float activeload = Load * frameTime;
float storageconsumerdemand = 0;
foreach (var supply in PowerStorageConsumerList)
{
storageconsumerdemand += supply.RequestCharge(frameTime);
}
float storagesupply = 0;
float storagesupplierdemand = 0;
foreach (var supply in PowerStorageSupplierList)
{
storagesupply += supply.AvailableCharge(frameTime);
storagesupplierdemand += supply.RequestCharge(frameTime);
}
//If we have enough power to feed all load and storage demand, then feed everything
if (activesupply > activeload + storageconsumerdemand + storagesupplierdemand)
{
PowerAllDevices();
ChargeStorageConsumers(frameTime);
ChargeStorageSuppliers(frameTime);
return;
}
//We don't have enough power for the storage powernet suppliers, ignore powering them
else if (activesupply > activeload + storageconsumerdemand)
{
PowerAllDevices();
ChargeStorageConsumers(frameTime);
return;
}
float totalRemaining = activesupply + storagesupply;
foreach (var device in DeviceLoadList)
{
var deviceLoad = device.Load * frameTime;
if (deviceLoad > totalRemaining)
{
device.ExternalPowered = false;
DepoweredDevices.Add(device);
}
else
{
totalRemaining -= deviceLoad;
if (!device.ExternalPowered)
{
DepoweredDevices.Remove(device);
device.ExternalPowered = true;
}
}
}
// What we have left goes into storage consumers.
foreach (var consumer in PowerStorageConsumerList)
{
if (totalRemaining < 0)
{
break;
}
var demand = consumer.RequestCharge(frameTime);
var taken = Math.Min(demand, totalRemaining);
totalRemaining -= taken;
consumer.AddCharge(taken);
}
var supplierUsed = storagesupply - totalRemaining;
foreach (var supplier in PowerStorageSupplierList)
{
var load = supplier.AvailableCharge(frameTime);
var added = Math.Min(load, supplierUsed);
supplierUsed -= added;
supplier.DeductCharge(added);
if (supplierUsed <= 0)
{
return;
}
}
}
private void PowerAllDevices()
{
foreach (var device in DepoweredDevices)
{
device.ExternalPowered = true;
}
DepoweredDevices.Clear();
}
private void ChargeStorageConsumers(float frametime)
{
foreach (var storage in PowerStorageConsumerList)
{
storage.ChargePowerTick(frametime);
}
}
private void ChargeStorageSuppliers(float frametime)
{
foreach (var storage in PowerStorageSupplierList)
{
storage.ChargePowerTick(frametime);
}
}
///
/// Kills a powernet after it is marked dirty and its component have already been regenerated by the powernet system
///
public void DirtyKill()
{
WireList.Clear();
while (NodeList.Count != 0)
{
NodeList[0].DisconnectFromPowernet();
}
GeneratorList.Clear();
DeviceLoadList.Clear();
DepoweredDevices.Clear();
PowerStorageSupplierList.Clear();
PowerStorageConsumerList.Clear();
RemoveFromSystem();
}
///
/// Combines two powernets when they connect via powertransfer components
///
public void MergePowernets(Powernet toMerge)
{
//TODO: load balance reconciliation between powernets on merge tick here
foreach (var wire in toMerge.WireList)
{
wire.Parent = this;
}
WireList.AddRange(toMerge.WireList);
toMerge.WireList.Clear();
foreach (var node in toMerge.NodeList)
{
node.Parent = this;
}
NodeList.AddRange(toMerge.NodeList);
toMerge.NodeList.Clear();
foreach (var generator in toMerge.GeneratorList)
{
GeneratorList.Add(generator.Key, generator.Value);
}
Supply += toMerge.Supply;
toMerge.Supply = 0;
toMerge.GeneratorList.Clear();
foreach (var device in toMerge.DeviceLoadList)
{
DeviceLoadList.Add(device);
}
Load += toMerge.Load;
toMerge.Load = 0;
toMerge.DeviceLoadList.Clear();
DepoweredDevices.AddRange(toMerge.DepoweredDevices);
toMerge.DepoweredDevices.Clear();
PowerStorageSupplierList.AddRange(toMerge.PowerStorageSupplierList);
toMerge.PowerStorageSupplierList.Clear();
PowerStorageConsumerList.AddRange(toMerge.PowerStorageConsumerList);
toMerge.PowerStorageConsumerList.Clear();
toMerge.RemoveFromSystem();
}
///
/// Removes reference from the powernets list on the powernet system
///
private void RemoveFromSystem()
{
var EntitySystemManager = IoCManager.Resolve();
EntitySystemManager.GetEntitySystem().Powernets.Remove(this);
}
#region Registration
///
/// Register a continuous load from a device connected to the powernet
///
public void AddDevice(PowerDeviceComponent device)
{
DeviceLoadList.Add(device);
Load += device.Load;
if (!device.Powered)
DepoweredDevices.Add(device);
}
///
/// Update one of the loads from a deviceconnected to the powernet
///
public void UpdateDevice(PowerDeviceComponent device, float oldLoad)
{
if (DeviceLoadList.Contains(device))
{
Load -= oldLoad;
Load += device.Load;
}
}
///
/// Returns whether or not a power device is in this powernet's load list.
///
/// The device to check for.
/// True if the device is in the load list, false otherwise.
public bool HasDevice(PowerDeviceComponent device)
{
return DeviceLoadList.Contains(device);
}
///
/// Remove a continuous load from a device connected to the powernet
///
public void RemoveDevice(PowerDeviceComponent device)
{
if (DeviceLoadList.Contains(device))
{
Load -= device.Load;
DeviceLoadList.Remove(device);
if (DepoweredDevices.Contains(device))
DepoweredDevices.Remove(device);
}
else
{
Logger.WarningS("power", "We tried to remove device {0} twice from {1}, somehow.", device.Owner, this);
}
}
///
/// Register a power supply from a generator connected to the powernet
///
public void AddGenerator(PowerGeneratorComponent generator)
{
GeneratorList.Add(generator, generator.Supply);
Supply += generator.Supply;
}
///
/// Update the value supplied from a generator connected to the powernet
///
public void UpdateGenerator(PowerGeneratorComponent generator)
{
if (GeneratorList.ContainsKey(generator))
{
Supply -= GeneratorList[generator];
GeneratorList[generator] = generator.Supply;
Supply += generator.Supply;
}
}
///
/// Remove a power supply from a generator connected to the powernet
///
public void RemoveGenerator(PowerGeneratorComponent generator)
{
if (GeneratorList.ContainsKey(generator))
{
Supply -= GeneratorList[generator];
GeneratorList.Remove(generator);
}
else
{
Logger.WarningS("power", "We tried to remove generator {0} twice from {1}, somehow.", generator.Owner, this);
}
}
///
/// Register a power supply from a generator connected to the powernet
///
public void AddPowerStorage(PowerStorageComponent storage)
{
if (storage.ChargePowernet)
PowerStorageSupplierList.Add(storage);
else
PowerStorageConsumerList.Add(storage);
}
//How do I even call this? TODO: fix
public void UpdateStorageType(PowerStorageComponent storage)
{
//If our chargepowernet settings change we need to tell the powernet of this new setting and remove traces of our old setting
if (PowerStorageSupplierList.Contains(storage))
PowerStorageSupplierList.Remove(storage);
if (PowerStorageConsumerList.Contains(storage))
PowerStorageConsumerList.Remove(storage);
//Apply new setting
if (storage.ChargePowernet)
PowerStorageSupplierList.Add(storage);
else
PowerStorageConsumerList.Add(storage);
}
///
/// Remove a power supply from a generator connected to the powernet
///
public void RemovePowerStorage(PowerStorageComponent storage)
{
if (PowerStorageSupplierList.Contains(storage))
{
PowerStorageSupplierList.Remove(storage);
}
if (PowerStorageConsumerList.Contains(storage))
{
PowerStorageSupplierList.Remove(storage);
}
}
#endregion Registration
public override string ToString()
{
return $"Powernet {Uid}";
}
///
/// Priority that a device will receive power if powernet cannot supply every device
///
public enum Priority
{
Necessary = 0,
High = 1,
Medium = 2,
Low = 3,
Provider = 4,
Unnecessary = 5
}
///
/// Comparer that keeps the device dictionary sorted by powernet priority
///
public class DevicePriorityCompare : IComparer
{
public int Compare(PowerDeviceComponent x, PowerDeviceComponent y)
{
int compare = y.Priority.CompareTo(x.Priority);
//If the comparer returns 0 sortedset will believe it is a duplicate and return 0, so return 1 instead
if (compare == 0)
{
return y.Owner.Uid.CompareTo(x.Owner.Uid);
}
return compare;
}
}
}
}