using System;
using System.Xml;
using nft.util;
using System.Diagnostics;

namespace nft.framework.plugin
{
	/// <summary>
	/// Responsible for loading a contribution from an XML element.
	/// </summary>
	public interface IContributionFactory
	{
		/// <summary>
		/// Loads a contribution from an XML Element "e",
		/// which belongs to the plugin "owner".
		/// 
		/// In case of an error, this method must throw an exception
		/// instead of silently returning null.
		/// </summary>
		Contribution load( Plugin owner, ParamsReader e );

		Type OutputType { get; }

        string Description { get; }
	}

    public class DefaultContributionFactory : Contribution, IContributionFactory
    {
        internal protected DefaultContributionFactory(Plugin p, ParamsReader elm)
            : base(p, elm)
        {
            ParamsReader nd = elm["class|implementation"];
            if (nd.IsNull) {
                throw new PluginXmlException(p, elm, "Contribution class must be specified by 'name' attribute in the <class> element.");
            }
            this.baseType = PluginUtil.loadTypeFromManifest(nd);
        }

		private readonly Type baseType;
		public Type OutputType { get{ return baseType; } }

        public Contribution load(Plugin owner, ParamsReader e) 
		{
            ParamsReader r = e["class"];
            Type prodtype;
            if (r.IsNull) {
                prodtype =  this.baseType;
            } else {
                object[] args = new object[] { owner, e };
                prodtype = PluginUtil.loadTypeFromManifest(r);
                if (!prodtype.IsSubclassOf(baseType)) {
                    /*
                    throw new PluginXmlException(owner, e, string.Format(
                        "{0} is incorrect for this contribution (expected:{1})", prodtype.FullName, baseType.FullName));
                    */
                    prodtype = this.baseType;
                }
            }
            Contribution contrib = (Contribution)Activator.CreateInstance(prodtype, new object[] { owner, e });
			return contrib;
		}

        public override string Description {
            get {
                string dbase = base.Description;
                if (dbase != null & dbase.Length > 0) {
                    return dbase;
                } else {
                    return base.Name;
                }
            }
        }
    }
}
