using Cryville.Common;
using Cryville.Common.Pdt;
using Cryville.Crtr.Ruleset;
using System;
using System.Collections.Generic;

namespace Cryville.Crtr.Config {
	internal class RulesetConfigPropertyMasterAdapter : IPropertyMasterAdapter {
		readonly List<RulesetConfigPropertyAdapter> _props = new List<RulesetConfigPropertyAdapter>();
		readonly RulesetConfigStore _store;

		public RulesetConfigPropertyMasterAdapter(Dictionary<Identifier, ConfigDefinition> defs, Dictionary<string, object> values) {
			_store = new RulesetConfigStore(defs, values);
			PdtEvaluator.Instance.ContextRulesetConfig = _store;
			if (defs == null) return;
			foreach (var def in defs) {
				_props.Add(new RulesetConfigPropertyAdapter(def.Key, def.Value, this));
			}
		}

		public string DefaultCategory { get { return "miscellaneous"; } }

		public IEnumerable<IPropertyAdapter> GetProperties() { return _props; }

		public object GetValue(string key) {
			return _store[key];
		}

		public void SetValue(string key, object value) {
			_store[key] = value;
			foreach (var prop in _props) {
				prop.OnValueChanged();
			}
		}
	}

	internal class RulesetConfigPropertyAdapter : IPropertyAdapter {
		readonly RulesetConfigPropertyMasterAdapter _master;
		readonly ConfigDefinition _def;

		public RulesetConfigPropertyAdapter(Identifier key, ConfigDefinition def, RulesetConfigPropertyMasterAdapter master) {
			_master = master;
			_def = def;
			Name = (string)key.Name;
			switch (_def.type) {
				case ConfigType.number: Type = PropertyType.Number; break;
				case ConfigType.number_stepped: Type = PropertyType.NumberStepped; break;
				default: Type = PropertyType.Unknown; break;
			}
			_rangeOp = new PropOp.Clip(v => {
				m_range[0] = (double)v.Behind;
				m_range[1] = (double)v.Ahead;
			});
		}

		public string Category { get { return _def.category; } }

		public string Name { get; private set; }

		public PropertyType Type { get; private set; }

		readonly PdtOperator _rangeOp;
		readonly object[] m_range = new object[] { double.NegativeInfinity, double.PositiveInfinity };
		public object[] Range {
			get {
				if (_def.range != null)
					PdtEvaluator.Instance.Evaluate(_rangeOp, _def.range);
				return m_range;
			}
		}

		public object GetValue() {
			return _master.GetValue(Name);
		}

		public void SetValue(object value) {
			_master.SetValue(Name, value);
		}

		public event Action ValueChanged;
		public void OnValueChanged() {
			var ev = ValueChanged;
			if (ev != null) ev();
		}

		public bool SetMapped { get { return false; } }

		readonly PropStores.Float _numst = new PropStores.Float();
		public object MapValue(object value) {
			_numst.Value = (float)(double)value;
			if (_def.value == null) return _numst.Value;
			PdtEvaluator.Instance.ContextSelfValue = _numst.Source;
			PdtEvaluator.Instance.Evaluate(_numst.Target, _def.value);
			PdtEvaluator.Instance.ContextSelfValue = null;
			return _numst.Value;
		}

		public object MapValueInverse(object value) { throw new NotSupportedException(); }
	}
}