using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Diagnostics;
namespace CarFire
{
///
/// The Script class handles the parsing and execution of lists
/// of functions. Scripts are closely related to triggers.
///
public class Script
{
#region Public Properties
///
/// Determine if the script is in the process of being run.
///
public bool IsRunning { get { return mIsRunning; } }
#endregion
#region Public Methods
///
/// Construct a script object with code and a game reference.
///
/// The script code.
/// A game reference.
public Script(string code, object bindings)
{
mBindings = bindings;
string[] functions = Parse.List(code);
if (functions != null)
{
foreach (string function in functions)
{
string[] parts = Parse.Function(function);
if (parts != null)
{
string[] args = Parse.List(parts[1]);
if (args != null)
{
Function func = new Function(parts[0], args);
mFunctions.Add(func);
}
else throw new Exception("Arguments could not be parsed: " + parts[1]);
}
else throw new Exception("Function could not be parsed: " + function);
}
}
else throw new Exception("Script could not be parsed: " + code);
}
///
/// Start execution of the script. If there is no need to break
/// execution before the script ends, it will finish before this method
/// call ends. Otherwise, execution will be delayed and will finish sometime
/// in the future. This will execute each function in sequence as long
/// as each function evaluates to true. If a function does not evaluate to true,
/// this method will return and execution will be delayed. In either case,
/// the evaluation of the last function is returned by this method.
///
/// The player associated with this script.
/// Evaluation of the last function call.
public bool Run(Player player)
{
bool result = false;
if (!mIsRunning)
{
mIsRunning = true;
mRunningIndex = 0;
}
for (; mRunningIndex < mFunctions.Count; mRunningIndex++)
{
result = Call(mRunningIndex, player);
if (!result) break;
}
if (mRunningIndex >= mFunctions.Count - 1) mIsRunning = false;
return result;
}
public void Reset()
{
mIsRunning = false;
}
#endregion
#region Private Methods
///
/// Call a function in the last at a certain index.
///
/// The function index.
/// The associated player object.
/// The evaluation of the function.
bool Call(int index, Player player)
{
Debug.Assert(0 <= index && index < mFunctions.Count);
try
{
object[] args = new object[2];
args[0] = player;
args[1] = mFunctions[index].Arguments;
return (bool)mBindings.GetType().InvokeMember(mFunctions[index].Name, BindingFlags.InvokeMethod, null, mBindings, args);
}
#pragma warning disable 0168
catch (System.MissingMethodException ex)
#pragma warning restore 0168
{
throw new Exception("Function could not be found: " + mFunctions[index].Name);
}
}
#endregion
#region Private Types
class Function
{
public string Name { get { return mName; } }
public string[] Arguments { get { return mArgs; } }
public Function(string name, string[] args)
{
mName = name;
mArgs = args;
}
string mName;
string[] mArgs;
}
#endregion
#region Private Variables
object mBindings;
List mFunctions = new List();
bool mIsRunning;
int mRunningIndex;
#endregion
}
}