/// <summary>
        /// Get values of all properties in specified object and returns them as a map.
        /// The object can be a user defined object, map or array.Returned properties
        /// correspondently are object properties, map key-pairs or array elements with their indexes.
        /// </summary>
        /// <param name="obj">an object to get properties from.</param>
        /// <returns>a map, containing the names of the object's properties and their values.</returns>
        public static Dictionary <string, object> GetProperties(object obj)
        {
            if (obj == null)
            {
                throw new NullReferenceException("Object cannot be null");
            }

            var map = new Dictionary <string, object>();

            if (obj is JObject)
            {
                var thisObj = (JObject)obj;
                foreach (var property in thisObj.Properties())
                {
                    map[property.Name] = property.Value;
                }
            }
            else if (obj is JArray)
            {
                var list = (JArray)obj;
                for (var index = 0; index < list.Count; index++)
                {
                    map[index.ToString()] = list[index];
                }
            }
            else if (obj is IDictionary)
            {
                var thisMap = (IDictionary)obj;
                foreach (var key in thisMap.Keys)
                {
                    map[key.ToString()] = thisMap[key];
                }
            }
            else if (obj is IDictionary <string, object> )
            {
                var thisMap = (IDictionary <string, object>)obj;
                foreach (var key in thisMap.Keys)
                {
                    map[key] = thisMap[key];
                }
            }
            else if (obj is IEnumerable)
            {
                var list  = (IEnumerable)obj;
                var index = 0;
                foreach (var value in list)
                {
                    map[index.ToString()] = value;
                    index++;
                }
            }
            else
            {
                return(PropertyReflector.GetProperties(obj));
            }

            return(map);
        }
        /// <summary>
        /// Gets names of all properties implemented in specified object.
        ///
        /// The object can be a user defined object, map or array.Returned property name
        /// correspondently are object properties, map keys or array indexes.
        /// </summary>
        /// <param name="obj">an objec to introspect.</param>
        /// <returns>a list with property names.</returns>
        public static List <string> GetPropertyNames(object obj)
        {
            if (obj == null)
            {
                throw new NullReferenceException("Object cannot be null");
            }

            var properties = new List <string>();

            if (obj is JObject)
            {
                var thisObj = (JObject)obj;
                foreach (var property in thisObj.Properties())
                {
                    properties.Add(property.Name);
                }
            }
            else if (obj is JArray)
            {
                var list = (JArray)obj;
                for (var index = 0; index < list.Count; index++)
                {
                    properties.Add(index.ToString());
                }
            }
            else if (obj is IDictionary)
            {
                var map = (IDictionary)obj;
                foreach (var key in map.Keys)
                {
                    properties.Add(key.ToString());
                }
            }
            else if (obj is IDictionary <string, object> )
            {
                var map = (IDictionary <string, object>)obj;
                foreach (var key in map.Keys)
                {
                    properties.Add(key);
                }
            }
            else if (obj is IEnumerable)
            {
                var list  = (IEnumerable)obj;
                var index = 0;
                foreach (var value in list)
                {
                    properties.Add(index.ToString());
                    index++;
                }
            }
            else
            {
                return(PropertyReflector.GetPropertyNames(obj));
            }

            return(properties);
        }
        /// <summary>
        /// Checks if object has a property with specified name.
        /// The object can be a user defined object, map or array.The property name
        /// correspondently must be object property, map key or array index.
        /// </summary>
        /// <param name="obj">an object to introspect.</param>
        /// <param name="name">a name of the property to check.</param>
        /// <returns>true if the object has the property and false if it doesn't.</returns>
        public static bool HasProperty(object obj, string name)
        {
            if (obj == null || name == null)
            {
                return(false);
            }

            var jObject = obj as JObject;

            if (jObject != null)
            {
                var thisObj = jObject;
                foreach (var property in thisObj.Properties())
                {
                    if (name.Equals(property.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        return(true);
                    }
                }
                return(false);
            }

            var jArray = obj as JArray;

            if (jArray != null)
            {
                var list  = jArray;
                var index = IntegerConverter.ToNullableInteger(name);
                return(index >= 0 && index < list.Count);
            }

            var map = obj as IDictionary;

            if (map != null)
            {
                foreach (var key in map.Keys)
                {
                    if (name.Equals(key.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        return(true);
                    }
                }
                return(false);
            }

            var genDictionary = obj as IDictionary <string, object>;

            if (genDictionary != null)
            {
                foreach (var key in genDictionary.Keys)
                {
                    if (name.Equals(key, StringComparison.OrdinalIgnoreCase))
                    {
                        return(true);
                    }
                }
                return(false);
            }

            var enumerable = obj as IEnumerable;

            if (enumerable != null)
            {
                var index       = IntegerConverter.ToNullableInteger(name);
                var genericEnum = enumerable.Cast <object>();
                return(index >= 0 && index < genericEnum.Count());
            }

            return(PropertyReflector.HasProperty(obj, name));
        }
        /// <summary>
        /// Gets value of object property specified by its name.
        /// The object can be a user defined object, map or array.The property name
        /// correspondently must be object property, map key or array index.
        /// </summary>
        /// <param name="obj">an object to read property from.</param>
        /// <param name="name">a name of the property to get.</param>
        /// <returns>the property value or null if property doesn't exist or introspection failed.</returns>
        public static object GetProperty(object obj, string name)
        {
            if (obj == null || name == null)
            {
                return(false);
            }

            name = name.ToLower();

            var jObject = obj as JObject;

            if (jObject != null)
            {
                var thisObj = jObject;
                foreach (var property in thisObj.Properties())
                {
                    if (name.Equals(property.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        return(property.Value);
                    }
                }
                return(null);
            }

            var jArray = obj as JArray;

            if (jArray != null)
            {
                var list  = jArray;
                var index = IntegerConverter.ToNullableInteger(name);
                return(index >= 0 && index < list.Count ? list[index.Value] : null);
            }

            var map = obj as IDictionary;

            if (map != null)
            {
                foreach (var key in map.Keys)
                {
                    if (name.Equals(key.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        return(map[key]);
                    }
                }
            }

            var genDictionary = obj as IDictionary <string, object>;

            if (genDictionary != null)
            {
                foreach (var key in genDictionary.Keys)
                {
                    if (name.Equals(key, StringComparison.OrdinalIgnoreCase))
                    {
                        return(genDictionary[key]);
                    }
                }
            }

            var enumerable = obj as IEnumerable;

            if (enumerable != null)
            {
                var index = IntegerConverter.ToNullableInteger(name);
                if (index >= 0)
                {
                    var list = (IEnumerable)obj;
                    foreach (var value in list)
                    {
                        if (index == 0)
                        {
                            return(value);
                        }
                        index--;
                    }
                }
                return(null);
            }

            return(PropertyReflector.GetProperty(obj, name));
        }
        /// <summary>
        /// Sets value of object property specified by its name.
        ///
        /// The object can be a user defined object, map or array.The property name
        /// correspondently must be object property, map key or array index.
        ///
        /// If the property does not exist or introspection fails this method doesn't do
        /// anything and doesn't any throw errors.
        /// </summary>
        /// <param name="obj">an object to write property to.</param>
        /// <param name="name">a name of the property to set.</param>
        /// <param name="value">a new value for the property to set.</param>
        public static void SetProperty(object obj, string name, object value)
        {
            if (obj == null)
            {
                throw new ArgumentNullException(nameof(obj), "Object cannot be null");
            }
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name), "Method name cannot be null");
            }

            var type   = obj.GetType().GetTypeInfo();
            var isDict = type.GetInterfaces().Contains(typeof(IDictionary)) || type.GetInterfaces().Contains(typeof(IDictionary <,>));

            if (isDict)
            {
                var mapObj = (IDictionary <string, object>)obj;

                foreach (var key in mapObj.Keys)
                {
                    if (name.Equals(key, StringComparison.OrdinalIgnoreCase))
                    {
                        mapObj[key] = value;
                        return;
                    }
                }

                mapObj[name] = value;
            }
            else if (obj.GetType().IsArray)
            {
                var array  = ((Array)obj);
                var length = array.Length;
                var index  = IntegerConverter.ToIntegerWithDefault(name, -1);

                if (index >= 0 && index < length)
                {
                    array.SetValue(value, index);
                }
            }
            else if (type.GetInterfaces().Contains(typeof(IList)) || type.GetInterfaces().Contains(typeof(IList <>)))
            {
                var list  = (IList <object>)obj;
                var index = IntegerConverter.ToIntegerWithDefault(name, -1);
                if (index < 0)
                {
                    return;
                }

                if (index < list.Count)
                {
                    list[index] = value;
                }
                else
                {
                    while (index - 1 >= list.Count)
                    {
                        list.Add(null);
                    }
                    list.Add(value);
                }
            }
            else
            {
                PropertyReflector.SetProperty(obj, name, value);
            }
        }