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

namespace ZionRobot.Helpers
{
    class CQuery
    {
        /*
         element	-> Tipo da tag (input, h1, ...)
        :first	-> Primeiro elemento
        :last	-> Último elemento
        :eq(v)	-> Posição na lista
        [attribute=value] -> Todos os elementos com o atributo e valor correspondente (name="userName")
        */

        //private static string pattern = @"\[[^\]]*\]|([:=\[\(]{0}[a-zA-Z0-9;]{1,})";
        private static string pattern = @"\[.*\]$|([:=\[\(]{0}[a-zA-Z0-9;]{1,})";

        public static bool CompareInnerHtmlByValue(mshtml.IHTMLElement element, string value)
        {
            return element.innerHTML == value;
        }

        public static bool CompareInnerHtmlByElements(mshtml.IHTMLElement element1, mshtml.IHTMLElement element2)
        {
            try
            {

                var aa = (element1 as mshtml.HTMLDocument).body.innerText;
                var bb = (element2 as mshtml.HTMLDocument).body.innerText;
            }
            catch (Exception ex)
            {

                throw;
            }
            var db = ((mshtml.HTMLDocument)element1.document.body.document).body.innerText;
            var dbx = ((mshtml.HTMLDocument)element2.document.body.document).body.innerText;

            return element1.innerHTML == element2.innerHTML;
        }

        public static bool CompareInnerTextByValue(mshtml.IHTMLElement element, string value)
        {
            return element.innerText == value;
        }

        public static bool CompareInnerTextByElements(mshtml.IHTMLElement element1, mshtml.IHTMLElement element2)
        {
            return element1.innerText == element2.innerText;
        }

        public static List<mshtml.IHTMLElement> Find(string selector, List<mshtml.IHTMLElementCollection> listElements)
        {
            List<mshtml.IHTMLElement> result = new List<mshtml.IHTMLElement>();
            bool cont = true;
            foreach (string tag in selector.Split('>').ToList())
            {
                var tmp = System.Text.RegularExpressions.Regex.Matches(tag, pattern);
                int count = 0;
                foreach (System.Text.RegularExpressions.Match match in tmp)
                {
                    count++;
                    switch (match.Value[0])
                    {
                        case '[':

                            string[] parts = match.Value.TrimStart('[').TrimEnd(']').Split(@"=".ToCharArray(), 2);

                            if (result.Count == 0)
                            {
                                foreach (var elements in listElements)
                                {
                                    foreach (mshtml.IHTMLElement element in elements)
                                    {
                                        var attr = element.getAttribute(parts[0]);

                                        if (attr != null && !DBNull.Value.Equals(attr) && attr == parts[1])
                                            result.Add(element);
                                    }
                                }
                            }
                            else
                            {
                                /*result.ForEach(r =>
                                {
                                    var a = r.getAttribute(parts[0]);
                                });*/

                                if (parts[0].ToUpper() == "CLASS")
                                {
                                    result = searchByClass(
                                        result.Where(i => i.className != null && i.className != string.Empty).ToList(), parts[1]);

                                    if (result.Count == 0)
                                        cont = false;
                                }
                                else if(parts[0].ToUpper() == "INNERTEXT")
                                {
                                    result = searchByInnerText(result, parts[1]);

                                    if (result.Count == 0)
                                        cont = false;
                                }
                                else
                                {
                                    //result.ForEach(r =>
                                    //{
                                    //    var temp = r.getAttribute(parts[0]);
                                    //});

                                    try
                                    {
                                        if (parts.Count() == 1)
                                            break;

                                        var a = result.Where(element =>
                                                element.getAttribute(parts[0]) == parts[1]);



                                        result = result.Where(element =>
                                                element.getAttribute(parts[0]) == parts[1]
                                                || (
                                                    (//element.getAttribute(parts[0]) != null
                                                    //&& 
                                                    //element.getAttribute(parts[0]) != ""
                                                    //&&
                                                    System.Text.RegularExpressions.Regex.IsMatch(parts[1], "\\.\\*"))
                                                    && (
                                                        element.getAttribute(parts[0]) != null
                                                        &&
                                                        ((string)element.getAttribute(parts[0])).Contains(parts[1].Replace(".*", ""))
                                                    )
                                                    
                                                )
                                             ).ToList();

                                        /*List<mshtml.IHTMLElement> temp = new List<mshtml.IHTMLElement>();

                                        foreach(mshtml.IHTMLElement element in result)
                                        {
                                            try
                                            {

                                                var x = element.getAttribute(parts[0]);

                                                if (!string.IsNullOrEmpty(((string)element.getAttribute(parts[0]))))
                                                {

                                                    if (element.getAttribute(parts[0]) == parts[1] || (System.Text.RegularExpressions.Regex.IsMatch(parts[1], "\\.\\*"))
                                                        &&
                                                        ((string)element.getAttribute(parts[0])).Contains(parts[1].Replace(".*", "")))
                                                    {

                                                        temp.Add(element);
                                                    }

                                                }
                                            }
                                            catch (Exception)
                                            {

                                            }
                                        }
                                        result = temp;*/

                                        if (result.Count == 0)
                                            cont = false;
                                    }
                                    catch (Exception ex)
                                    {

                                        throw;
                                    }
                                }
                            }
                            break;
                        case ':':
                            break;
                        default:

                            string[] parts1 = match.Value.ToUpper().Split(";".ToCharArray(), 2);

                            if (result.Count == 0)
                            {
                                foreach (var elements in listElements)
                                {
                                    if (parts1.Count() == 1)
                                        result.AddRange(getChildren(elements.tags(match.Value)));
                                    else
                                        result.AddRange(getChildren(elements.item(int.Parse(parts1[1]))));
                                }
                            }
                            else
                            {
                                List<mshtml.IHTMLElement> temp = new List<mshtml.IHTMLElement>();

                                foreach (mshtml.IHTMLElement element in result)
                                {
                                    if (parts1.Count() == 1)
                                        temp.AddRange(getChildren((element.all as mshtml.IHTMLElementCollection).tags(match.Value)));
                                    else
                                        temp.AddRange(getChildren((element.all as mshtml.IHTMLElementCollection).item(match.Value, parts1[1])));
                                }
                                result = temp;
                            }

                            if (result.Count == 0)
                                cont = false;
                            break;
                    }

                    if (cont == false)
                        break;
                }
                if (cont == false)
                    break;
            };

            return result;
        }

        private static List<mshtml.IHTMLElement> searchByClass(List<mshtml.IHTMLElement> elements, string value)
        {
            List<mshtml.IHTMLElement> result = new List<mshtml.IHTMLElement>();
            List<string> parts = value.Split('|').ToList();

            foreach (mshtml.IHTMLElement element in elements)
            {
                foreach (string part in parts)
                {
                    if (System.Text.RegularExpressions.Regex.IsMatch(element.className, part))
                    {
                        result.Add(element);
                        break;
                    }
                }
            }
            return result;
        }

        private static List<mshtml.IHTMLElement> searchByInnerText(List<mshtml.IHTMLElement> elements, string value)
        {
            List<mshtml.IHTMLElement> result = new List<mshtml.IHTMLElement>();
            List<string> parts = value.Split('|').ToList();
            
            List<string> chars = new List<string>() { @"\", @"$", @"^", @"*", @"(", @")", @"+", 
                @"{", @"}", @"[", @"]", @"|", @"?", @"-", @".", @"/"};


            foreach (string part in parts)
            {
                string partTemp =
                    System.Text.RegularExpressions.Regex.Replace(part, @"\W", string.Empty); ;
                chars.ForEach(c =>
                {
                    partTemp = partTemp.Replace(c, '\\' + c);
                });


                foreach (mshtml.IHTMLElement element in elements)
                {
                    var text = element.innerText;
                    if (text != null)
                    {
                        string temp = System.Text.RegularExpressions.Regex.Replace(text, @"\W", string.Empty);

                        if (System.Text.RegularExpressions.Regex.IsMatch(temp, partTemp))
                        {
                            result.Add(element);
                            break;
                        }
                    }
                }
            }
            return result;
        }

        /*private static string ToLiteral(string input)
        {
            using (var writer = new System.IO.StringWriter())
            {
                using (var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"))
                {
                    provider.GenerateCodeFromExpression(new System.CodeDom.CodePrimitiveExpression(input), writer, null);
                    return writer.ToString();
                }
            }
        }*/

        private static List<mshtml.IHTMLElement> getChildren(dynamic elements)
        {
            List<mshtml.IHTMLElement> children = new List<mshtml.IHTMLElement>();
            foreach (mshtml.IHTMLElement element in elements)
            {
                children.Add(element);
            }
            return children;
        }

        private static List<mshtml.IHTMLElement> getChild(mshtml.IHTMLElement parent, string tagName)
        {
            List<mshtml.IHTMLElement> children = new List<mshtml.IHTMLElement>();

            foreach (mshtml.IHTMLElement child in parent.children)
            {
                if (child.tagName == tagName.ToUpper())
                    children.Add(child);

                children.AddRange(getChild(child, tagName));
            }

            return children;
        }
    }
}
