﻿using System.Collections;
using System.Collections.Generic;
using AsmodeeDigital.Common.Plugin.Utils;
using NUnit.Framework;

namespace AsmodeeNet.Tests.Utils
{
    public class NotLoggableClass
    {
        public string str = default(string);
        public int i = default(int);
    }

    public class ReflectionTest
    {
        [Test, TestCaseSource("ProvideLoggableTypes")]
        public void TestCanBeLogged(object obj)
        {
            Assert.True(Reflection.CanBeLogged(obj));
        }

        public static IEnumerable<object> ProvideLoggableTypes()
        {
            yield return false;
            yield return 1.1f;
            yield return 2u;
            yield return "hi";
            yield return new int[] { 1, 2, 3 };
            yield return new List<int> { 1, 2, 3 };
        }

        [Test, TestCaseSource("ProvideNotLoggableTypes")]
        public void TestCannotBeLogged(object obj)
        {
            Assert.False(Reflection.CanBeLogged(obj));
        }

        public static IEnumerable<object> ProvideNotLoggableTypes()
        {
            yield return new NotLoggableClass();
            yield return new List<NotLoggableClass>();
            yield return new NotLoggableClass[] { };
        }

        [Test, TestCaseSource("ProvideExcludedFields")]
        public void TestExcludedFieldsAreNotReturned(HashSet<string> excludedFields, Hashtable expected)
        {
            ArbitratyReflectionObject test = new ArbitratyReflectionObject();
            Hashtable actual = Reflection.HashtableFromObject(test, excludedFields);

            // AsmoLogger.Debug("TestExcludedFieldsAreNotReturned", "", actual);

            if (excludedFields == null)
            {
                return;
            }

            Assert.AreEqual(expected, actual);
        }

        public static IEnumerable<object[]> ProvideExcludedFields()
        {
            yield return new object[] {
                null,
                new Hashtable() {{ "A", "a" }, { "B", "b" }, { "parent", new Hashtable() { { "child1", "c1" }, { "child2", "c2" } } } }
            };
            yield return new object[] {
                new HashSet<string>() { },
                new Hashtable() {{ "A", "a" }, { "B", "b" }, { "parent", new Hashtable() { { "child1", "c1" }, { "child2", "c2" } } } }
            };
            yield return new object[] {
                new HashSet<string>() { "A" },
                new Hashtable() {{ "B", "b" }, { "parent", new Hashtable() { { "child1", "c1" }, { "child2", "c2" } } } }
            };
            yield return new object[] {
                new HashSet<string>() { "B" },
                new Hashtable() {{ "A", "a" }, { "parent", new Hashtable() { { "child1", "c1" }, { "child2", "c2" } } } }
            };
            yield return new object[] {
                new HashSet<string>() { "parent" },
                new Hashtable() {{ "A", "a" }, { "B", "b" }}
            };
            yield return new object[] {
                new HashSet<string>() { "parent.child1" },
                new Hashtable() {{ "A", "a" }, { "B", "b" }, { "parent", new Hashtable() { { "child2", "c2" } } } }
            };
            yield return new object[] {
                new HashSet<string>() { "parent.child1", "parent.child2" },
                new Hashtable() {{ "A", "a" }, { "B", "b" }, { "parent", new Hashtable() { } } }
            };
        }

        public class ArbitratyReflectionObject
        {
            public string A = "a";
            public string B = "b";

            public ArbitratyReflectionSubObject parent = new ArbitratyReflectionSubObject();

            public override string ToString()
            {
                return string.Format("ArbitratyReflectionObject: A={0}, B={1}, parent={2}", A, B, parent);
            }
        }

        public class ArbitratyReflectionSubObject
        {
            public string child1 = "c1";
            public string child2 = "c2";

            public override string ToString()
            {
                return string.Format("ArbitratyReflectionObject: child1={0}, child2={1}", child1, child2);
            }
        }
    }
}
