using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace nft.framework.plugin
{
    [Serializable]
    public sealed class VContributionId : VAbstractIdentifier
    {
        // Contribution ID will be used as a part of filepath.
        // {Plugin_id}\{Contribution_short_id}
        static public readonly string PID_Sepalator = "\\";

        public static VContributionId FromString(string idStr)
        {
            int n = idStr.IndexOf(PID_Sepalator);
            if (n == -1) {
                string format = I18n.T("'{0}' is not a invalid contribution ID. It should be '{plugin_id}\\{inner_id}'.");
                Debug.Assert(false, String.Format(format, idStr));
            }

            return new VContributionId(idStr.Substring(0, n), idStr.Substring(n + PID_Sepalator.Length));
        }

        internal static VContributionId CreateFrom(ParamsReader contribRootReader)
        {
            string short_id = contribRootReader["id"].InnerText;
            if (short_id == null)
            {
                string templ = I18n.T("An attribute '{1}' is required for the node '{0}'.");
                throw new PluginXmlException(contribRootReader, string.Format(
                    templ, "contribution", "name", contribRootReader.SourceURI));
            }

            Uri uri = new Uri(contribRootReader.SourceURI);
            return new VContributionId(new VPluginId(uri), short_id);
        }

        internal static VContributionId CreateUsingOrigin(string pathorid, Plugin origin)
        {
            int n = pathorid.IndexOf(PID_Sepalator);
            if (n == -1)
            {// 'hoge' -> { pluginID = origin.ID, subId = 'hoge' }
                return new VContributionId(origin, pathorid);
            }
            else if (n == 1 && pathorid[0] == '.')
            {// '.\hoge' -> { pluginID = origin.ID, subId = 'hoge' }
                return new VContributionId(origin, pathorid.Substring(2));
            }
            else
            {// 'fuga\hoge' -> { pluginID = 'fuga', subId = 'hoge' }
                return new VContributionId(pathorid.Substring(0, n), pathorid.Substring(n + PID_Sepalator.Length));
            }
        }

        public VContributionId(Plugin owner, string subId) : this(owner.PluginID.AsString, subId)
        {}

        public VContributionId(VPluginId pluginId, string subId) : this(pluginId.AsString, subId)
        {}

        private VContributionId(string pluginId, string subId) : base(CombineWithValidating(pluginId, subId))
        {}
        
        public string PluginID
        {
            get {
                int n = AsString.IndexOf(PID_Sepalator);
                return AsString.Substring(0, n);
            }
        }

        public string SubID
        {
            get
            {
                int n = AsString.IndexOf(PID_Sepalator);
                return AsString.Substring(n + PID_Sepalator.Length);
            }
        }

        private static string CombineWithValidating(string pluginId, string subId)
        {
            ValidateIdCaracters(pluginId, "Plugin-ID");
            ValidateIdCaracters(subId.Replace("factory#",""), "Sub-ID");
            return pluginId + PID_Sepalator + subId;
        }

        private static void ValidateIdCaracters(string testStr, string argName)
        {
            string format = I18n.T("'{0}' contains invalid character(s) as a {1}.");
            Debug.Assert( 
                PluginUtil.IdCharValidator.IsMatch(testStr), String.Format(format, testStr, argName)
            );
        }
    }
}
