using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using Newtonsoft.Json.Linq;

using Learn_CSS.APP.Web.Models.Tools;

namespace Learn_CSS.APP.Web.Models.Services
{
    public class JsonIncludeService
    {

        /// <summary>
        /// Содержит ли main в себе subobject
        /// </summary>
        /// <param name="main"></param>
        /// <param name="subobject"></param>
        /// <returns></returns>
        public bool Include(JToken main, JToken subobject)
        {
            return RecursivInclude(main, subobject);
        }


        protected bool RecursivInclude(JToken main, JToken sub)
        {           
            //Если у элементов разные типы то
            if (main.Type != sub.Type)
                return false;

            if (main.Type == JTokenType.Property)
                throw new Exception("Error type");


            if (main is JValue)
            {
                return Include_Values((JValue)main, (JValue)sub, RecursivInclude);
            }  
            if (main is JObject)
            {
                return Include_Object((JObject)main, (JObject)sub, RecursivInclude);
            }  
            if (main is JArray)
            {
                return Include_Array_IgnorePosition((JArray)main, (JArray)sub, RecursivInclude);
            }

            throw new Exception("Unknow type");
        }



        #region Include

        /// <summary>
        /// Сравнивает элементы значимого типа
        /// </summary>
        protected bool Include_Values(JValue main, JValue sub, Func<JToken, JToken, bool> IncludeFunction) 
        {
            return main.Value<string>() == sub.Value<string>();
        }


        /// <summary>
        /// Сравнивает объекты по ключам
        /// </summary>
        protected bool Include_Object(JObject main, JObject sub, Func<JToken, JToken, bool> IncludeFunction) 
        {
            //Получить все ключи
            var main_childrens_properties = main.Properties().ToList();
            var sub_childrens_properties = sub.Properties().ToList();

            if (sub_childrens_properties.Count > main_childrens_properties.Count)
            {
                return false;
            }

            foreach (var sub_elem in sub_childrens_properties)
            {
                var main_elem = main_childrens_properties
                    .FirstOrDefault(e => e.Name == sub_elem.Name);

                //В основном объекте нет ключа
                if (main_elem == null)
                {
                    return false;
                }

                if (!IncludeFunction(main_elem.First(), sub_elem.First()))
                {
                    return false;
                }
            }

            return true;
        }


        /// <summary>
        /// Сравнение вхождения по массиву, со строгим учетом позиции main[i] = sub[i]
        /// </summary>
        protected bool Include_Array_WithPosition(JArray main, JArray sub, Func<JToken, JToken, bool> IncludeFunction) 
        {
            var main_children = main.Children().ToList();
            var sub_children = sub.Children().ToList();

            if (sub_children.Count > main_children.Count)
            {
                return false;
            }

            for (int i = 0; i < sub_children.Count(); i++)
            {
                if (!IncludeFunction(main_children[i], sub_children[i]))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Сравнение вхождение по массиву, без строго учета позиции main.contains(sub[i])
        /// </summary>
        protected bool Include_Array_IgnorePosition(JArray main, JArray sub, Func<JToken, JToken, bool> IncludeFunction) 
        {
            var main_children = main.Children().ToList();
            var sub_children = sub.Children().ToList();

            if (sub_children.Count > main_children.Count)
            {
                return false;
            }

            while (sub_children.Count() > 0)
            {
                var find_main = main_children.FirstOrDefaultWithPosition(e => IncludeFunction(e, sub_children[0]));

                if (find_main == null)
                {
                    return false;
                }

                sub_children.RemoveAt(0);
                main_children.RemoveAt(find_main.Index);
            }

            return true;
        }

        #endregion

    }
}
