Code/Systems/VNScript/Environment.cs
using Sandbox.Diagnostics;
using System.Linq;
using System.Collections.Generic;

namespace VNScript;

/// <summary>
/// Interface for an environment that can store and retrieve variables.
/// </summary>
public interface IEnvironment
{
	public Value GetVariable( string name );
	
	public void SetVariable( string name, Value value );
	
	public IEnumerable<string> VariableSet();
	
	public Dictionary<string, Value> GetVariables();
}

/// <summary>
/// A map of environment variables.
/// </summary>
public class EnvironmentMap( Dictionary<string, Value> variables ) : IEnvironment
{
	private readonly Dictionary<string, Value> _variables = variables;
	protected static readonly Logger Log = new( "Environment" );
	
	public EnvironmentMap() : this( new Dictionary<string, Value>() ) { }
	
	public EnvironmentMap( IEnvironment copy ) : this()
	{
		foreach ( var key in copy.VariableSet() )
		{
			SetVariable( key, copy.GetVariable( key ) );
		}
	}
	
	public override string ToString()
	{
		return VariableSet().Select( k => '"' + k + '"' + " = " + GetVariable( k ) )
			.Aggregate( ( acc, v ) => acc + '\n' + v ).Trim();
	}
	
	public Value GetVariable( string name )
	{
		if ( GlobalEnvironment.Map.VariableSet().Contains( name ) )
		{
			return GlobalEnvironment.Map._variables[name];
		}
		
		if ( !_variables.TryGetValue( name, out var value ) )
		{
			throw new UndefinedVariableException( name );
		}
		
		return value;
	}
	
	public void SetVariable( string name, Value value )
	{
		if ( name.StartsWith( Value.GlobalPrefix ) )
		{
			GlobalEnvironment.Map._variables[name] = value;
			
			return;
		}
		
		_variables[name] = value;
	}
	
	public IEnumerable<string> VariableSet()
	{
		return _variables.Keys;
	}
	
	public Dictionary<string, Value> GetVariables()
	{
		return _variables;
	}
}

/// <summary>
/// The global environment.
/// </summary>
public static class GlobalEnvironment
{
	/// <summary>
	/// Provides access to the global environment map.
	/// </summary>
	public static EnvironmentMap Map { get; private set; } = new();
	
	/// <summary>
	/// Clears the global environment.
	/// </summary>
	public static void Clear() => Map = new EnvironmentMap();
}