using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using LinkFuture.Extensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System.Text;
using System.Net;
using System.Linq.Expressions;
using System.Collections.Specialized;
using LinkFuture.Library;

namespace TestProject
{
    /// <summary>
    ///This is a test class for MyExtensionTest and is intended
    ///to contain all MyExtensionTest Unit Tests
    ///</summary>
    [TestClass()]
    public class MyExtensionTest
    {
        #region String
        /// <summary>
        ///A test for UnescapeXml
        ///</summary>
        [TestMethod()]
        public void UnescapeXmlTest()
        {
            var input = "<A></A>";
            var actual = input.EscapeXml();
            var expected = actual.UnescapeXml();
            Assert.AreEqual(expected, input);
        }
        /// <summary>
        ///A test for EncodeJson
        ///</summary>
        [TestMethod()]
        public void EncodeJsonTest()
        {
            string s = "ABC:{\\}";
            string expected = "ABC:{\\\\}"; 
            string actual = s.EncodeJson();
            Assert.AreEqual(expected, actual);
        }
        /// <summary>
        ///A test for FromBase64
        ///</summary>
        [TestMethod()]
        public void FromBase64Test()
        {
            string s = "ABC!@#!@#$!"; 
            string actual = s.ToBase64();
            string expected = actual.FromBase64(); 
            Assert.AreEqual(expected, s);
        }
        /// <summary>
        ///A test for IsFileName
        ///</summary>
        [TestMethod()]
        public void IsFileNameTest()
        {
            string s = "C:\\ab.com";
            bool expected = false; 
            bool actual = s.IsFileName();
            Assert.AreEqual(expected, actual);
        }
        /// <summary>
        ///A test for ToCamelCasing
        ///</summary>
        [TestMethod()]
        public void ToCamelCasingTest()
        {
            string value = "AbCd"; 
            string expected = "abCd";
            string actual = value.ToCamelCasing();
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for ToPascalCasing
        ///</summary>
        [TestMethod()]
        public void ToPascalCasingTest()
        {
            string value = "abCd";
            string expected = "AbCd";
            string actual = value.ToPascalCasing();
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for SubString
        ///</summary>
        [TestMethod()]
        public void SubStringTest()
        {
            string s = "ABCDEFG AAAA"; 
            int maxLength = 7; 
            string appendString = "_BBB";
            string expected = "ABC_BBB"; 
            string actual = s.SubString(maxLength, appendString);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for IsEmail
        ///</summary>
        [TestMethod()]
        public void IsEmailTest()
        {
            Assert.AreEqual("half@hotmail.com".IsEmail(), true);
            Assert.AreEqual("half!#hotmail.com".IsEmail(), false);
        }
        /// <summary>
        ///A test for IsUri
        ///</summary>
        [TestMethod()]
        public void IsUriTest()
        {
            Assert.AreEqual("http://www.bing.com".IsUri(), true);
            Assert.AreEqual("http:www.bing.com".IsUri(), false);
        }
        /// <summary>
        ///A test for IsIP
        ///</summary>
        [TestMethod()]
        public void IsIPTest()
        {
            Assert.AreEqual("192.168.0.1".IsIP(), true);
            Assert.AreEqual("2001:0db8:0000:0000:0000:ff00:0042:8329".IsIP(), true);
            Assert.AreEqual("ADSFASDF".IsIP(), false);
        }

        /// <summary>
        ///A test for ToStream
        ///</summary>
        [TestMethod()]
        public void ToStreamTest()
        {
            var input = "ASDF";
            Stream actual = input.ToStream();
            Assert.AreEqual(actual.Length, input.Length);
            actual = input.ToStream(Encoding.UTF8);
            Assert.AreEqual(actual.Length, input.Length);
        }
        #endregion

        #region IDictionary
        /// <summary>
        ///A test for DictionaryJoin
        ///</summary>
        [TestMethod()]
        public void DictionaryJoinTest()
        {
            IDictionary<int, object> value = new Dictionary<int, object>
                                                 {
                                                    {1,"1"},{2,2}
                                                 };
            IDictionary<int, object> join = new Dictionary<int, object>
                                                 {
                                                    {3,"1"},{4,2}
                                                 };
            value.CollectionEquals(join);
            value.Join(join);
            Assert.AreEqual(value.Count, 4);
        }
        [TestMethod()]
        public void AppendIfNotExistTest()
        {
            IDictionary<int, object> value = new Dictionary<int, object>
                                                 {
                                                    {1,"1"},{2,2}
                                                 };
            value.AppendIfNotExist(1, 1);
            Assert.AreEqual(value.Count, 2);
            value.AppendIfNotExist(3, 1);
            Assert.AreEqual(value.Count, 3);
        }
        [TestMethod()]
        public void CloneDictionaryTest()
        {
            IDictionary<int, object> value = new Dictionary<int, object>
                                                 {
                                                    {1,"1"},{2,2}
                                                 };
            var newOne = value.Clone();
            Assert.AreEqual(value.Count, newOne.Count);
        }

        [TestMethod()]
        public void CollectionEqualsTest()
        {
            var a1 = new List<int> { 1, 2, 3 };
            var a2 = new List<int> { 1, 2, 3 };
            Assert.IsTrue(a1.CollectionEquals(a2));

            var d1 = new Dictionary<int, object>() { { 1, 2 }, { 2, "string" } };
            var d2 = new Dictionary<int, object>() { { 2, "string" }, { 1, 2 } };
            Assert.IsTrue(d1.CollectionEquals(d2));

            var d3 = new Dictionary<int, object>() { { 1, 2 }, { 2, "string1" } };
            var d4 = new Dictionary<int, object>() { { 2, "string" }, { 1, 2 } };
            Assert.IsTrue(!d3.CollectionEquals(d4));
        }

        [TestMethod()]
        [ExpectedException(typeof(ArgumentException))]
        public void CollectionEqualsExceptionTest()
        {
            var a = new List<List<int>>
                        {
                            new List<int>{1,2,3},
                            new List<int>{4,5,6},
                        };
            var b = new List<List<int>>
                        {
                            new List<int>{1,2,3},
                            new List<int>{4,5,6},
                        };
            Assert.IsTrue(a.CollectionEquals(b));
        }

        #endregion

        #region Bool
        /// <summary>
        ///A test for ToBool
        ///</summary>
        [TestMethod()]
        public void ToBoolTest()
        {
            Assert.IsTrue("1".ToBool());
            Assert.IsTrue(!"0".ToBool());
            Assert.IsTrue("true".ToBool());
            Assert.IsTrue("TRue".ToBool());
            Assert.IsTrue(!"false".ToBool());
            Assert.IsTrue(!"False".ToBool());
            Assert.IsTrue(!"XXXXX".ToBool());
            Assert.IsTrue("XXXXX".ToBool(true));
        }
        #endregion

        #region Decimal
        /// <summary>
        ///A test for IsNaNOrZero
        ///</summary>
        [TestMethod()]
        public void DecimalTest()
        {
            Assert.AreEqual("1".ToDecimal(), 1);
            Assert.AreEqual("XX".ToDecimal(1), 1);
            Assert.AreEqual("XX".ToDecimal(), 0);
            Assert.IsTrue(!"1".ToDecimal().IsNaNOrZero());
            Assert.IsTrue("0".ToDecimal().IsNaNOrZero());
            Assert.IsTrue(0M.IsNaNOrZero());
            Assert.IsTrue(Decimal.Zero.IsNaNOrZero());
            Assert.IsTrue(!Decimal.MaxValue.IsNaNOrZero());
            Assert.IsTrue(!Decimal.MinValue.IsNaNOrZero());
        }
        #endregion

        #region Double
        /// <summary>
        ///A test for IsNaNOrZero
        ///</summary>
        [TestMethod()]
        public void DoubleTest()
        {
            Assert.AreEqual("0".ToDouble(), 0F);
            Assert.AreEqual("0.1".ToDouble(), 0.1);
            Assert.AreEqual("0.1S".ToDouble(), 0.0);
            Assert.AreEqual("0.1S".ToDouble(3.3), 3.3);

            Assert.IsTrue(((double)0F).IsNaNOrZero());
            Assert.IsTrue(double.NaN.IsNaNOrZero());
            Assert.IsTrue(double.NegativeInfinity.IsNaNOrZero());
            Assert.IsTrue(double.PositiveInfinity.IsNaNOrZero());
            Assert.IsTrue(!double.Epsilon.IsNaNOrZero());
            Assert.IsTrue(!double.MaxValue.IsNaNOrZero());
            Assert.IsTrue(!double.MinValue.IsNaNOrZero());
            Assert.IsTrue("0".ToDouble().IsNaNOrZero());

            double maxValue = 50;
            double minValue = 0;
            Assert.AreEqual(20, 40.0.FromPrecent(maxValue, minValue));
            Assert.AreEqual(40, 20.0.ToPrecent(maxValue, minValue));

            Assert.AreEqual("2%", 0.02.ToPrecent());
            Assert.AreEqual("2.13%", 0.02125.ToPrecent());
        }
        #endregion

        #region Float
        [TestMethod()]
        public void FloatTest()
        {
            Assert.AreEqual("0".ToFloat(), 0F);
            Assert.AreEqual("0.1".ToFloat(), 0.1F);
            Assert.AreEqual("0.1S".ToFloat(), 0.0F);
            Assert.AreEqual("0.1S".ToFloat(3.3F), 3.3F);

            Assert.IsTrue(0F.IsNaNOrZero());
            Assert.IsTrue(float.NaN.IsNaNOrZero());
            Assert.IsTrue(float.NegativeInfinity.IsNaNOrZero());
            Assert.IsTrue(float.PositiveInfinity.IsNaNOrZero());
            Assert.IsTrue(!float.Epsilon.IsNaNOrZero());
            Assert.IsTrue(!float.MaxValue.IsNaNOrZero());
            Assert.IsTrue(!float.MinValue.IsNaNOrZero());
            Assert.IsTrue("0".ToFloat().IsNaNOrZero());
        }

        #endregion

        #region Int
        [TestMethod()]
        public void IntTest()
        {
            Assert.AreEqual("0".ToInt(), 0);
            Assert.AreEqual("0.1".ToInt(), 0);
            Assert.AreEqual("0.1S".ToInt(), 0);
            Assert.AreEqual("0.1S".ToInt(3), 3);

            Assert.IsTrue(((int?)0).IsNaNOrZero());
            Assert.IsTrue(0.IsNaNOrZero());
            Assert.IsTrue(((int?)null).IsNaNOrZero());
            Assert.IsTrue(((int?)"0".ToInt()).IsNaNOrZero());
        }

        #endregion

        #region Guid
        [TestMethod]
        public void GuidTest()
        {
            var a = Guid.NewGuid();
            Assert.AreEqual(a.ToString().ToGuid(), a);
            Assert.AreEqual("AAAA".ToGuid(), Guid.Empty);
            Assert.AreEqual("AAAA".ToGuid(a), a);
        }
        #endregion

        #region URI
        [TestMethod]
        public void URITest()
        {
            Assert.IsTrue("http://www.msn.com".ToUri() != null);
            Assert.IsTrue("httpwww.msn.com".ToUri() != null);
            Assert.IsTrue("httpwww.msn.com".ToUri(UriKind.Absolute) == null);
            Assert.IsTrue("http://www.msn.com/abc/a.aspx".ToUri().ToUrlBase() == "http://www.msn.com/");
            Assert.IsTrue("http://www.msn.com:88/abc/a.aspx".ToUri().ToUrlBase() == "http://www.msn.com:88/");
            Assert.IsTrue("http://www.msn.com/abc/a.aspx".ToUrlBase() == "http://www.msn.com/");
        }
        [TestMethod]
        public void URICombineTest()
        {
            var a = "http://www.host1.com:88/abc/a.aspx".ToUri();
            var baseUri = "http://www.host2.com:33/XXX/baseUri.aspx".ToUri();
            var result = a.Combine(baseUri);
            Assert.IsTrue(result.AbsoluteUri == "http://www.host1.com:88/abc/a.aspx");

            var b = "b/b.aspx".ToUri();
            result = b.Combine(baseUri);
            Assert.IsTrue(result.AbsoluteUri == "http://www.host2.com:33/XXX/b/b.aspx");

            var d = "/b/b.aspx".ToUri();
            result = d.Combine(baseUri);
            Assert.IsTrue(result.AbsoluteUri == "http://www.host2.com:33/b/b.aspx");
        }
        [TestMethod]
        public void URIQueryTest()
        {

            var list = "http://www.host1.com:88/abc/a.aspx".Query();
            Assert.IsNull(list);

            list = "http://www.host1.com:88/abc/a.aspx?a=1+2&b=this is name test&c".Query();
            Assert.IsTrue(list.Count == 3
                && list["a"] == "1+2"
                && list["b"] == "this is name test"
                && list["c"] == string.Empty
                );

            list = "/abc/a.aspx?a=1&b=2&c".ToUri().Query();
            Assert.IsTrue(list.Count == 3);

            //append
            var uri = "http://www.host1.com:88/abc/a.aspx?a=1+2&b=this is name test&c";
            var output = uri.UpdateQuery("new", "Fuck new");
            Assert.IsTrue(output == "http://www.host1.com:88/abc/a.aspx?a=1+2&b=this is name test&c&new=Fuck%20new");

            //append
            uri = "http://www.host1.com:88/abc/a.aspx";
            output = uri.UpdateQuery("c", "Fuck new");
            Assert.IsTrue(output == "http://www.host1.com:88/abc/a.aspx?c=Fuck%20new");

            //update
            uri = "http://www.host1.com:88/abc/a.aspx?c=Fuck%20new&b";
            output = uri.UpdateQuery("b", "Fuck new");
            Assert.IsTrue(output == "http://www.host1.com:88/abc/a.aspx?c=Fuck%20new&b=Fuck%20new");

            uri = output;
            //update
            output = uri.UpdateQuery("c", "AAA");
            Assert.IsTrue(output == "http://www.host1.com:88/abc/a.aspx?c=AAA&b=Fuck%20new");

            list = output.Query();
            Assert.IsTrue(list.Count == 2
                && list["b"] == "Fuck new"
                && list["c"] == "AAA"
                );
        }
        #endregion

        #region Datetime
        [TestMethod]
        public void DateTimeTest()
        {
            Assert.IsTrue(!DateTime.MinValue.IsValid());

            Assert.IsTrue("2012/12/12".ToDateTime().ToString(CultureInfo.InvariantCulture) == "12/12/2012 00:00:00");
            Assert.IsTrue("ASDFA".ToDateTime() == DateTime.MinValue);
            Assert.IsTrue("ASDFA".ToDateTime(DateTime.MaxValue) == DateTime.MaxValue);

            Assert.IsTrue("2012/12/12".ToUtcDateTime().ToString(CultureInfo.InvariantCulture) == "12/12/2012 08:00:00");

            Assert.IsTrue("2012/12/12".ToUtcDateTime().ToString(CultureInfo.InvariantCulture) == "12/12/2012 08:00:00");

            Assert.IsTrue("2012/12/12 00:00:00".ToDateTimeOffset().ToString(CultureInfo.InvariantCulture) == "12/12/2012 00:00:00 -08:00");

            Assert.IsTrue("2012/12/12 00:00:00".ToDateTimeOffset(-420).ToString(CultureInfo.InvariantCulture) == "12/12/2012 00:00:00 -07:00");

            //if no time zone specific
            Assert.IsTrue("2012/12/12 00:00:00".ToDateTime().ToDateTimeOffset().ToString(CultureInfo.InvariantCulture) == "12/12/2012 00:00:00 -08:00");
            //utc timezone specific
            Assert.IsTrue("2012/12/12 00:00:00".ToUtcDateTime().ToDateTimeOffset().ToString(CultureInfo.InvariantCulture) == "12/12/2012 08:00:00 +00:00");
        }

        #endregion

        #region Array
        [TestMethod]
        public void EndWithTest()
        {
            var input = new []{1, 2, 3, 4, 5};
            var end1 = new[] { 4, 5 };
            var end2 = new[] { 7, 5 };
            Assert.IsTrue(input.EndWith(end1));
            Assert.IsFalse(input.EndWith(end2));
        }
        [TestMethod]
        public void RemoveTest()
        {
            var input = new[] { 0, 1, 2, 3, 4, 5 };
            var output = input.Remove(3, 3);
            Assert.IsTrue(output.Length == input.Length - 1);
            output = input.Remove(3);
            Assert.IsTrue(output.Length == 3);
        }
        [TestMethod]
        public void MergeTest()
        {
            var input1 = new[] { 0, 1, 2, 3, 4, 5 };
            var input2 = new[] { 6, 7, 8, 9, 10, 11 };
            var input3 = new[] { 12, 13, 14, 15, 16, 17 };

            var list = new List<int[]>()
                             {
                                 input1,
                                 input2,
                                 input3
                             };
            var output = list.Merge();
            Assert.IsTrue(output.Length == input1.Length+input2.Length+input3.Length);
        }

        [TestMethod]
        public void ArrayEndWithTest()
        {
            var input1 = new[] { 0, 1, 2, 3, 4, 5 };
            var input2 = new[] { 6, 7, 8, 9, 10, 11 };
            var input3 = new[] { 12, 13, 14, 15, 16, 17 };

            var list = new List<int[]>()
                             {
                                 input1,
                                 input2,
                                 input3
                             };
            var end1 = new[] { 16, 17 };
            var end2 = new[] { 16, 18 };
            var end3 = new[] { 11, 12, 13, 14, 15, 16, 17 };
            var end4 = new[] { 0, 1, 2, 3, 4, 5,6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };

            Assert.IsTrue(list.EndWith(end1));
            Assert.IsTrue(list.EndWith(end3));
            Assert.IsTrue(list.EndWith(end4));
            Assert.IsFalse(list.EndWith(end2));
        }

        [TestMethod]
        public void SubByteTest()
        {
            var input = new byte[] { 0, 1, 2, 3, 4, 5 };
            var output = input.SubByte(3);
            Assert.IsTrue(output.Length == input.Length -3);

            output = input.SubByte(3,1);
            Assert.IsTrue(output[0] == input[3]);

            output = input.SubByte(-1);
            Assert.IsTrue(output.Length == input.Length);

            output = input.SubByte(input.Length);
            Assert.IsTrue(output ==null);
        }
        [TestMethod]
        public void ArrayCompareTest()
        {
            var input1 = new byte[] { 0, 1, 2, 3, 4, 5 };
            var input2 = new byte[] { 0, 1, 2, 3, 4, 5 };
            Assert.IsTrue(input1.Compare(input2));
            Assert.IsTrue(input1.CollectionEquals(input2));
            
            var input3 = new byte[] { 0, 1, 2, 3, 4, 6 };
            Assert.IsFalse(input1.Compare(input3));
            Assert.IsFalse(input1.CollectionEquals(input3));
            
            var input4 = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
            Assert.IsFalse(input1.Compare(input4));
            Assert.IsFalse(input1.CollectionEquals(input4));

        }

        #endregion

        #region Refection
        [TestMethod]
        public void GetPropertyNameTest()
        {
            var name = MyExtension.GetPropertyName(((TestClass p) => p.TestProperty));
            Assert.IsTrue(name.Equals("TestProperty"));
        }
        [TestMethod]
        public void GetAttributeTest()
        {
            var myAttribute = MyExtension.GetAttribute<ColumnMemberAttribute, TestClass, string>((p => p.TestProperty));
            Assert.IsTrue(myAttribute!=null);

            var myClass = new TestClass();
            myAttribute = MyExtension.GetAttribute<ColumnMemberAttribute,string>(() => myClass.TestProperty);
            Assert.IsTrue(myAttribute != null);

            myAttribute = typeof(TestClass).GetProperties()[0].GetAttribute<ColumnMemberAttribute>();
            Assert.IsTrue(myAttribute != null);

            //Has Attribute 
            var hasAttribute = MyExtension.HasAttribute<ColumnMemberAttribute, string>(() => myClass.TestProperty);
            Assert.IsTrue(hasAttribute);


            hasAttribute = MyExtension.HasAttribute<ColumnMemberAttribute, TestClass, string>((p => p.TestProperty));
            Assert.IsTrue(hasAttribute);

            hasAttribute = typeof(TestClass).GetProperties()[0].HasAttribute<ColumnMemberAttribute>();
            Assert.IsTrue(hasAttribute);
        }
        [TestMethod]
        public void PeopertyGetterAndSetterTest()
        {
            var test = new TestClass();
            var propertyList = typeof (TestClass).ReteriveAttributeList<ColumnMemberAttribute>();

            var get = MyExtension.GetValueGetter((TestClass p) => p.TestProperty);
            var set = MyExtension.GetValueSetter((TestClass p) => p.TestProperty);

            var setValue = "ABC";
            set.DynamicInvoke(test, setValue);
            var result = get.DynamicInvoke(test);
            Assert.IsTrue(string.Equals(setValue, result));
            Assert.IsTrue(string.Equals(setValue, test.TestProperty));


            setValue = "CCCCCCC";
            test.SetProperty(typeof(TestClass).GetProperties()[0], setValue);
            Assert.IsTrue(string.Equals(setValue, test.TestProperty));


            //expression test start
            var watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1000; i++)
            {
                test = new TestClass();
                setValue = "ABCD" + i;
                test.SetProperty(p => p.TestProperty, setValue);
                Assert.IsTrue(string.Equals(setValue, test.TestProperty));
                var output = test.GetProperty(p => p.TestProperty);
                Assert.IsTrue(string.Equals(output, test.TestProperty));
            }
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);

            watch.Restart();
            for (int i = 0; i < 1000; i++)
            {
                test = new TestClass();
                var guidValue = Guid.NewGuid();
                test.SetProperty(p => p.GuidProperty, guidValue);
                Assert.IsTrue(string.Equals(guidValue, test.GuidProperty));
                var outputGuid = test.GetProperty(p => p.GuidProperty);
                Assert.IsTrue(string.Equals(outputGuid, test.GuidProperty));
            }
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
        }
        [TestMethod]
        public void ChangeType()
        {
            var testData = Guid.NewGuid().ToString();
            var test = testData.ChangeType<Guid>();
            Assert.IsTrue(string.Equals(testData,test.ToString()));

            testData = "true";
            var test1 = testData.ChangeType<bool>();
            Assert.IsTrue(test1);

            testData = "0";
            test1 = testData.ChangeType<bool>();
            Assert.IsTrue(!test1);
        }

        [TestMethod]
        public void CaseTest()
        {
            var p1 = MyExtension.GetProperty(((UserInfo b) => b.Password));
            var p2 = MyExtension.GetProperty(() => new UserInfo().Password);
            Assert.IsTrue(p1.Name.Equals(p2.Name));
        }

        #endregion

        #region Retrieve Feed
        [TestMethod]
        public void RetrieveFeed()
        {
            var result = "http://www.bing.com".ToUri().RetrieveFeed<string>();
            Assert.IsTrue(!string.IsNullOrWhiteSpace(result));

            var myParams = new NameValueCollection {{"q", "skyfall"}, {"B", "2"}};
            result = "http://www.bing.com".ToUri().RetrieveFeed<string>(myParams);
            Assert.IsTrue(!string.IsNullOrWhiteSpace(result));

            var reset = new AutoResetEvent(false);
            "http://www.bing.com".ToUri().RetrieveFeedAsync(delegate(CallbackInfo<string> output)
                {
                    Console.WriteLine("RetrieveFeedAsync");
                    Console.WriteLine(output.Result);
                    Assert.IsTrue(!string.IsNullOrWhiteSpace(output.Result));
                    reset.Set();
                },myParams);
            reset.WaitOne(TimeSpan.FromMinutes(1));
        }
        #endregion
    }
}
