﻿using System;
using System.Reflection;
using RocketPlus.Exception;

namespace RocketPlus.Util
{
    public static class ReflectionUtils
    {
        public static T GetField<T>(Object o, String fieldName)
        {
            var field = o.GetType()
                .GetField(fieldName, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
            if (field == null)
            {
                throw new FieldNotFoundException(o.GetType().FullName, fieldName);
            }

            object value = field.GetValue(o);
           
            if (value.GetType() != typeof (T))
            {
                throw new InvalidFieldCastException(o.GetType().FullName, fieldName, typeof(T).Name, value.GetType().Name);
            }

            return (T) value;
        }

        public static T GetStaticField<T>(Type type, String fieldName)
        {
            FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);

            if (fieldInfo == null)
            {
                throw new FieldNotFoundException(type.FullName, fieldName, true);
            }

            object value = fieldInfo.GetValue(null);
            
            if (value.GetType() != typeof(T))
            {
                throw new InvalidFieldCastException(type.FullName, fieldName, typeof(T).Name, value.GetType().Name, true);
            }

            return (T) value;
        }

        public static void SetStaticField(Type type, String fieldName, Object value)
        {
            var fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);

            if (fieldInfo == null)
            {
                throw new FieldNotFoundException(type.FullName, fieldName, true);
            } 

            fieldInfo.SetValue(null, value);
        }


        public static void SetField(Object o, String fieldName, Object value)
        {
            var fieldInfo = o.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);

            if (fieldInfo == null)
            {
                throw new FieldNotFoundException(o.GetType().FullName, fieldName, true);
            }
 
            fieldInfo.SetValue(o, value);
        }

        public static Object InvokePrivateMethod(Object o, String methodName, params object[] methodParams)
        {
            if (methodParams == null) methodParams = new object[0];
            MethodInfo dynMethod = o.GetType().GetMethod(methodName,
                BindingFlags.NonPublic | BindingFlags.Instance);
            if (dynMethod == null)
            {
                throw new MethodNotFoundException(o.GetType().ToString(), methodName);
            }
            return dynMethod.Invoke(o, methodParams);
        }
    }
}