using System.Xml;
using Kit.Helpers;
using Kit.Helpers.Repository.Converter;

namespace Kit.Helpers.Repository.Xml
{
    public interface IXmlSimpleFilter<TContentKey, TKey>
    {
        TKey GetKey();
        TContentKey GetXmlContentKey();
    }

    public abstract class EntityXmlSelectSimpleRepository<TEntity, TFilter, TKey, TContextKey> :
       EntityXmlRepository<TContextKey>,
       ISelectPageXmlRepository<TEntity, TFilter, TContextKey>
       where TFilter : IXmlSimpleFilter<TContextKey, TKey>
       where TEntity : IPersistentKey<TKey>
    {
        public EntityXmlSelectSimpleRepository(string parentElementName, string path, string element, IConverter<XmlNode, TEntity> converter)
            : base(parentElementName, path, element)
        {
            _converter = converter;
        }

        public EntityXmlSelectSimpleRepository(string path, string element, IConverter<XmlNode, TEntity> converter) : base(path, element)
        {
            _converter = converter;
        }


        public EntityXmlSelectSimpleRepository(string element, IConverter<XmlNode, TEntity> converter) : base(element)
        {
            _converter = converter;
        }


        public EntityXmlSelectSimpleRepository(string element) : base(element)
        {
        }

        public EntityXmlSelectSimpleRepository(string path, string element) : base(path, element)
        {
        }

        public EntityXmlSelectSimpleRepository(string rootElementName, string path, string element)  : base(rootElementName, path, element)
        {
        }

        protected IConverter<XmlNode, TEntity> _converter = null;

        protected virtual TEntity ConvertToEntity(XmlNode persistent)
        {
            return _converter.Convert(persistent);
        }

        protected virtual TEntity ConvertToEntity(XmlNode persistent, TFilter filter)
        {
            return this.ConvertToEntity(persistent);
        }

        public virtual XmlNode GetParentNode(XmlNode root)
        {
            XmlNode result = root.SelectSingleNode(_path);
            if (!string.IsNullOrEmpty(_rootElementName))
            {
                var node = result.SelectSingleNode(_rootElementName);
                if (node == null)
                {
                    node = result.OwnerDocument.CreateElement(_rootElementName);
                    result.AppendChild(node);
                }
                result = node;
            }

            return result;
        }

        public virtual IQueryable<XmlNode> CreateQuery(XmlDocument db, TFilter filter)
        {
            XmlNode parent = GetParentNode(db);

            if (parent == null) parent = db.CreateElement("Empty");
            return parent.SelectNodes(this._elementName).Cast<XmlNode>().AsQueryable();
        }

        public virtual IQueryable<XmlNode> CreateQuery(XmlNode node, TFilter filter)
        {
            XmlNode parent = GetParentNode(node);

            if (parent == null) parent = node.OwnerDocument.CreateElement("Empty");
            return parent.SelectNodes(this._elementName).Cast<XmlNode>().AsQueryable();
        }

        protected virtual IQueryable<XmlNode> ApplyFilter(IQueryable<XmlNode> query, TFilter filter)
        {
            return query;
        }

        protected virtual TEntity FillXmlClass(TEntity entity, XmlNode persistent, TFilter filter)
        {
            return entity;
        }

        protected IQueryable<XmlNode> SelectNodesByFilter(XmlDocument db, TFilter filter)
        {
            IQueryable<XmlNode> query = this.CreateQuery(db, filter);
            query = this.ApplyFilter(query, filter);

            return query;
        }

        public virtual IEnumerable<TEntity> Select(XmlDocument db, TFilter filter)
        {
            IQueryable<XmlNode> query = this.SelectNodesByFilter(db, filter);
            query = this.ApplyFilter(query, filter);
            var tt = query.ToList()
                        .Select(x => this.FillXmlClass(this.ConvertToEntity(x, filter), x, filter))
                        .ToList();

            return this.FillResult(db, tt, filter);
        }

        public virtual IEnumerable<TEntity> Select(XmlNode node, TFilter filter)
        {
            IQueryable<XmlNode> query = this.CreateQuery(node, filter);
            query = this.ApplyFilter(query, filter);
            var tt = query.ToList()
                .Select(x => this.FillXmlClass(this.ConvertToEntity(x, filter), x, filter))
                .ToList();

            return this.FillResult(node.OwnerDocument ?? (XmlDocument)node, tt, filter);
        }

        public virtual IEnumerableWithPage<TEntity> SelectPage(XmlDocument db, TFilter filter, int pageNo, int pageSize)
        {
            IQueryable<XmlNode> query = this.CreateQuery(db, filter);
            query = this.ApplyFilter(query, filter);
            var result = new ListWithPage<TEntity>();
            result.TotalRows = query.Count();
            result.PageNo = pageNo;
            result.PageSize = pageSize;
            var tt = query.Skip((pageNo - 1) * pageSize).Take(pageSize)
                .Select(x => this.FillXmlClass(this.ConvertToEntity(x, filter), x, filter))
                .ToList();
            result.AddRange(FillResult(db, tt, filter));

            return result;
        }

        public virtual IEnumerable<TEntity> FillResult(XmlDocument db, IEnumerable<TEntity> entities, TFilter filter) { return entities; }

        public int Count(XmlDocument db, TFilter filter)
        {
            IQueryable<XmlNode> query = this.SelectNodesByFilter(db, filter);
            {
                return query.Count();
            }
        }
    }
}
