using System;
using System.Collections.Generic;
using System.Text;
using nft.framework.drawing;
using nft.framework;
using nft.framework.plugin;
using nft.core.schedule;
using System.Diagnostics;

namespace nft.core.view
{
    public delegate T ResourceParser<T>(ParamsReader reader);

    public class ConditionedResourceFactory
    {
        static private readonly string VALUE_NODE = "default|value";
        static private readonly string CONDITION_NODE = "condition";
        static private readonly string CONDITION_DESC_ATTR = "when";

        static private List<IConditionFactory> factories = new List<IConditionFactory>();

        public static void RegisterConditionFactory(IConditionFactory fct){
            factories.Add(fct);
        }

        private static string debug_indent = "";
        public static ConditionedResource<T> LoadAsConditiondResources<T>(ParamsReader resource, ResourceParser<T> callback){
            ParamsReader dnode = resource[VALUE_NODE];
            T _default = callback(dnode.IsNull ? resource : dnode);
            ParamsReader conds = resource[CONDITION_NODE];
#if DEBUG
            //debug_indent = "";
            //Debug.WriteLine("default value = " + _default);
#endif
            if (conds.IsNull) {
                ConditionedResource<T> ret = new ConditionedResource<T>(_default);
                //Debug.WriteLine("+condition:" + ret);
                return ret;
            } else {
                NestedConditionsResouceRoot<T> ret = new NestedConditionsResouceRoot<T>(_default);
                //Debug.WriteLine("+condition:"+ret);
                foreach (ParamsReader child in conds.Each) {
                    ConditionedResource<T> temp = LoadChildConditions(_default, child, callback);
                    ret.AddChild(temp);
                }
                return ApplyOptimization<T>(ret);
            }
        }

        #region private functions
        private static ConditionedResource<T> LoadChildConditions<T>(T val_defult, ParamsReader resource, ResourceParser<T> callback){
            ISceneCondition sc = GetConditionFor(resource);
#if DEBUG
            //string indent_saved = debug_indent;
            //debug_indent += "  ";
#endif
            ParamsReader conds = resource[CONDITION_NODE];
            if (conds.IsNull) {
                ParamsReader dnode = resource[VALUE_NODE];
                T _default = callback(dnode.IsNull ? resource : dnode);
                //Debug.WriteLine(debug_indent + "value=" + _default);
                SingleConditionResouce<T> ret = new SingleConditionResouce<T>(sc, _default);
                //Debug.WriteLine(debug_indent + "+condition:" + ret);
                return ret;
            } else {
                ParamsReader dnode = resource[VALUE_NODE];
                T _default = dnode.IsNull ? val_defult : callback(dnode);
                //Debug.WriteLine(debug_indent + "value=" + _default);
                NestedConditionsResouce<T> ret = new NestedConditionsResouce<T>(sc, _default);
                //Debug.WriteLine(debug_indent + "+condition:" + ret);
                foreach (ParamsReader child in conds.Each) {
                    ConditionedResource<T> temp = LoadChildConditions(_default, child, callback);
                    ret.AddChild(temp);
                }
#if DEBUG
                //debug_indent = indent_saved;
#endif
                return ApplyOptimization<T>(ret);
            }
        }

        private static ConditionedResource<T> ApplyOptimization<T>(NestedConditionsResouceRoot<T> orig) {
            return orig;
        }

        private static ISceneCondition GetConditionFor(ParamsReader reader) {
            ISceneCondition sc = null;
            foreach (IConditionFactory cf in factories) {
                sc = cf.GetCondition(reader);
                if (sc != null) {
                    Debug.WriteLine("");
                    return sc;
                }
            }
            // find from primitive conditions.
            ParamsReader pr2 = reader[CONDITION_DESC_ATTR];
            if (pr2.IsNull) throw new PluginXmlException(reader, CONDITION_DESC_ATTR+" must be specified.");
            string attr = pr2.InnerText;
            sc = PrimitiveConditions.GetByName(attr);
            if (sc == null) {
                throw new PluginXmlException(reader, "Unresolved condition description : "+attr);
            }
            return sc;
        }
        #endregion
    }
}
