using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Threading;
using GameServiceWarden.ModuleAPI;
namespace GameServiceWarden.Core.Games
{
public class GameServiceInfo : IDisposable //entity
{
///
/// The name of the service itself, independent of the name of the module this service is using.
///
public string ServiceName { get { return serviceName; } set { Interlocked.Exchange(ref serviceName, value); } }
///
/// The services console output stream.
///
private volatile string serviceName; //thread-safe(?)
public Stream ServiceConsoleStream { get; private set; } // Thread safe.
private readonly object controlLock = new object();
private volatile ServiceState state;
private readonly IGameService service;
private readonly string assemblyName;
///
/// Name of module this service uses.
///
public string ModuleName { get; private set; }
private readonly IReadOnlyDictionary configurables;
private bool disposed;
public GameServiceInfo(IGameService service, string moduleName, string assemblyName)
{
this.service = service ?? throw new ArgumentNullException("service");
this.ModuleName = moduleName ?? throw new ArgumentNullException("moduleName");
this.assemblyName = assemblyName ?? throw new ArgumentNullException("assemblyName");
this.service.StateChangeEvent += OnServiceStateChange;
Dictionary tempConfigurables = new Dictionary();
foreach (IGameConfigurable configurable in service.Configurables)
{
tempConfigurables.Add(configurable.OptionName, configurable);
}
this.configurables = new ReadOnlyDictionary(tempConfigurables);
}
///
/// Starts this service.
///
/// Is thrown when the service is already running.
public void Start()
{
lock (controlLock)
{
if (state != ServiceState.Stopped) throw new InvalidOperationException("Service instance already running.");
DateTimeFormatInfo format = new DateTimeFormatInfo();
format.TimeSeparator = "-";
format.DateSeparator = "_";
ServiceConsoleStream = new MemoryStream(8 * 1024 * 1024); // 8 MB
ServiceConsoleStream = Stream.Synchronized(ServiceConsoleStream);
service.InitializeService(new StreamWriter(ServiceConsoleStream));
}
}
///
/// Stops the service.
///
/// Is thrown when the is not running.
public void Stop()
{
lock (controlLock)
{
if (state != ServiceState.Running) throw new InvalidOperationException("Service instance not running.");
this.service.ElegantShutdown();
ServiceConsoleStream.Close();
ServiceConsoleStream = null;
}
}
///
/// Sends a command to this service to execute.
///
/// The command to execute.
/// Is thrown when the service is not running.
public void ExecuteCommand(string command)
{
lock (controlLock)
{
if (state != ServiceState.Running) throw new InvalidOperationException("Service instance not running.");
service.ExecuteCommand(command);
}
}
///
/// Gets the possible 's names for this service.
///
/// A returned where the string is the option's name.
public ISet GetConfigurableOptions()
{
return new HashSet(this.configurables.Keys);
}
public bool SetConfigurableValue(string configurationName, string value)
{
if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\".");
return this.configurables[configurationName].SetValue(value);
}
public string GetConfigurableValue(string configurationName)
{
if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\".");
return this.configurables[configurationName].GetValue();
}
/// The that this service is currently in.
public ServiceState GetServiceState()
{
lock (controlLock)
{
return state;
}
}
/// The name of assembly this module is contained in.
public string GetAssemblyName()
{
return assemblyName;
}
private void OnServiceStateChange(object sender, ServiceState current)
{
lock (controlLock)
{
this.state = current;
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
ServiceConsoleStream?.Dispose();
}
//No unmanaged code, therefore, no finalizer.
disposed = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}