using System;
using System.IO;
using System.ComponentModel;
using System.Windows;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace AvalonLife
{
[Serializable]
class LifeSim : INotifyPropertyChanged, ISerializable
{
#region LifeSim public interface
/// <summary>
/// LifeSim()
///
/// Constructs an instance of the sim controller and creates an empty model
/// for it to run. LifeSim is in the paused state after construction, but the
/// timer is running.
/// </summary>
/// <param name="lm"></param>
public LifeSim()
{
_lm = new LifeModel();
_timerInterval = ALSettings.Default.TimerInterval;
_timer = new System.Windows.Forms.Timer();
_timer.Interval = _timerInterval;
_timer.Tick += new EventHandler(TimerEvent);
_timer.Tag = this;
_timer.Start();
}
/// <summary>
/// LifeSim(int, int)
///
/// Constructs an instance of the sim controller and creates an empty model
/// for it to run, using the specified dimensions. LifeSim is in the paused
/// state after construction, but the timer is running.
/// </summary>
/// <param name="rows">Height of the requested model grid</param>
/// <param name="columns">Width of the requested model grid</param>
public LifeSim(int rows, int columns)
{
_lm = new LifeModel(rows, columns);
_timerInterval = ALSettings.Default.TimerInterval;
_timer = new System.Windows.Forms.Timer();
_timer.Interval = _timerInterval;
_timer.Tick += new EventHandler(TimerEvent);
_timer.Tag = this;
_timer.Start();
}
#region ISerializable methods
/// <summary>
/// LifeSim(SerializationInfo, StreamingContext)
///
/// Called by the BinaryFormatter to construct an instance of LifeSim from a
/// stream. Deserializes the private members, then the LifeModel deserialization
/// is called, and finally the timer is created and started. The sim is constructed
/// in a paused state. The UI is responsible for wiring up events.
/// </summary>
/// <param name="info"></param>
/// <param name="ctxt"></param>
public LifeSim( SerializationInfo info, StreamingContext ctxt )
{
_generation = (int)info.GetValue( "_generation", typeof(int) );
_timerInterval = (int)info.GetValue( "_timerInterval", typeof(int) );
_lm = new LifeModel( info, ctxt );
_isPaused = true;
_timer = new System.Windows.Forms.Timer();
_timer.Interval = _timerInterval;
_timer.Tick += new EventHandler(TimerEvent);
_timer.Tag = this;
_timer.Start();
}
/// <summary>
/// GetObjectData(SerializationInfo, StreamingContext)
///
/// Called by the BinaryFormatter to serialize an instance of LifeSim. Serializes
/// the private members and then calls the LifeModel serialization method directly.
/// </summary>
/// <param name="info"></param>
/// <param name="ctxt"></param>
public void GetObjectData( SerializationInfo info, StreamingContext ctxt )
{
info.AddValue( "_generation", _generation );
info.AddValue( "_timerInterval", _timerInterval );
_lm.GetObjectData( info, ctxt );
}
#endregion
/// <summary>
/// ~LifeSim()
///
/// I haven't verified it, but this is almost certainly unnecessary. The timer
/// class dispose() should kill the timer and clean up. Anyway no harm done.
/// </summary>
~LifeSim()
{
if ( _timer != null )
_timer.Stop();
}
/// <summary>
/// TimerEvent(Object, EventArgs)
///
/// This function handles the timer tick. If the game is in the unpaused state it calls
/// the LifeModel.Evaluate() method to iterate the model. It then increments the
/// generation count. It's a static method so we pass in the 'this' pointer for the
/// LifeSim instance that owns the timer in its Tag property. Useful little things, Tags.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void TimerEvent( Object sender, EventArgs e )
{
LifeSim ls = ((System.Windows.Forms.Timer)sender).Tag as LifeSim;
if ( ls != null && !ls.IsPaused )
{
if ( !ls._lm.EvolutionHalted || ALSettings.Default.HaltOnStability == false )
{
ls._lm.Evaluate();
ls.Generation++;
}
else
{
ls.IsPaused = true;
if (ls._uiCallback != null)
{
ls._uiCallback();
}
}
}
else if ( ls == null )
throw( new System.InvalidOperationException("TimerEvent") );
}
/// <summary>
/// ResetSim()
///
/// Called from the UI to reset the simulation to its starting condition. On
/// exit the simulation is paused.
/// </summary>
public void ResetSim()
{
if ( !_isPaused )
IsPaused = true;
Generation = 0;
_lm.ResetModel();
}
/// <summary>
/// NewModel()
///
/// Called from the UI during handling of a click on Game | New.
/// </summary>
public void NewModel()
{
if ( !_isPaused )
_isPaused = true;
_lm = new LifeModel();
Generation = 0;
}
/// <summary>
/// NewModel(int, int)
///
/// Called from the UI during handling of a click on Game | New. Creates a
/// new model using the passed in dimensions.
/// </summary>
public void NewModel(int rows, int columns)
{
if (!_isPaused)
_isPaused = true;
_lm = new LifeModel(rows, columns);
Generation = 0;
}
/// <summary>
/// NewModel(Stream)
///
/// Called to decode a stream of .cells format data into a LifeModel. The bulk
/// of the work is done in the LifeModel.LifeModel(Stream) constructor. If
/// construction fails we will already have warned the user in the constructor,
/// so all we do here is restore the paused state we had on entry.
/// </summary>
/// <param name="str">Stream containing the .cells formated data</param>
/// <returns></returns>
public bool NewModel( Stream str )
{
bool result = false;
bool paused = _isPaused;
_isPaused = true;
try
{
LifeModel lm = new LifeModel( str );
_lm = lm;
Generation = 0;
result = true;
}
catch( System.Exception )
{
_isPaused = paused;
}
return result;
}
#endregion
#region INotifyPropertyChanged members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region LifeSim public properties
/// <summary>
/// Counts the generations that the current model has run
/// </summary>
private int _generation = 0;
public int Generation
{
get
{
return _generation;
}
protected set
{
_generation = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Generation"));
}
}
/// <summary>
/// Holds a reference to the simulation model
/// </summary>
private LifeModel _lm = null;
public LifeModel Model
{
get
{
return _lm;
}
}
/// <summary>
/// Holds the timer interval, set to default on start
/// </summary>
private int _timerInterval = 0;
public int TimerInterval
{
get
{
return _timerInterval;
}
set
{
_timerInterval = value;
_timer.Interval = _timerInterval;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("TimerInterval"));
}
}
/// <summary>
/// Holds the simulation run state: false if running, true if paused.
/// </summary>
private bool _isPaused = true;
public bool IsPaused
{
get
{
return _isPaused;
}
set
{
_isPaused = value;
}
}
/// <summary>
/// The timer handler may detect that the model has ceased evolving,
/// i.e. entered a stable state. In that case it will make a call to
/// the function in this delegate to inform the UI. The UI is responsible
/// for setting a function-typed value to this delegate member if it
/// wants to receive this callback.
/// </summary>
public delegate void UISimStatusCallback();
private UISimStatusCallback _uiCallback = null;
public UISimStatusCallback UICallback
{
get
{
return _uiCallback;
}
set
{
_uiCallback = value;
}
}
#endregion
#region LifeSim private data
private System.Windows.Forms.Timer _timer = null;
#endregion
}
}