using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Xml;

namespace Wayne.Lib.Log
{
    /// <summary>
    /// Filter for a log configuration.
    /// </summary>
    public class LogConfigFilter
    {
        /// <summary>
        /// List of subfilters 
        /// </summary>
        public readonly List<LogConfigSubFilter> SubFilters = new List<LogConfigSubFilter>();

        /// <summary>
        /// Entity type regex
        /// </summary>
        public string EntityTypeRegexFilter { get; set; }

        /// <summary>
        /// Entity subtype regex
        /// </summary>
        public string EntitySubTypeRegexFilter { get; set; }

        /// <summary>
        /// Regex for the ID
        /// </summary>
        public string IdRegexFilter { get; set; }

        /// <summary>
        /// Filter level
        /// </summary>
        public DebugLogLevel FilterLevel { get; set; }

        /// <summary>
        /// Constructor
        /// </summary>
        public LogConfigFilter()
        {
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="entityTypeRegexFilter"></param>
        /// <param name="entitySubTypeRegexFilter"></param>
        /// <param name="idRegexFilter"></param>
        /// <param name="filterLevel"></param>
        public LogConfigFilter(string entityTypeRegexFilter, string entitySubTypeRegexFilter, string idRegexFilter, DebugLogLevel filterLevel)
        {
            EntityTypeRegexFilter = entityTypeRegexFilter;
            EntitySubTypeRegexFilter = entitySubTypeRegexFilter;
            IdRegexFilter = idRegexFilter;
            FilterLevel = filterLevel;
        }

        /// <summary>
        /// Deserialization constructor
        /// </summary>
        /// <param name="logFilterNode"></param>
        internal LogConfigFilter(XmlNode logFilterNode)
        {
            XmlAttribute entityTypeAttribute = logFilterNode.Attributes["EntityType"];
            if (entityTypeAttribute != null)
                EntityTypeRegexFilter = entityTypeAttribute.Value;
            else
                EntityTypeRegexFilter = string.Empty;

            XmlAttribute entitySubTypeAttribute = logFilterNode.Attributes["EntitySubType"];
            if (entitySubTypeAttribute != null)
                EntitySubTypeRegexFilter = entitySubTypeAttribute.Value;
            else
                EntitySubTypeRegexFilter = string.Empty;

            XmlAttribute idAttribute = logFilterNode.Attributes["Id"];
            if (idAttribute != null)
                IdRegexFilter = idAttribute.Value;
            else
                IdRegexFilter = string.Empty;

            XmlAttribute filterLevelttribute = logFilterNode.Attributes["FilterLevel"];
            if (filterLevelttribute != null)
                FilterLevel = EnumSupport.Parse(filterLevelttribute.Value, false, DebugLogLevel.Normal);
            else
                FilterLevel = DebugLogLevel.Normal;

            foreach (XmlNode childNode in logFilterNode.ChildNodes)
            {
                if (childNode.Name.Equals("SubFilter", StringComparison.Ordinal))
                {
                    XmlAttribute enabledAttribute = childNode.Attributes["Enabled"];
                    if ((enabledAttribute != null) && !XmlConvert.ToBoolean(enabledAttribute.Value))
                        continue;
                    SubFilters.Add(new LogConfigSubFilter(childNode));
                }
            }
        }

        internal bool MatchFilter(EntityCategory entityCategory, out DebugLogLevel debugLogLevel)
        {
            bool entityTypeMatch = (string.IsNullOrEmpty(EntityTypeRegexFilter) || Regex.IsMatch(entityCategory.Entity.EntityType, EntityTypeRegexFilter, RegexOptions.IgnoreCase));
            if (entityTypeMatch)
            {
                bool entitySubTypeMatch = (string.IsNullOrEmpty(EntitySubTypeRegexFilter) || Regex.IsMatch(entityCategory.Entity.EntitySubType, EntitySubTypeRegexFilter, RegexOptions.IgnoreCase));
                if (entitySubTypeMatch)
                {
                    bool idMatch = string.IsNullOrEmpty(IdRegexFilter);
                    if (!idMatch)
                    {
                        if (entityCategory.Entity.Id == IdentifiableEntity.NoId)
                            idMatch = Regex.IsMatch(string.Empty, IdRegexFilter, RegexOptions.IgnoreCase);
                        else
                            idMatch = Regex.IsMatch(entityCategory.Entity.Id.ToString(CultureInfo.InvariantCulture), IdRegexFilter, RegexOptions.IgnoreCase);
                    }
                    if (idMatch)
                    {
                        foreach (LogConfigSubFilter subFilter in SubFilters)
                        {
                            if (subFilter.MatchFilter(entityCategory, out debugLogLevel))
                            {
                                // Use the first match found.
                                return true;
                            }
                        }
                        debugLogLevel = FilterLevel;
                        return true;
                    }
                }
            }

            debugLogLevel = DebugLogLevel.Excluded;
            return false;
        }

        /// <summary>
        /// ToString
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Concat("EntityType=\"", EntityTypeRegexFilter ?? string.Empty, 
                "\", SubType=\"", EntitySubTypeRegexFilter ?? string.Empty,
                "\", Id=\"", IdRegexFilter ?? string.Empty, 
                "\", Level=\"", FilterLevel, "\"");
        }

        /// <summary>
        /// Serialization
        /// </summary>
        /// <param name="xmlWriter"></param>
        internal void WriteXml(XmlWriter xmlWriter)
        {
            xmlWriter.WriteStartElement("Filter");

            if (!string.IsNullOrEmpty(EntityTypeRegexFilter))
                xmlWriter.WriteAttributeString("EntityType", EntityTypeRegexFilter ?? string.Empty);

            if (!string.IsNullOrEmpty(EntitySubTypeRegexFilter))
                xmlWriter.WriteAttributeString("EntitySubType", EntitySubTypeRegexFilter ?? string.Empty);

            if (!string.IsNullOrEmpty(IdRegexFilter))
                xmlWriter.WriteAttributeString("Id", IdRegexFilter ?? string.Empty);

            xmlWriter.WriteAttributeString("FilterLevel", FilterLevel.ToString());

            foreach (LogConfigSubFilter subFilter in SubFilters)
                subFilter.WriteXml(xmlWriter);

            xmlWriter.WriteEndElement(); // Filter
        }
    }
}