using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace InABox.Core
{
    public class StandardProperty : IProperty
    {
        public Type _class;

        private Func<object, object> _getter;

        private string _name = "";

        private bool? _calculated;

        private Action<object, object> _setter;

        public string _type = "";
        public Type _propertyType = typeof(object);

        public StandardProperty()
        {
            Editor = new NullEditor();
            HasEditor = false;
        }

        [ComboLookupEditor(typeof(StandardPropertyClassLookups))]
        public string Class
        {
            get => _class.EntityName();
            set
            {
                _class = CoreUtils.GetEntity(value);
                CheckExpressions();
            }
        }

        public string Name
        {
            get => _name;
            set
            {
                _name = value;
                CheckExpressions();
            }
        }

        [ComboLookupEditor(typeof(PropertyTypeLookups))]
        public string Type
        {
            get => _type;
            set
            {
                Type propType;
                try
                {
                    propType = CoreUtils.GetEntityOrNull(_type) ?? typeof(object);
                }
                catch
                {
                    propType = typeof(object);
                }
                PropertyType = propType; // Invoke other setter to ensure consistency of functionality
            }
        }

        public Type PropertyType
        {
            get => _propertyType;
            set
            {
                _propertyType = value;
                _type = value.EntityName();
                IsEntityLink = _propertyType.GetInterfaces().Contains(typeof(IEntityLink));
            }
        }

        public PropertyInfo Property { get; set; }

        //[ComboLookupEditor(typeof(PropertyEditorLookups))]
        public BaseEditor Editor { get; set; }

        public bool HasEditor { get; set; }

        public string Caption { get; set; }

        public long Sequence { get; set; }

        [NullEditor]
        public string Page { get; set; }

        public bool Required { get; set; }

        public LoggablePropertyAttribute? Loggable { get; set; }

        public IProperty? Parent { get; set; }

        public bool IsEntityLink { get; set; }

        public bool IsCalculated
        {
            get
            {
                if (!_calculated.HasValue)
                {
                    _calculated = Property.GetCustomAttribute<AggregateAttribute>() != null
                        || Property.GetCustomAttribute<FormulaAttribute>() != null
                        || Property.GetCustomAttribute<ConditionAttribute>() != null;
                }
                return _calculated.Value;
            }
        }

        public Expression Expression()
        {
            return CoreUtils.CreateMemberExpression(_class, Name);
        }

        public Func<object, object> Getter()
        {
            return _getter;
        }

        public Action<object, object> Setter()
        {
            return _setter;
        }

        public override string ToString()
        {
            return string.Format("{0}.{1}", Class, Name);
        }

        private void CheckExpressions()
        {
            if (_class == null)
                return;
            if (string.IsNullOrEmpty(_name))
                return;
            try
            {
                _propertyType = CoreUtils.GetProperty(_class, _name).PropertyType;
            }
            catch (Exception e)
            {
                Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
            }

            try
            {
                _getter = Expressions.Getter(_class, _name);
            }
            catch (Exception e)
            {
                Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
            }

            try
            {
                _setter = Expressions.Setter(_class, _name);
            }
            catch (Exception e)
            {
                Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
            }
        }

        private class StandardPropertyClassLookups : LookupGenerator<object>
        {
            public StandardPropertyClassLookups(object[] items) : base(items)
            {
                var types = CoreUtils.TypeList(
                    AppDomain.CurrentDomain.GetAssemblies(),
                    myType =>
                        myType.IsClass
                        && !myType.IsAbstract
                        && myType.IsSubclassOf(typeof(BaseEditor))
                );
                foreach (var type in types)
                    AddValue(type.EntityName(), type.Name);
            }
        }
    }
}