using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using static InABox.Core.IFilter;

namespace InABox.Core
{
    public interface ISubQuery
    {
        Type GetQueryType();

        IFilter? GetFilter();

        IColumn GetColumn();
    }

    public class SubQuery<T> : ISubQuery
    {
        public SubQuery()
        {
            Filter = null;
            Column = null;
        }

        public SubQuery(Filter<T>? filter, Column<T> column)
        {
            Filter = filter;
            Column = column;
        }

        public Filter<T>? Filter { get; set; }
        public Column<T> Column { get; set; }

        public Type GetQueryType() => typeof(T);

        public IFilter? GetFilter() => Filter;

        public IColumn GetColumn() => Column;

        public override string ToString()
        {
            return $"{typeof(T).Name}.{Column} WHERE {Filter?.ToString()}";
        }
    }

    public class SubQuerySerializationWrapper
    {
        public SubQuerySerializationWrapper(string type, string subquery)
        {
            Type = type;
            SerializedSubquery = subquery;
        }

        public string Type { get; set; }
        public string SerializedSubquery { get; set; }
    }


    // !!! If updating this list, make sure to update everywhere this list is used! That includes SQLiteProvider, the ToString() functions,
    // the FilterExtensions.MatchOperator, but possible otherwhere to; make sure everything is updated.
    public enum Operator
    {
        IsEqualTo,
        IsNotEqualTo,
        IsGreaterThan,
        IsGreaterThanOrEqualTo,
        IsLessThan,
        IsLessThanOrEqualTo,
        BeginsWith,
        Contains,
        EndsWith,
        InList,
        NotInList,
        InQuery,
        All,
        None,
        NotInQuery,
        Regex,
        Not = byte.MaxValue
    }

    public enum ListOperator
    {
        Includes,
        Excludes
    }

    // !!! If updating this list, make sure to update everywhere this list is
    // used! That includes SQLiteProvider the FilterConstants.Resolve,
    // but possible otherwhere to; make sure everything is updated.
    public enum FilterConstant
    {
        Today,
        Now,
        Zero,
        OneWeekAgo,
        OneWeekAhead,
        OneMonthAgo,
        OneMonthAhead,
        ThreeMonthsAgo,
        ThreeMonthsAhead,
        SixMonthsAgo,
        SixMonthsAhead,
        OneYearAgo,
        OneYearAhead,
        StartOfLastWeek,
        EndOfLastWeek,
        StartOfThisWeek,
        EndOfThisWeek,
        StartOfNextWeek,
        EndOfNextWeek,
        StartOfLastMonth,
        EndOfLastMonth,
        StartOfThisMonth,
        EndOfThisMonth,
        StartOfNextMonth,
        EndOfNextMonth,
        StartOfLastCalendarYear,
        EndOfLastCalendarYear,
        StartOfThisCalendarYear,
        EndOfThisCalendarYear,
        StartOfNextCalendarYear,
        EndOfNextCalendarYear,
        StartOfLastFinancialYear,
        EndOfLastFinancialYear,
        StartOfThisFinancialYear,
        EndOfThisFinancialYear,
        StartOfNextFinancialYear,
        EndOfNextFinancialYear,
        Null
    }

    public static class FilterConstants
    {
        private static Operator[] SimpleOperators = new Operator[]
        {
            Operator.IsEqualTo,
            Operator.IsNotEqualTo,
            Operator.IsGreaterThan,
            Operator.IsGreaterThanOrEqualTo,
            Operator.IsLessThan,
            Operator.IsLessThanOrEqualTo
        };
        
        public static FilterConstant[] Constants(Type type, Operator op, bool aggregate = false)
        {
            if (type == typeof(Guid) && (op == Operator.IsEqualTo || op == Operator.IsNotEqualTo))
                return new[] { FilterConstant.Null };

            if ((type == typeof(DateTime)) && SimpleOperators.Contains(op))
                return new[]
                {
                    FilterConstant.Null,
                    FilterConstant.Today, 
                    FilterConstant.Now,       
                    FilterConstant.OneWeekAgo,
                    FilterConstant.OneWeekAhead,
                    FilterConstant.OneMonthAgo,
                    FilterConstant.OneMonthAhead,
                    FilterConstant.ThreeMonthsAgo,
                    FilterConstant.ThreeMonthsAhead,
                    FilterConstant.SixMonthsAgo,
                    FilterConstant.SixMonthsAhead,
                    FilterConstant.OneYearAgo,
                    FilterConstant.OneYearAhead,
                    FilterConstant.StartOfLastWeek,
                    FilterConstant.EndOfLastWeek,
                    FilterConstant.StartOfThisWeek,
                    FilterConstant.EndOfThisWeek,
                    FilterConstant.StartOfNextWeek,
                    FilterConstant.EndOfNextWeek,
                    FilterConstant.StartOfLastMonth,
                    FilterConstant.EndOfLastMonth,
                    FilterConstant.StartOfThisMonth,
                    FilterConstant.EndOfThisMonth,
                    FilterConstant.StartOfNextMonth,
                    FilterConstant.EndOfNextMonth,
                    FilterConstant.StartOfLastCalendarYear,
                    FilterConstant.EndOfLastCalendarYear,
                    FilterConstant.StartOfThisCalendarYear,
                    FilterConstant.EndOfThisCalendarYear,
                    FilterConstant.StartOfNextCalendarYear,
                    FilterConstant.EndOfNextCalendarYear,
                    FilterConstant.StartOfLastFinancialYear,
                    FilterConstant.EndOfLastFinancialYear,
                    FilterConstant.StartOfThisFinancialYear,
                    FilterConstant.EndOfThisFinancialYear,                    
                    FilterConstant.StartOfNextFinancialYear,
                    FilterConstant.EndOfNextFinancialYear
                };

            if (type.IsNumeric() && SimpleOperators.Contains(op))
            {
                return aggregate 
                    ? new[] { FilterConstant.Null }
                    : new [] { FilterConstant.Zero };
            }

            return new FilterConstant[] { };
        }

        public static object? Resolve(this FilterConstant constant, Type type)
        {
            return constant switch
            {
                FilterConstant.Null => CoreUtils.GetDefault(type),
                FilterConstant.Today => DateTime.Today,
                FilterConstant.Now => DateTime.Now,
                FilterConstant.Zero => CoreUtils.GetDefault(type),
                FilterConstant.OneWeekAgo => DateTime.Today.AddDays(-7),
                FilterConstant.OneWeekAhead => DateTime.Today.AddDays(7),
                FilterConstant.OneMonthAgo => DateTime.Today.AddMonths(-1),
                FilterConstant.OneMonthAhead => DateTime.Today.AddMonths(1),
                FilterConstant.ThreeMonthsAgo => DateTime.Today.AddMonths(-3),
                FilterConstant.ThreeMonthsAhead => DateTime.Today.AddMonths(3),
                FilterConstant.SixMonthsAgo => DateTime.Today.AddMonths(-6),
                FilterConstant.SixMonthsAhead => DateTime.Today.AddMonths(6),
                FilterConstant.OneYearAgo => DateTime.Today.AddYears(-1),
                FilterConstant.OneYearAhead => DateTime.Today.AddYears(1),
                FilterConstant.StartOfLastWeek => DateTime.Today.StartOfWeek(DayOfWeek.Monday).AddDays(-7),
                FilterConstant.EndOfLastWeek => DateTime.Today.StartOfWeek(DayOfWeek.Monday).AddTicks(-1),
                FilterConstant.StartOfThisWeek => DateTime.Today.StartOfWeek(DayOfWeek.Monday),
                FilterConstant.EndOfThisWeek => DateTime.Today.StartOfWeek(DayOfWeek.Monday).AddDays(7).AddTicks(-1),
                FilterConstant.StartOfNextWeek => DateTime.Today.StartOfWeek(DayOfWeek.Monday).AddDays(7),
                FilterConstant.EndOfNextWeek => DateTime.Today.StartOfWeek(DayOfWeek.Monday).AddDays(14).AddTicks(-1),
                FilterConstant.StartOfLastMonth => DateTime.Today.StartOfMonth().AddMonths(-1),
                FilterConstant.EndOfLastMonth => DateTime.Today.StartOfMonth().AddTicks(-1),
                FilterConstant.StartOfThisMonth => DateTime.Today.StartOfMonth(),
                FilterConstant.EndOfThisMonth => DateTime.Today.StartOfMonth().AddMonths(1).AddTicks(-1),
                FilterConstant.StartOfNextMonth => DateTime.Today.StartOfMonth().AddMonths(1),
                FilterConstant.EndOfNextMonth => DateTime.Today.StartOfMonth().AddMonths(2).AddTicks(-1),
                FilterConstant.StartOfLastCalendarYear => DateTime.Today.StartOfYear().AddYears(-1),
                FilterConstant.EndOfLastCalendarYear => DateTime.Today.StartOfYear().AddTicks(-1),
                FilterConstant.StartOfThisCalendarYear => DateTime.Today.StartOfYear(),
                FilterConstant.EndOfThisCalendarYear => DateTime.Today.StartOfYear().AddYears(1).AddTicks(-1),
                FilterConstant.StartOfNextCalendarYear => DateTime.Today.StartOfYear().AddYears(1),
                FilterConstant.EndOfNextCalendarYear => DateTime.Today.StartOfYear().AddYears(2).AddTicks(-1),
                FilterConstant.StartOfLastFinancialYear => DateTime.Today.AddMonths(6).StartOfYear().AddMonths(-18),
                FilterConstant.EndOfLastFinancialYear => DateTime.Today.AddMonths(6).StartOfYear().AddMonths(-6).AddTicks(-1),
                FilterConstant.StartOfThisFinancialYear => DateTime.Today.AddMonths(6).StartOfYear().AddMonths(-6),
                FilterConstant.EndOfThisFinancialYear => DateTime.Today.AddMonths(6).StartOfYear().AddMonths(6).AddTicks(-1),
                FilterConstant.StartOfNextFinancialYear => DateTime.Today.AddMonths(6).StartOfYear().AddMonths(6),
                FilterConstant.EndOfNextFinancialYear => DateTime.Today.AddMonths(6).StartOfYear().AddMonths(18).AddTicks(-1),
                _ => throw new Exception($"Cannot handle {constant}!"),
            };
        }
    }

    public class CustomFilterValue
    {
        public byte[] Data { get; set; }

        public CustomFilterValue(byte[] data)
        {
            Data = data;
        }

        public override string ToString()
        {
            return "<Custom Value>";
        }
    }

    public interface IFilter : ISerializeBinary
    {
        string Property { get; set; }
        Type Type { get; }

        Operator Operator { get; set; }
        object? Value { get; set; }

        bool IsNot { get; set; }

        /// <summary>
        /// List of filters to apply to the main filter with boolean operator AND. The <see cref="Ands"/> take precedence over the <see cref="Ors"/>.
        /// </summary>
        IEnumerable<IFilter> Ands { get; }
        /// <summary>
        /// List of filters to apply to the main filter with boolean operator OR. The <see cref="Ands"/> take precedence over the <see cref="Ors"/>.
        /// </summary>
        IEnumerable<IFilter> Ors { get; }

        public IFilter And(IFilter filter);
        public IFilter And<T>(Expression<Func<T,object>> expression);
        public IFilter And(string property);

        public IFilter Or(IFilter filter);
        public IFilter Or<T>(Expression<Func<T,object>> expression);
        
        IFilter IsEqualTo(bool value);
        IFilter IsEqualTo(string value);
        IFilter IsEqualTo(Guid value);
        IFilter IsEqualTo(int value);
        IFilter IsEqualTo(DateTime value);
        IFilter IsEqualTo(TimeSpan value);
        IFilter IsEqualTo<TEnum>(TEnum value) where TEnum : struct, IConvertible, IComparable, IFormattable;
        IFilter IsEqualTo(object value);
        
        IFilter IsNotEqualTo(bool value);
        IFilter IsNotEqualTo(string value);
        IFilter IsNotEqualTo(Guid value);
        IFilter IsNotEqualTo(int value);
        IFilter IsNotEqualTo(DateTime value);
        IFilter IsNotEqualTo(TimeSpan value);
        IFilter IsNotEqualTo<TEnum>(TEnum value) where TEnum : struct, IConvertible, IComparable, IFormattable;
        IFilter IsNotEqualTo(object value);
        
        IFilter IsGreaterThan(bool value);
        IFilter IsGreaterThan(string value);
        IFilter IsGreaterThan(Guid value);
        IFilter IsGreaterThan(int value);
        IFilter IsGreaterThan(DateTime value);
        IFilter IsGreaterThan(TimeSpan value);
        IFilter IsGreaterThan<TEnum>(TEnum value) where TEnum : struct, IConvertible, IComparable, IFormattable;
        IFilter IsGreaterThan(object value);
        
        IFilter IsGreaterThanOrEqualTo(bool value);
        IFilter IsGreaterThanOrEqualTo(string value);
        IFilter IsGreaterThanOrEqualTo(Guid value);
        IFilter IsGreaterThanOrEqualTo(int value);
        IFilter IsGreaterThanOrEqualTo(DateTime value);
        IFilter IsGreaterThanOrEqualTo(TimeSpan value);
        IFilter IsGreaterThanOrEqualTo<TEnum>(TEnum value) where TEnum : struct, IConvertible, IComparable, IFormattable;
        IFilter IsGreaterThanOrEqualTo(object value);
        
        IFilter IsLessThan(bool value);
        IFilter IsLessThan(string value);
        IFilter IsLessThan(Guid value);
        IFilter IsLessThan(int value);
        IFilter IsLessThan(DateTime value);
        IFilter IsLessThan(TimeSpan value);
        IFilter IsLessThan<TEnum>(TEnum value) where TEnum : struct, IConvertible, IComparable, IFormattable;
        IFilter IsLessThan(object value);
         
        IFilter IsLessThanOrEqualTo(bool value);
        IFilter IsLessThanOrEqualTo(string value);
        IFilter IsLessThanOrEqualTo(Guid value);
        IFilter IsLessThanOrEqualTo(int value);
        IFilter IsLessThanOrEqualTo(DateTime value);
        IFilter IsLessThanOrEqualTo(TimeSpan value);
        IFilter IsLessThanOrEqualTo<TEnum>(TEnum value) where TEnum : struct, IConvertible, IComparable, IFormattable;
        IFilter IsLessThanOrEqualTo(object value);    
        
        IFilter InList(params bool[] value);
        IFilter InList(params string[] value);
        IFilter InList(params Guid[] value);
        IFilter InList(params int[] value);
        IFilter InList(params DateTime[] value);
        IFilter InList(params TimeSpan[] value);
        IFilter InList<TEnum>(params TEnum[] value) where TEnum : struct, IConvertible, IComparable, IFormattable;
        IFilter InList(params object[] value);    
        
        IFilter NotInList(params bool[] value);
        IFilter NotInList(params string[] value);
        IFilter NotInList(params Guid[] value);
        IFilter NotInList(params int[] value);
        IFilter NotInList(params DateTime[] value);
        IFilter NotInList(params TimeSpan[] value);
        IFilter NotInList<TEnum>(params TEnum[] value) where TEnum : struct, IConvertible, IComparable, IFormattable;
        IFilter NotInList(params object[] value);   
        
        IFilter BeginsWith(object value);   
        IFilter BeginsWith(string value); 
        
        IFilter Contains(object value);   
        IFilter Contains(string value);
        
        IFilter EndsWith(object value);   
        IFilter EndsWith(string value);

        IFilter InQuery<U>(SubQuery<U> value);
        IFilter InQuery<U>(Filter<U>? filter, Expression<Func<U, object?>> column);

        IFilter NotInQuery<U>(SubQuery<U> value);
        IFilter NotInQuery<U>(Filter<U>? filter, Expression<Func<U, object?>> column);
        
        IFilter Regex(string regex);

        IFilter All();
        IFilter None();

        IFilter Not();

        string AsOData();

        IEnumerable<string> ColumnNames();

        public delegate object? ConvertCustomValueFunction(IFilter filter, CustomFilterValue value);

        /// <summary>
        /// Walk through the filter and convert all values which are <see cref="CustomFilterValue"/> to normal objects.
        /// </summary>
        /// <param name="convert">A function used to convert each value.</param>
        public void ConvertCustomValues(ConvertCustomValueFunction convert);
    }

    public static class Filter
    {
        public static IFilter Create<T>(Type concrete, Expression<Func<T,object>> expression)
        {
            if (!typeof(T).IsAssignableFrom(concrete))
                throw new Exception($"Filter: {concrete.EntityName()} does not implement {typeof(T).EntityName()}");
            var type = typeof(Filter<>).MakeGenericType(concrete);
            var property = CoreUtils.GetFullPropertyName(expression,".");
            return (Activator.CreateInstance(type, property) as IFilter)!;            
        }
        public static IFilter Create(Type concrete)
        {
            var type = typeof(Filter<>).MakeGenericType(concrete);
            return (Activator.CreateInstance(type) as IFilter)!;
        }
        public static IFilter Create(Type concrete, string property)
        {
            var type = typeof(Filter<>).MakeGenericType(concrete);
            return (Activator.CreateInstance(type, property) as IFilter)!;
        }

        public static IFilter Create(Type concrete, string property, Operator op, object value)
        {
            var type = typeof(Filter<>).MakeGenericType(concrete);
            var result = (Activator.CreateInstance(type, property) as IFilter)!;
            result.Operator = op;
            result.Value = value;
            return result;
        }

        public static Filter<T> All<T>() => new Filter<T>().All();

        public static Filter<T> None<T>() => new Filter<T>().None();
    }

    public class Filter<T> : IFilter
    {
        public Type Type => PropertyDefinition?.PropertyType ?? typeof(object);

        private string _property;
        public string Property
        {
            get => _property;
            set
            {
                _property = value;
                _propertyDefinition = null;
            }
        }

        private IProperty? _propertyDefinition;
        public IProperty? PropertyDefinition
        {
            get
            {
                if (Property.IsNullOrWhiteSpace())
                {
                    return null;
                }
                else
                {
                    _propertyDefinition ??= DatabaseSchema.PropertyStrict(typeof(T), Property);
                    return _propertyDefinition;
                }
            }
            set
            {
                _propertyDefinition = value;
                if(value != null)
                {
                    Property = value.Name;
                }
            }
        }
        
        public Filter<T>? Parent { get; set; }

        /// <summary>
        /// List of filters to apply to the main filter with boolean operator AND. The <see cref="Ands"/> take precedence over the <see cref="Ors"/>.
        /// </summary>
        public List<Filter<T>> Ands { get; private set; }

        /// <summary>
        /// List of filters to apply to the main filter with boolean operator OR. The <see cref="Ands"/> take precedence over the <see cref="Ors"/>.
        /// </summary>
        public List<Filter<T>> Ors { get; private set; }

        public Operator Operator { get; set; }
        
        public object? Value { get; set; }

        public bool IsNot { get; set; }

        IEnumerable<IFilter> IFilter.Ands => Ands;

        IEnumerable<IFilter> IFilter.Ors => Ors;

        #region And

        public IFilter And<T1>(Expression<Func<T1, object>> expression)
        {
            return And(CoreUtils.GetFullPropertyName(expression, "."));
        }

        public IFilter And(IFilter filter)
        {
            if (filter is Filter<T> filterT)
                return And(filterT);
            throw new Exception($"Cannot add filter of type {filter.GetType()} to Filter<{typeof(T)}>");
        }

        IFilter IFilter.And(string property) => And(property);

        public Filter<T> And(Filter<T> filter)
        {
            filter.Parent = this;
            Ands.Add(filter);
            return this;
        }
        

        public Filter<T> And(Expression<Func<T, object?>> expression)
        {
            var and = new Filter<T>(expression);
            and.Parent = this;
            Ands.Add(and);
            return and;
        }

        public Filter<T> And(string property)
        {
            var and = new Filter<T>(property);
            and.Parent = this;
            Ands.Add(and);
            return and;
        }

        public static Filter<T>? And(params Filter<T>?[] filters)
        {
            Filter<T>? result = null;
            foreach (var filter in filters)
            {
                if(filter != null)
                {
                    if (result is null)
                        result = filter;
                    else
                        result.Ands.Add(filter);
                }
            }

            return result;
        }

        #endregion

        public Filter<T> TextSearch(string terms, params Expression<Func<T, string>>[] expressions)
        {
            //String[] comps = terms.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            //foreach (var comp in comps)
            //{
            //    Filter<T> search = null;
            //    foreach (var expression in expressions)
            //        search = search == null ? new Filter<T>(expression).Contains(comp.Trim()) : search.Or(expression).Contains(comp.Trim());
            //    Ands.Add(search);
            //}            
            Filter<T>? search = null;
            foreach (var expression in expressions)
                search = search == null
                    ? new Filter<T>(CoreUtils.GetFullPropertyName(expression, ".")).Contains(terms.Trim())
                    : search.Or(CoreUtils.GetFullPropertyName(expression, ".")).Contains(terms.Trim());
            if(search != null)
                Ands.Add(search);
            return this;
        }

        #region Or

        public IFilter Or<T1>(Expression<Func<T1, object>> expression)
        {
            return Or(CoreUtils.GetFullPropertyName(expression, "."));
        }

        public IFilter Or(IFilter filter)
        {
            if (filter is Filter<T> filterT)
                return Or(filterT);
            throw new Exception($"Cannot add filter of type {filter.GetType()} to Filter<{typeof(T)}>");
        }

        public Filter<T> Or(Filter<T> filter)
        {
            filter.Parent = this;
            Ors.Add(filter);
            return this;
        }

        public Filter<T> Or(Expression<Func<T, object?>> expression)
        {
            var or = new Filter<T>(expression);
            or.Parent = this;
            Ors.Add(or);
            return or;
        }

        public Filter<T> Or(string property)
        {
            var or = new Filter<T>(property);
            or.Parent = this;
            Ors.Add(or);
            return or;
        }

        #endregion

        #region ApplyOperator

        private Filter<T> ApplyOperator<TValue>(Operator action, TValue value) where TValue : struct
        {
            Operator = action;
            Value = value;
            return Parent ?? this;
        }

        private Filter<T> ApplyOperator<TValue>(Operator action, TValue[] value) where TValue : struct
        {
            Operator = action;
            Value = value;
            return Parent ?? this;
        }

        private Filter<T> ApplyEnumOperator<TValue>(Operator action, TValue value) where TValue : struct, IConvertible, IComparable, IFormattable
        {
            Operator = action;
            Value = value;
            return Parent ?? this;
        }

        private Filter<T> ApplyEnumOperator<TValue>(Operator action, TValue[] value) where TValue : struct, IConvertible, IComparable, IFormattable
        {
            Operator = action;
            Value = value;
            return Parent ?? this;
        }

        private Filter<T> ApplyStringOperator(Operator action, string value)
        {
            Operator = action;
            Value = value;
            return Parent ?? this;
        }

        private Filter<T> ApplyStringOperator(Operator action, string[] value)
        {
            Operator = action;
            Value = value;
            return Parent ?? this;
        }

        private Filter<T> ApplyObjectOperator(Operator action, object? value)
        {
            Operator = action;
            Value = value;
            return Parent ?? this;
        }

        private Filter<T> ApplyObjectOperator(Operator action, object[] value)
        {
            Operator = action;
            Value = value;
            return Parent ?? this;
        }

        private Filter<T> ApplyNullOperator(Operator action)
        {
            Operator = action;
            Value = null;
            return Parent ?? this;
        }

        #endregion

        #region Constructors

        public Filter()
        {
            Ands = new List<Filter<T>>();
            Ors = new List<Filter<T>>();

            _property = "";
        }

        public Filter(Column<T> column) : this()
        {
            _property = column.Property;
            _propertyDefinition = column.PropertyDefinition;
        }

        public Filter(Expression<Func<T, object?>> expression) : this()
        {
            _property = CoreUtils.GetFullPropertyName(expression, ".");
        }

        public Filter(IProperty property) : this()
        {
            _property = property.Name;
            _propertyDefinition = property;
        }

        public Filter(string property) : this()
        {
            _property = property;
        }

        public Filter(string property, Operator op, object value) : this(property)
        {
            Operator = op;
            Value = value;
        }

        public static explicit operator Filter<T>?(Filter<Entity>? v)
        {
            if (v == null)
                return null;

            //Filter<T> result = new Filter<T>();
            var json = Serialization.Serialize(v);
            json = json.Replace(typeof(Entity).EntityName(), typeof(T).EntityName());
            var result = Serialization.Deserialize<Filter<T>>(json);
            //IFormatterConverter formatter = new System.Runtime.Serialization.FormatterConverter()
            //SerializationInfo info = new SerializationInfo(typeof(T), );
            //v.Serialize(info, new StreamingContext());
            //result.Deserialize(info, new StreamingContext());
            return result;
        }

        #endregion

        #region Casting

        public Filter<TNew> Cast<TNew>()
            where TNew : T
        {
            var filter = new Filter<TNew>();
            filter._property = _property;
            filter._propertyDefinition = _propertyDefinition;

            filter.Operator = Operator;
            filter.Value = Value;
            filter.IsNot = IsNot;
            foreach(var and in Ands)
            {
                filter.And(and.Cast<TNew>());
            }
            foreach(var or in Ors)
            {
                filter.Or(or.Cast<TNew>());
            }
            return filter;
        }

        #endregion

        #region IsEqualTo

        public Filter<T> IsEqualTo(bool value) => ApplyOperator(Operator.IsEqualTo, value);
        IFilter IFilter.IsEqualTo(bool value) => IsEqualTo(value);
        
        public Filter<T> IsEqualTo(string value) => ApplyStringOperator(Operator.IsEqualTo, value);
        IFilter IFilter.IsEqualTo(string value) => IsEqualTo(value);
        
        public Filter<T> IsEqualTo(Guid value) => ApplyOperator(Operator.IsEqualTo, value);
        IFilter IFilter.IsEqualTo(Guid value) => IsEqualTo(value);
        
        public Filter<T> IsEqualTo(int value) => ApplyOperator(Operator.IsEqualTo, value);
        IFilter IFilter.IsEqualTo(int value) => IsEqualTo(value);
        
        public Filter<T> IsEqualTo(DateTime value) =>  ApplyOperator(Operator.IsEqualTo, value);
        IFilter IFilter.IsEqualTo(DateTime value) => IsEqualTo(value);

        public Filter<T> IsEqualTo(TimeSpan value) => ApplyOperator(Operator.IsEqualTo, value);
        IFilter IFilter.IsEqualTo(TimeSpan value) => IsEqualTo(value);
        
        public Filter<T> IsEqualTo<TEnum>(TEnum value) 
            where TEnum : struct, IConvertible, IComparable, IFormattable
            => ApplyEnumOperator(Operator.IsEqualTo, value);
        IFilter IFilter.IsEqualTo<TEnum>(TEnum value) 
            where TEnum : struct 
            => IsEqualTo<TEnum>(value);
        
        public Filter<T> IsEqualTo(object? value) => ApplyObjectOperator(Operator.IsEqualTo, value);
        IFilter IFilter.IsEqualTo(object? value) => IsEqualTo(value);

        #endregion

        #region IsNotEqualTo

        public Filter<T> IsNotEqualTo(bool value) =>  ApplyOperator(Operator.IsNotEqualTo, value);
        IFilter IFilter.IsNotEqualTo(bool value) => IsNotEqualTo(value);
        
        public Filter<T> IsNotEqualTo(string value) => ApplyStringOperator(Operator.IsNotEqualTo, value);
        IFilter IFilter.IsNotEqualTo(string value) => IsNotEqualTo(value);
        
        public Filter<T> IsNotEqualTo(Guid value) => ApplyOperator(Operator.IsNotEqualTo, value);
        IFilter IFilter.IsNotEqualTo(Guid value) => IsNotEqualTo(value);
        
        public Filter<T> IsNotEqualTo(int value) => ApplyOperator(Operator.IsNotEqualTo, value);
        IFilter IFilter.IsNotEqualTo(int value) => IsNotEqualTo(value);
        
        public Filter<T> IsNotEqualTo(DateTime value) =>  ApplyOperator(Operator.IsNotEqualTo, value);
        IFilter IFilter.IsNotEqualTo(DateTime value) => IsNotEqualTo(value);

        public Filter<T> IsNotEqualTo(TimeSpan value) => ApplyOperator(Operator.IsNotEqualTo, value);
        IFilter IFilter.IsNotEqualTo(TimeSpan value) => IsNotEqualTo(value);
        
        public Filter<T> IsNotEqualTo<TEnum>(TEnum value) 
            where TEnum : struct, IConvertible, IComparable, IFormattable
            => ApplyEnumOperator(Operator.IsNotEqualTo, value);
        IFilter IFilter.IsNotEqualTo<TEnum>(TEnum value) 
            where TEnum : struct 
            => IsNotEqualTo<TEnum>(value);
        
        public Filter<T> IsNotEqualTo(object value) => ApplyObjectOperator(Operator.IsNotEqualTo, value);
        IFilter IFilter.IsNotEqualTo(object value) => IsNotEqualTo(value);
        
        #endregion

        #region IsGreaterThan

        public Filter<T> IsGreaterThan(bool value) =>  ApplyOperator(Operator.IsGreaterThan, value);
        IFilter IFilter.IsGreaterThan(bool value) => IsGreaterThan(value);
        
        public Filter<T> IsGreaterThan(string value) => ApplyStringOperator(Operator.IsGreaterThan, value);
        IFilter IFilter.IsGreaterThan(string value) => IsGreaterThan(value);
        
        public Filter<T> IsGreaterThan(Guid value) => ApplyOperator(Operator.IsGreaterThan, value);
        IFilter IFilter.IsGreaterThan(Guid value) => IsGreaterThan(value);
        
        public Filter<T> IsGreaterThan(int value) => ApplyOperator(Operator.IsGreaterThan, value);
        IFilter IFilter.IsGreaterThan(int value) => IsGreaterThan(value);
        
        public Filter<T> IsGreaterThan(DateTime value) =>  ApplyOperator(Operator.IsGreaterThan, value);
        IFilter IFilter.IsGreaterThan(DateTime value) => IsGreaterThan(value);

        public Filter<T> IsGreaterThan(TimeSpan value) => ApplyOperator(Operator.IsGreaterThan, value);
        IFilter IFilter.IsGreaterThan(TimeSpan value) => IsGreaterThan(value);
        
        public Filter<T> IsGreaterThan<TEnum>(TEnum value) 
            where TEnum : struct, IConvertible, IComparable, IFormattable
            => ApplyEnumOperator(Operator.IsGreaterThan, value);
        IFilter IFilter.IsGreaterThan<TEnum>(TEnum value) 
            where TEnum : struct 
            => IsGreaterThan<TEnum>(value);
        
        public Filter<T> IsGreaterThan(object value) => ApplyObjectOperator(Operator.IsGreaterThan, value);
        IFilter IFilter.IsGreaterThan(object value) => IsGreaterThan(value);

        #endregion

        #region IsGreaterThanOrEqualTo
        
        public Filter<T> IsGreaterThanOrEqualTo(bool value) =>  ApplyOperator(Operator.IsGreaterThanOrEqualTo, value);
        IFilter IFilter.IsGreaterThanOrEqualTo(bool value) => IsGreaterThanOrEqualTo(value);
        
        public Filter<T> IsGreaterThanOrEqualTo(string value) => ApplyStringOperator(Operator.IsGreaterThanOrEqualTo, value);
        IFilter IFilter.IsGreaterThanOrEqualTo(string value) => IsGreaterThanOrEqualTo(value);
        
        public Filter<T> IsGreaterThanOrEqualTo(Guid value) => ApplyOperator(Operator.IsGreaterThanOrEqualTo, value);
        IFilter IFilter.IsGreaterThanOrEqualTo(Guid value) => IsGreaterThanOrEqualTo(value);
        
        public Filter<T> IsGreaterThanOrEqualTo(int value) => ApplyOperator(Operator.IsGreaterThanOrEqualTo, value);
        IFilter IFilter.IsGreaterThanOrEqualTo(int value) => IsGreaterThanOrEqualTo(value);
        
        public Filter<T> IsGreaterThanOrEqualTo(DateTime value) =>  ApplyOperator(Operator.IsGreaterThanOrEqualTo, value);
        IFilter IFilter.IsGreaterThanOrEqualTo(DateTime value) => IsGreaterThanOrEqualTo(value);

        public Filter<T> IsGreaterThanOrEqualTo(TimeSpan value) => ApplyOperator(Operator.IsGreaterThanOrEqualTo, value);
        IFilter IFilter.IsGreaterThanOrEqualTo(TimeSpan value) => IsGreaterThanOrEqualTo(value);
        
        public Filter<T> IsGreaterThanOrEqualTo<TEnum>(TEnum value) 
            where TEnum : struct, IConvertible, IComparable, IFormattable
            => ApplyEnumOperator(Operator.IsGreaterThanOrEqualTo, value);
        IFilter IFilter.IsGreaterThanOrEqualTo<TEnum>(TEnum value) 
            where TEnum : struct 
            => IsGreaterThanOrEqualTo<TEnum>(value);
        
        public Filter<T> IsGreaterThanOrEqualTo(object value) => ApplyObjectOperator(Operator.IsGreaterThanOrEqualTo, value);
        IFilter IFilter.IsGreaterThanOrEqualTo(object value) => IsGreaterThanOrEqualTo(value);

        #endregion

        #region IsLessThan

        public Filter<T> IsLessThan(bool value) =>  ApplyOperator(Operator.IsLessThan, value);
        IFilter IFilter.IsLessThan(bool value) => IsLessThan(value);
        
        public Filter<T> IsLessThan(string value) => ApplyStringOperator(Operator.IsLessThan, value);
        IFilter IFilter.IsLessThan(string value) => IsLessThan(value);
        
        public Filter<T> IsLessThan(Guid value) => ApplyOperator(Operator.IsLessThan, value);
        IFilter IFilter.IsLessThan(Guid value) => IsLessThan(value);
        
        public Filter<T> IsLessThan(int value) => ApplyOperator(Operator.IsLessThan, value);
        IFilter IFilter.IsLessThan(int value) => IsLessThan(value);
        
        public Filter<T> IsLessThan(DateTime value) =>  ApplyOperator(Operator.IsLessThan, value);
        IFilter IFilter.IsLessThan(DateTime value) => IsLessThan(value);

        public Filter<T> IsLessThan(TimeSpan value) => ApplyOperator(Operator.IsLessThan, value);
        IFilter IFilter.IsLessThan(TimeSpan value) => IsLessThan(value);
        
        public Filter<T> IsLessThan<TEnum>(TEnum value) 
            where TEnum : struct, IConvertible, IComparable, IFormattable
            => ApplyEnumOperator(Operator.IsLessThan, value);
        IFilter IFilter.IsLessThan<TEnum>(TEnum value) 
            where TEnum : struct 
            => IsLessThan<TEnum>(value);
        
        public Filter<T> IsLessThan(object value) => ApplyObjectOperator(Operator.IsLessThan, value);
        IFilter IFilter.IsLessThan(object value) => IsLessThan(value);

        #endregion

        #region IsLessThanOrEqualTo
        
        public Filter<T> IsLessThanOrEqualTo(bool value) =>  ApplyOperator(Operator.IsLessThanOrEqualTo, value);
        IFilter IFilter.IsLessThanOrEqualTo(bool value) => IsLessThanOrEqualTo(value);
        
        public Filter<T> IsLessThanOrEqualTo(string value) => ApplyStringOperator(Operator.IsLessThanOrEqualTo, value);
        IFilter IFilter.IsLessThanOrEqualTo(string value) => IsLessThanOrEqualTo(value);
        
        public Filter<T> IsLessThanOrEqualTo(Guid value) => ApplyOperator(Operator.IsLessThanOrEqualTo, value);
        IFilter IFilter.IsLessThanOrEqualTo(Guid value) => IsLessThanOrEqualTo(value);
        
        public Filter<T> IsLessThanOrEqualTo(int value) => ApplyOperator(Operator.IsLessThanOrEqualTo, value);
        IFilter IFilter.IsLessThanOrEqualTo(int value) => IsLessThanOrEqualTo(value);
        
        public Filter<T> IsLessThanOrEqualTo(DateTime value) =>  ApplyOperator(Operator.IsLessThanOrEqualTo, value);
        IFilter IFilter.IsLessThanOrEqualTo(DateTime value) => IsLessThanOrEqualTo(value);

        public Filter<T> IsLessThanOrEqualTo(TimeSpan value) => ApplyOperator(Operator.IsLessThanOrEqualTo, value);
        IFilter IFilter.IsLessThanOrEqualTo(TimeSpan value) => IsLessThanOrEqualTo(value);
        
        public Filter<T> IsLessThanOrEqualTo<TEnum>(TEnum value) 
            where TEnum : struct, IConvertible, IComparable, IFormattable
            => ApplyEnumOperator(Operator.IsLessThanOrEqualTo, value);
        IFilter IFilter.IsLessThanOrEqualTo<TEnum>(TEnum value) 
            where TEnum : struct 
            => IsLessThanOrEqualTo<TEnum>(value);
        
        public Filter<T> IsLessThanOrEqualTo(object value) => ApplyObjectOperator(Operator.IsLessThanOrEqualTo, value);
        IFilter IFilter.IsLessThanOrEqualTo(object value) => IsLessThanOrEqualTo(value);
        
        #endregion

        #region BeginsWith

        public Filter<T> BeginsWith(string value) => ApplyStringOperator(Operator.BeginsWith, value);
        IFilter IFilter.BeginsWith(string value) => BeginsWith(value);
        
        public Filter<T> BeginsWith(object value) => ApplyObjectOperator(Operator.BeginsWith, value);
        IFilter IFilter.BeginsWith(object value) => BeginsWith(value);
        
        #endregion

        #region EndsWith

        public Filter<T> EndsWith(string value) => ApplyStringOperator(Operator.EndsWith, value);
        IFilter IFilter.EndsWith(string value) => EndsWith(value);
        
        public Filter<T> EndsWith(object value) => ApplyObjectOperator(Operator.EndsWith, value);
        IFilter IFilter.EndsWith(object value) => EndsWith(value);

        #endregion

        #region Contains

        public Filter<T> Contains(string value) => ApplyStringOperator(Operator.Contains, value);
        IFilter IFilter.Contains(string value) => Contains(value);
        
        public Filter<T> Contains(object value) => ApplyObjectOperator(Operator.Contains, value);
        IFilter IFilter.Contains(object value) => Contains(value);

        #endregion
        
        #region Regex
        
        public Filter<T> Regex(string regex) => ApplyStringOperator(Operator.Regex, regex);
        IFilter IFilter.Regex(string value) => Regex(value);
        
        #endregion

        #region InList

        public Filter<T> InList(params bool[] value) =>  ApplyOperator(Operator.InList, value);
        IFilter IFilter.InList(params bool[] value) => InList(value);

        public Filter<T> InList(params string[] value) => ApplyStringOperator(Operator.InList, value);
        IFilter IFilter.InList(params string[] value) => InList(value);

        public Filter<T> InList(params Guid[] value) => ApplyOperator(Operator.InList, value);
        IFilter IFilter.InList(params Guid[] value) => InList(value);

        public Filter<T> InList(params int[] value) => ApplyOperator(Operator.InList, value);
        IFilter IFilter.InList(params int[] value) => InList(value);

        public Filter<T> InList(params DateTime[] value) => ApplyOperator(Operator.InList, value);
        IFilter IFilter.InList(params DateTime[] value) => InList(value);

        public Filter<T> InList(params TimeSpan[] value) => ApplyOperator(Operator.InList, value);
        IFilter IFilter.InList(params TimeSpan[] value) => InList(value);

        public Filter<T> InList<TEnum>(params TEnum[] value) 
            where TEnum : struct, IConvertible, IComparable, IFormattable
            => ApplyEnumOperator(Operator.InList, value);
        IFilter IFilter.InList<TEnum>(params TEnum[] value) => InList(value);

        public Filter<T> InList(params object[] value) => ApplyObjectOperator(Operator.InList, value);
        IFilter IFilter.InList(params object[] value) => InList(value);

        #endregion

        #region NotInList

        public Filter<T> NotInList(params bool[] value) =>  ApplyOperator(Operator.NotInList, value);
        IFilter IFilter.NotInList(params bool[] value) => NotInList(value);

        public Filter<T> NotInList(params string[] value) => ApplyStringOperator(Operator.NotInList, value);
        IFilter IFilter.NotInList(params string[] value) => NotInList(value);

        public Filter<T> NotInList(params Guid[] value) => ApplyOperator(Operator.NotInList, value);
        IFilter IFilter.NotInList(params Guid[] value) => NotInList(value);

        public Filter<T> NotInList(params int[] value) => ApplyOperator(Operator.NotInList, value);
        IFilter IFilter.NotInList(params int[] value) => InList(value);

        public Filter<T> NotInList(params DateTime[] value) => ApplyOperator(Operator.NotInList, value);
        IFilter IFilter.NotInList(params DateTime[] value) => NotInList(value);

        public Filter<T> NotInList(params TimeSpan[] value) => ApplyOperator(Operator.NotInList, value);
        IFilter IFilter.NotInList(params TimeSpan[] value) => NotInList(value);

        public Filter<T> NotInList<TEnum>(params TEnum[] value) 
            where TEnum : struct, IConvertible, IComparable, IFormattable
            => ApplyEnumOperator(Operator.NotInList, value);
        IFilter IFilter.NotInList<TEnum>(params TEnum[] value) => NotInList(value);

        public Filter<T> NotInList(params object[] value) => ApplyObjectOperator(Operator.NotInList, value);
        IFilter IFilter.NotInList(params object[] value) => InList(value);

        #endregion

        #region InQuery

        public Filter<T> InQuery<U>(SubQuery<U> value) => ApplyObjectOperator(Operator.InQuery, value);
        IFilter IFilter.InQuery<U>(SubQuery<U> value) => InQuery(value);

        public Filter<T> InQuery<U>(Filter<U>? filter, Expression<Func<U, object?>> column) => InQuery(new SubQuery<U>(filter, new Column<U>(column)));
        IFilter IFilter.InQuery<U>(Filter<U>? filter, Expression<Func<U, object?>> column) => InQuery(filter, column);

        #endregion

        #region NotInQuery

        public Filter<T> NotInQuery<U>(SubQuery<U> value) => ApplyObjectOperator(Operator.NotInQuery, value);
        IFilter IFilter.NotInQuery<U>(SubQuery<U> value) => NotInQuery(value);

        public Filter<T> NotInQuery<U>(Filter<U>? filter, Expression<Func<U, object?>> column) => NotInQuery(new SubQuery<U>(filter, new Column<U>(column)));
        IFilter IFilter.NotInQuery<U>(Filter<U>? filter, Expression<Func<U, object?>> column) => NotInQuery(filter, column);

        #endregion

        #region Not

        public Filter<T> Not()
        {
            IsNot = !IsNot;
            return this;
        }
        IFilter IFilter.Not() => Not();

        #endregion

        #region All/None

        IFilter IFilter.All() => All();
        public Filter<T> All() => ApplyNullOperator(Operator.All);

        IFilter IFilter.None() => None();
        public Filter<T> None() => ApplyNullOperator(Operator.None);

        #endregion

        #region Lists of Values

        /// <summary>
        /// Create a new filter by aggregating comparisons of <paramref name="expression"/> to <paramref name="values"/>
        /// into a list of Ands or Ors, depending on <paramref name="includes"/>.
        /// </summary>
        /// <remarks>
        /// Returns <see langword="null"/> if <paramref name="values"/> is empty.
        /// </remarks>
        /// <typeparam name="TType"></typeparam>
        /// <param name="expression"></param>
        /// <param name="includes"></param>
        /// <param name="values"></param>
        /// <returns>
        /// <see langword="null"/> if <paramref name="values"/> is empty, otherwise a new filter.
        /// </returns>
        private static Filter<T>? CreateListFilter<TType>(Expression<Func<T, TType>> expression, ListOperator includes, IEnumerable<TType> values)
        {
            Filter<T>? result = null;
            foreach (var value in values)
            {
                CoreUtils.TryFindMemberExpression(expression.Body, out var me);

                var filter = new Filter<T>(CoreUtils.MemberExpressionToString(me));
                filter.Operator = includes.Equals(ListOperator.Includes) ? Operator.IsEqualTo : Operator.IsNotEqualTo;
                filter.Value = value;

                if (result == null)
                {
                    result = filter;
                }
                else
                {
                    if (includes.Equals(ListOperator.Includes))
                        result.Ors.Add(filter);
                    else
                        result.Ands.Add(filter);
                }
            }

            return result;
        }

        public static Filter<T>? List(Expression<Func<T, bool>> expression, ListOperator includes, IEnumerable<bool> values)
        {
            return CreateListFilter(expression, includes, values);
            //.Cast<object>());
        }

        public static Filter<T>? List(Expression<Func<T, string>> expression, ListOperator includes, IEnumerable<string> values)
        {
            return CreateListFilter(expression, includes, values);
            //.Cast<object>());
        }

        public static Filter<T>? List(Expression<Func<T, Guid>> expression, ListOperator includes, IEnumerable<Guid> values)
        {
            return CreateListFilter(expression, includes, values);
            //.Cast<object>());
        }

        public static Filter<T>? List(Expression<Func<T, int>> expression, ListOperator includes, IEnumerable<int> values)
        {
            return CreateListFilter(expression, includes, values);
            //.Cast<object>());
        }

        public static Filter<T>? List(Expression<Func<T, DateTime>> expression, ListOperator includes, IEnumerable<DateTime> values)
        {
            return CreateListFilter(expression, includes, values);
            //.Cast<object>());
        }

        public static Filter<T>? List(Expression<Func<T, TimeSpan>> expression, ListOperator includes, IEnumerable<TimeSpan> values)
        {
            return CreateListFilter(expression, includes, values);
            //.Cast<object>());
        }

        public static Filter<T>? List(Expression<Func<T, object>> expression, ListOperator includes, IEnumerable<object> values)
        {
            return CreateListFilter(expression, includes, values);
        }

        public static Filter<T>? List<TEnum>(Expression<Func<T, TEnum>> expression, ListOperator includes, IEnumerable<TEnum> values)
        {
            return CreateListFilter(expression, includes, values);
            //.Cast<object>());
        }

        #endregion

        #region Binary Serialisation

        private enum ValueType
        {
            Null,
            Byte,
            Short,
            Integer,
            Long,
            Float,
            Double,
            String,
            Boolean,
            DateTime,
            TimeSpan,
            Guid,
            FilterConstant,
            Array,
            SubQuery,
            ObjectArray,
            CustomValue
        }

        private class ValueTypeClass
        {
            public ValueType ValueType;

            public ValueTypeClass(ValueType valueType)
            {
                ValueType = valueType;
            }
        }
        private class ArrayClass : ValueTypeClass
        {
            public ValueTypeClass ElementType;

            public ArrayClass(ValueTypeClass elementType): base(ValueType.Array)
            {
                ElementType = elementType;
            }
        }

        private void WriteValueType(CoreBinaryWriter writer, Type? type)
        {
            ValueType valueType;
            Type? nestedType = null;
            if (type is null)
            {
                valueType = ValueType.Null;
            }
            else if (type.IsArray)
            {
                // Essentially, the array ValueType will always be followed by another type which represents the element type; and if the element type is Array,
                // it also will be followed by another ValueType.
                var elementType = type.GetElementType();

                if(elementType == typeof(object))
                {
                    valueType = ValueType.ObjectArray;
                }
                else
                {
                    nestedType = elementType;
                    valueType = ValueType.Array;
                }
            }
            else if (typeof(ISubQuery).IsAssignableFrom(type))
            {
                valueType = ValueType.SubQuery;
            }
            else if(type == typeof(CustomFilterValue))
            {
                valueType = ValueType.CustomValue;
            }
            else if (type == typeof(bool))
            {
                valueType = ValueType.Boolean;
            }
            else if (type == typeof(Guid))
            {
                valueType = ValueType.Guid;
            }
            else if (type == typeof(DateTime))
            {
                valueType = ValueType.DateTime;
            }
            else if (type == typeof(TimeSpan))
            {
                valueType = ValueType.TimeSpan;
            }
            else if (type == typeof(FilterConstant))
            {
                valueType = ValueType.FilterConstant;
            }
            else if (type.IsEnum)
            {
                valueType = ValueType.Integer;
            }
            else if (type == typeof(string))
            {
                valueType = ValueType.String;
            }
            else if (type == typeof(byte))
            {
                valueType = ValueType.Byte;
            }
            else if (type == typeof(short))
            {
                valueType = ValueType.Short;
            }
            else if (type == typeof(int))
            {
                valueType = ValueType.Integer;
            }
            else if (type == typeof(long))
            {
                valueType = ValueType.Long;
            }
            else if (type == typeof(float))
            {
                valueType = ValueType.Float;
            }
            else if (type == typeof(double))
            {
                valueType = ValueType.Double;
            }
            else
            {
                throw new Exception("Invalid Filter Value Type");
            }

            writer.Write((byte)valueType);
            if(nestedType != null)
            {
                WriteValueType(writer, nestedType);
            }
        }
        private void SerializeValue(CoreBinaryWriter writer, object? value, bool includeType)
        {
            if (includeType)
            {
                WriteValueType(writer, value?.GetType());
            }
            if (value is null)
            {
            }
            else if(value.GetType() == typeof(object[]) && value is object[] objArr)
            {
                writer.Write(objArr.Length);
                foreach (var item in objArr)
                {
                    SerializeValue(writer, item, true);
                }
            }
            else if (value.GetType().IsArray)
            {
                var arr = (value as Array)!;
                writer.Write(arr.Length);
                foreach (var item in arr)
                {
                    // Type of the array has already been figured out by the WriteValueType call.
                    SerializeValue(writer, item, false);
                }
            }
            else if(value is ISubQuery subQuery)
            {
                writer.Write(subQuery.GetQueryType().EntityName());
                writer.Write(subQuery.GetColumn().Property);

                var filter = subQuery.GetFilter();
                if(filter != null)
                {
                    writer.Write(true);
                    filter.SerializeBinary(writer);
                }
                else
                {
                    writer.Write(false);
                }
            }
            else if(value is CustomFilterValue customValue)
            {
                writer.Write(customValue.Data.Length);
                writer.Write(customValue.Data);
            }
            else if (value is bool bl)
            {
                writer.Write(bl);
            }
            else if (value is Guid guid)
            {
                writer.Write(guid);
            }
            else if (value is DateTime dateTime)
            {
                writer.Write(dateTime.Ticks);
            }
            else if (value is TimeSpan timeSpan)
            {
                writer.Write(timeSpan.Ticks);
            }
            else if (value is FilterConstant f) // Filter Constant must come before Enum check.
            {
                writer.Write((int)f);
            }
            else if (value is Enum e)
            {
                writer.Write((int)value);
            }
            else if (value is string str)
            {
                writer.Write(str);
            }
            else if (value is byte b)
            {
                writer.Write(b);
            }
            else if (value is short s)
            {
                writer.Write(s);
            }
            else if (value is int i)
            {
                writer.Write(i);
            }
            else if (value is long l)
            {
                writer.Write(l);
            }
            else if (value is float f32)
            {
                writer.Write(f32);
            }
            else if (value is double f64)
            {
                writer.Write(f64);
            }
        }

        private ValueTypeClass ReadValueType(CoreBinaryReader reader)
        {
            ValueType valueType = (ValueType)reader.ReadByte();

            if(valueType == ValueType.Array)
            {
                return new ArrayClass(ReadValueType(reader));
            }
            else
            {
                return new ValueTypeClass(valueType);
            }
        }
        private Type? ConvertToType(ValueTypeClass valueType)
        {
            switch (valueType.ValueType)
            {
                case ValueType.Null:
                    return null;
                case ValueType.Byte:
                    return typeof(byte);
                case ValueType.Short:
                    return typeof(short);
                case ValueType.Integer:
                    return typeof(int);
                case ValueType.Long:
                    return typeof(long);
                case ValueType.Float:
                    return typeof(float);
                case ValueType.Double:
                    return typeof(double);
                case ValueType.String:
                    return typeof(string);
                case ValueType.Boolean:
                    return typeof(bool);
                case ValueType.DateTime:
                    return typeof(DateTime);
                case ValueType.TimeSpan:
                    return typeof(TimeSpan);
                case ValueType.Guid:
                    return typeof(Guid);
                case ValueType.FilterConstant:
                    return typeof(FilterConstant);
                case ValueType.Array:
                    var arrClass = (ArrayClass)valueType;
                    return ConvertToType(arrClass.ElementType)?.MakeArrayType();
                case ValueType.SubQuery:
                    return typeof(ISubQuery);
                case ValueType.CustomValue:
                    return typeof(CustomFilterValue);
                case ValueType.ObjectArray:
                    return typeof(object[]);
                default:
                    throw new Exception($"Unknown value type {valueType.ValueType}");
            }
        }
        private object? DeserializeValue(CoreBinaryReader reader, ValueTypeClass valueType)
        {
            switch (valueType.ValueType)
            {
                case ValueType.Null:
                    return null;
                case ValueType.Byte:
                    return reader.ReadByte();
                case ValueType.Short:
                    return reader.ReadInt16();
                case ValueType.Integer:
                    return reader.ReadInt32();
                case ValueType.Long:
                    return reader.ReadInt64();
                case ValueType.Float:
                    return reader.ReadSingle();
                case ValueType.Double:
                    return reader.ReadDouble();
                case ValueType.String:
                    return reader.ReadString();
                case ValueType.Boolean:
                    return reader.ReadBoolean();
                case ValueType.DateTime:
                    return new DateTime(reader.ReadInt64());
                case ValueType.TimeSpan:
                    return new TimeSpan(reader.ReadInt64());
                case ValueType.Guid:
                    return reader.ReadGuid();
                case ValueType.FilterConstant:
                    return (FilterConstant)reader.ReadInt32();
                case ValueType.CustomValue:
                    var length = reader.ReadInt32();
                    return new CustomFilterValue(reader.ReadBytes(length));
                case ValueType.ObjectArray:
                    var nItems = reader.ReadInt32();
                    var objArr = new object?[nItems];
                    for (int i = 0; i < nItems; ++i)
                    {
                        var elType = ReadValueType(reader);
                        objArr[i] = DeserializeValue(reader, elType);
                    }
                    return objArr;
                case ValueType.Array:
                    var array = (ArrayClass)valueType;
                    nItems = reader.ReadInt32();
                    var arr = Array.CreateInstance(ConvertToType(array.ElementType), nItems);
                    for (int i = 0; i < nItems; ++i)
                    {
                        arr.SetValue(DeserializeValue(reader, array.ElementType), i);
                    }
                    return arr;
                case ValueType.SubQuery:
                    var queryType = CoreUtils.GetEntity(reader.ReadString());
                    var column = Column.Create(queryType, reader.ReadString());

                    IFilter? filter;
                    if (reader.ReadBoolean())
                    {
                        filter = Filter.Create(queryType);
                        filter.DeserializeBinary(reader);
                    }
                    else
                    {
                        filter = null;
                    }

                    var subQuery = Activator.CreateInstance(typeof(SubQuery<>).MakeGenericType(queryType), filter, column);
                    return subQuery;
                default:
                    throw new Exception($"Unknown value type {valueType.ValueType}");
            }
        }
        private object? DeserializeValue(CoreBinaryReader reader)
        {
            var type = ReadValueType(reader);
            return DeserializeValue(reader, type);
        }

        public void SerializeBinary(CoreBinaryWriter writer)
        {
            // For compatability purposes when redesigning system to no longer use Serializable Expression.
            writer.Write((byte)0);

            writer.Write(Property);
            if (IsNot)
            {
                writer.Write((byte)Operator.Not);
            }
            writer.Write((byte)Operator);

            SerializeValue(writer, Value, true);

            writer.Write(Ands.Count);
            foreach(var and in Ands)
            {
                and.SerializeBinary(writer);
            }

            writer.Write(Ors.Count);
            foreach(var or in Ors)
            {
                or.SerializeBinary(writer);
            }
        }

        public void DeserializeBinary(CoreBinaryReader reader)
        {
            // For compatability purposes when redesigning system to no longer use Serializable Expression.
            var b = reader.ReadByte();
            if(b != 0)
            {
                var exp = reader.DeserialiseExpression(typeof(T), b);
                if(exp is ParameterExpression)
                {
                    Property = "";
                }
                else
                {
                    Property = CoreUtils.ExpressionToString(typeof(object), exp);
                    Logger.Send(LogType.Information, "", $"Non-zero byte: {Property} ({exp.Type})");
                }
            }
            else
            {
                Property = reader.ReadString();
            }

            Operator = (Operator)reader.ReadByte();
            if(Operator == Operator.Not)
            {
                IsNot = true;
                Operator = (Operator)reader.ReadByte();
            }

            var val = DeserializeValue(reader);
            var type = (Operator == Operator.InList || Operator == Operator.NotInList)
                ? Type.MakeArrayType()
                : Type;
            Value = CoreUtils.ChangeType(val, type);

            Ands.Clear();
            var nAnds = reader.ReadInt32();
            for(int i = 0; i < nAnds; ++i)
            {
                var and = new Filter<T>();
                and.DeserializeBinary(reader);
                Ands.Add(and);
            }

            Ors.Clear();
            var nOrs = reader.ReadInt32();
            for (int i = 0; i < nOrs; ++i)
            {
                var or = new Filter<T>();
                or.DeserializeBinary(reader);
                Ors.Add(or);
            }
        }

        #endregion

        #region Display Functions

        private static string ValueToString(object? value)
        {
            if (value is string str)
                return "'" + str + "'";
            else if (value is Guid guid)
                return "guid'" + value + "'";
            else if (value is DateTime date)
                return "'" + date.ToString("o") + "'";
            else if (value is IList list)
                return $"[{string.Join(',', list.Cast<object?>().Select(x => ValueToString(x)))}]";
            else
                return value?.ToString() ?? "''";
        }

        public string AsOData()
        {
            var operators = new Dictionary<Operator, string>
            {
                { Operator.IsEqualTo, "{0} eq {1}" },
                { Operator.IsNotEqualTo, "{0} ne {1}" },
                { Operator.IsGreaterThan, "{0} gt {1}" },
                { Operator.IsGreaterThanOrEqualTo, "{0} ge {1}" },
                { Operator.IsLessThan, "{0} lt {1}" },
                { Operator.IsLessThanOrEqualTo, "{0} le {1}" },
                { Operator.BeginsWith, "startswith({0},{1}) eq true" },
                { Operator.Contains, "substringof({1} {0}) eq true" },
                { Operator.InList, "{0} in ({1})" },
                { Operator.NotInList, "{0} not in ({1})" },
                { Operator.InQuery, "{0} in ({1})" },
                { Operator.NotInQuery, "{0} not in ({1})" },
                { Operator.Regex, "{0} regexp {1}" }
            };

            var result = "";
            if(Operator == Operator.All)
            {
                result = "All";
            }
            else if(Operator == Operator.None)
            {
                result = "None";
            }
            else if (Property != null)
            {
                var value = "null";
                if (Value != null)
                {
                    value = ValueToString(Value);
                }
                result = string.Format("(" + operators[Operator] + ")",
                    Property,
                    value
                );
            }
            if (IsNot)
            {
                result = $"not {result}";
            }

            if (Ands != null && Ands.Count > 0)
                foreach (var and in Ands)
                {
                    var AndResult = and.AsOData();
                    if (!string.IsNullOrEmpty(AndResult))
                        result = string.Format("{0} and {1}", result, AndResult);
                }

            var bChanged = false;
            if (Ors != null && Ors.Count > 0)
            {
                foreach (var or in Ors)
                {
                    var OrResult = or.AsOData();
                    if (!string.IsNullOrEmpty(OrResult))
                    {
                        result = string.Format("{0} or {1}", result, OrResult);
                        bChanged = true;
                    }
                }

                if (bChanged)
                    result = string.Format("({0})", result);
            }

            return result;
        }

        public IEnumerable<string> ColumnNames()
        {
            List<String> result = new List<string>();
            result.Add(Property);
            foreach (var and in Ands)
                result.AddRange(and.ColumnNames());
            foreach (var or in Ors)
                result.AddRange(or.ColumnNames());
            return result;
        }

        public override string ToString()
        {
            return AsOData();
        }

        #endregion

        #region Custom Values

        /// <summary>
        /// Walk through the filter and convert all values which are <see cref="CustomFilterValue"/> to normal objects.
        /// </summary>
        /// <param name="convert">A function used to convert each value.</param>
        public void ConvertCustomValues(ConvertCustomValueFunction convert)
        {
            if(Value is CustomFilterValue custom)
            {
                Value = convert(this, custom);
            }
            foreach(var and in Ands)
            {
                and.ConvertCustomValues(convert);
            }
            foreach (var or in Ors)
            {
                or.ConvertCustomValues(convert);
            }
        }

        #endregion
    }

    public static class FilterSerialization
    {
        /// <summary>
        /// Inverse of <see cref="Write{T}(CoreBinaryWriter, Filter{T}?)"/>.
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        public static Filter<T>? ReadFilter<T>(this CoreBinaryReader reader)
        {
            if (reader.ReadBoolean())
            {
                var filter = new Filter<T>();
                filter.DeserializeBinary(reader);
                return filter;
            }
            return null;
        }

        /// <summary>
        /// Inverse of <see cref="ReadFilter{T}(CoreBinaryReader)"/>.
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="writer"></param>
        public static void Write<T>(this CoreBinaryWriter writer, Filter<T>? filter)
        {
            if (filter is null)
            {
                writer.Write(false);
            }
            else
            {
                writer.Write(true);
                filter.SerializeBinary(writer);
            }
        }
    }

    public class Filters<T> //where T : Entity
    {
        private readonly List<Filter<T>> filters = new List<Filter<T>>();

        public Filters(params Expression<Func<T, object>>[] expressions)
        {
            foreach (var expression in expressions)
                filters.Add(new Filter<T>(expression!));
        }

        public Filter<T>[] Items
        {
            get => filters.ToArray();
            set
            {
                filters.Clear();
                filters.AddRange(value);
            }
        }

        public Filters<T> Add(Filter<T>? filter)
        {
            if(filter != null)
                filters.Add(filter);
            return this;
        }

        public Filters<T> AddRange(IEnumerable<Filter<T>?> criteria)
        {
            foreach(var filter in criteria)
            {
                Add(filter);
            }
            return this;
        }

        public Filters<T> Remove(string column)
        {
            //Filter<T> filter = new Filter<T>(column);
            //columns.RemoveAll(x => x.ToString() == filter.ToString());
            return this;
        }

        public Filters<T> Clear()
        {
            filters.Clear();
            return this;
        }

        public static explicit operator Filters<T>(Filters<Entity> vs)
        {
            var result = new Filters<T>();
            var filter = new List<Filter<T>>();
            foreach (var v in vs.Items)
            {
                var cast = (Filter<T>?)v;
                if(cast != null)
                    filter.Add(cast);
            }
            result.Items = filter.ToArray();
            return result;
        }

        //public Expression<Func<T,bool>> AsExpression()
        //{
        //	Expression<Func<T, bool>> result = null;
        //	foreach (Filter<T> filter in filters)
        //	{
        //		if (result == null)
        //			result = filter.AsExpression();
        //		else
        //			result = result.And(filter.AsExpression());
        //	}
        //	return result;
        //}

        public Filter<T>? Combine()
        {
            if(filters.Count == 0)
            {
                return null;
            }
            else if(filters.Count == 1)
            {
                return filters[0];
            }
            else
            {
                var result = new Filter<T>().All();
                foreach (var filter in filters)
                {
                    if (filter != null)
                    {
                        if (result is null)
                            result = filter;
                        else
                            result.Ands.Add(filter);
                    }
                }

                return result;
            }
        }

        public Filter<T>? CombineOr()
        {
            if(filters.Count == 0)
            {
                return new Filter<T>().None();
            }
            else if(filters.Count == 1)
            {
                return filters[0];
            }
            else
            {
                var result = new Filter<T>().None();
                foreach (var filter in filters)
                {
                    if (filter != null)
                    {
                        if (result is null)
                            result = filter;
                        else
                            result.Ors.Add(filter);
                    }
                }

                return result;
            }
        }

        public static Filter<T>? Combine(params Filter<T>[] filters)
        {
            return new Filters<T>()
                .AddRange(filters)
                .Combine();
        }
    }

    public static class FilterExtensions
    {
        public static Filter<T>? Combine<T>(this Filter<T>? filter1, Filter<T>? filter2)
        {
            if(filter1 is null)
            {
                return filter2;
            }
            else if(filter2 is null)
            {
                return filter1;
            }
            else
            {
                return filter1.And(filter2);
            }
        }

        private static object? Encode(Type T, object? value)
        {
            if(T == typeof(string) && Equals(value, null))
            {
                return "";
            }
            return value;
        }

        private static bool MatchOperator<T>(this Filter<T> filter, T obj, IQueryProviderFactory query)
        {
            if(filter.Operator == Operator.All)
            {
                return true;
            }
            else if(filter.Operator == Operator.None)
            {
                return false;
            }

            if (filter.PropertyDefinition is null) return false;

            if(filter.Value is CustomFilterValue)
            {
                throw new Exception("Custom Filter Value not able to be processed!");
            }

            var valueType = filter.PropertyDefinition.PropertyType;
            var objValue = Encode(valueType, filter.PropertyDefinition.Getter()(obj));
            if(filter.Operator == Operator.InList || filter.Operator == Operator.NotInList)
            {
                if(filter.Value is Array arr && arr.Length == 0)
                {
                    return filter.Operator != Operator.InList;
                }
                else if(filter.Value is IEnumerable enumerable)
                {
                    foreach(var item in enumerable)
                    {
                        if (Equals(Encode(valueType, item), objValue))
                            return filter.Operator == Operator.InList;
                    }
                    return filter.Operator == Operator.NotInList;
                }
                else
                {
                    return false;
                }
            }
            else if(filter.Operator == Operator.InQuery || filter.Operator == Operator.NotInQuery)
            {
                if(filter.Value is ISubQuery subQuery)
                {
                    var result = query.Create(subQuery.GetQueryType());
                    var subFilter = Filter.Create(subQuery.GetQueryType(), subQuery.GetColumn().Name, Operator.IsEqualTo, objValue);
                    if(subQuery.GetFilter() is IFilter subQueryFilter)
                    {
                        subFilter.And(subQueryFilter);
                    }
                    var inQuery = result.Query(subFilter, Columns.None(subQuery.GetQueryType()))
                        .Rows.Count > 0;
                    return inQuery ? filter.Operator == Operator.InQuery : filter.Operator == Operator.NotInQuery;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                object? value;
                if(filter.Value is FilterConstant constant)
                {
                    value = constant.Resolve(filter.PropertyDefinition.PropertyType);
                }
                else
                {
                    value = filter.Value;
                }
                value = Encode(filter.PropertyDefinition.PropertyType, value);
                return filter.Operator switch
                {
                    Operator.IsEqualTo => Equals(objValue, value),
                    Operator.IsNotEqualTo => !Equals(objValue, value),
                    Operator.IsGreaterThan => TryCompare(objValue, value, out var comparison) && comparison > 0,
                    Operator.IsGreaterThanOrEqualTo => TryCompare(objValue, value, out var comparison) && comparison >= 0,
                    Operator.IsLessThan => TryCompare(objValue, value, out var comparison) && comparison < 0,
                    Operator.IsLessThanOrEqualTo => TryCompare(objValue, value, out var comparison) && comparison <= 0,
                    Operator.BeginsWith => (objValue?.ToString() ?? "").StartsWith(value?.ToString() ?? ""),
                    Operator.Contains => (objValue?.ToString() ?? "").Contains(value?.ToString() ?? ""),
                    Operator.EndsWith => (objValue?.ToString() ?? "").EndsWith(value?.ToString() ?? ""),
                    Operator.Regex => Regex.IsMatch(objValue?.ToString() ?? "",value?.ToString() ?? ""),
                    _ => throw new Exception($"Invalid operator {filter.Operator}"),
                };
            }
        }

        private static bool MatchFilter<T>(this Filter<T> filter, T obj, IQueryProviderFactory query) =>
            filter.IsNot ? !filter.MatchOperator(obj, query) : filter.MatchOperator(obj, query);


        private static bool TryCompare(object? a, object? b, out int result)
        {
            try
            {
                result = Comparer.Default.Compare(a, b);
                return true;
            }
            catch (ArgumentException)
            {
                result = 0;
                return false;
            }
        }

        /// <summary>
        /// Check if <paramref name="obj"/> matches this filter; that is, that the operators return true.
        /// </summary>
        /// <param name="query">The query provider used to evaluate <see cref="Operator.InQuery"/> filters.</param>
        public static bool Match<T>(this Filter<T>? filter, T obj, IQueryProviderFactory query) =>
            filter is null
            || (filter.MatchFilter(obj, query) && filter.Ands.All(x => x.Match(obj, query)))
            || filter.Ors.Any(x => x.Match(obj, query));
    }

    public class FilterJsonConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
        {
            if (!(value is IFilter filter))
            {
                writer.WriteNull();
                return;
            }

            //MethodInfo mi = value.GetType().GetTypeInfo().GetMethod("ExpressionToString");
            //String prop = mi.Invoke(value, new object[] { property, true }) as String;
            var prop = filter.Property;

            var op = filter.Operator;
            var val = filter.Value;

            var ands = filter.Ands.ToList();
            var ors = filter.Ors.ToList();

            //var table = value as DataTable;
            //// Save Columns
            writer.WriteStartObject();
            
            writer.WritePropertyName("$type");
            writer.WriteValue(value.GetType().FullName);

            writer.WritePropertyName("Expression");
            writer.WriteValue(prop);

            writer.WritePropertyName("Operator");
            writer.WriteValue(op);

            writer.WritePropertyName("IsNot");
            writer.WriteValue(filter.IsNot);

            if (val is FilterConstant)
            {
                writer.WritePropertyName("FilterConstant");
                writer.WriteValue(val);
            }
            else if(val is CustomFilterValue custom)
            {
                writer.WritePropertyName("CustomValue");
                writer.WriteValue(custom.Data);
            }
            else
            {
                writer.WritePropertyName("Value");
                var valType = val == null ? filter.Type : val.GetType();
                if (valType.IsArray)
                {
                    writer.WriteStartArray();
                    var arr = (val as IEnumerable)!;
                    foreach (var v in arr)
                        writer.WriteValue(v);
                    writer.WriteEndArray();
                }
                else if (valType.IsConstructedGenericType && valType.GetGenericTypeDefinition() == typeof(SubQuery<>))
                {
                    var serialized = Serialization.Serialize(val);
                    var wrapper = new SubQuerySerializationWrapper(valType.GenericTypeArguments[0].EntityName(), serialized);

                    var str = Serialization.Serialize(wrapper);

                    writer.WriteValue(str);
                }
                else
                {
                    writer.WriteValue(val);
                }
            }

            if (ands != null && ands.Count > 0)
            {
                writer.WritePropertyName("Ands");
                serializer.Serialize(writer, ands);
            }

            if (ors != null && ors.Count > 0)
            {
                writer.WritePropertyName("Ors");
                serializer.Serialize(writer, ors);
            }

            writer.WriteEndObject();
        }

        public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;

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

            while (reader.TokenType != JsonToken.EndObject && reader.Read())
            {
                if (reader.Value != null)
                {
                    var key = reader.Value.ToString();
                    reader.Read();
                    if (String.Equals(key, "$type"))
                        objectType = Type.GetType(reader.Value.ToString()) ?? objectType;
                    else if (string.Equals(key, "Ors"))
                    {
                        var ors = new List<object>();
                        var array = JArray.Load(reader);
                        foreach (var item in array)
                        {
                            var or = ReadJson(item.CreateReader(), objectType, existingValue, serializer);
                            if (or != null)
                                ors.Add(or);
                        }

                        data[key] = ors;
                    }
                    else if (string.Equals(key, "Ands"))
                    {
                        var ands = new List<object>();
                        var array = JArray.Load(reader);
                        foreach (var item in array)
                        {
                            var and = ReadJson(item.CreateReader(), objectType, existingValue, serializer);
                            if (and != null)
                                ands.Add(and);
                        }

                        data[key] = ands;
                    }
                    else if (reader.TokenType == JsonToken.StartArray)
                    {
                        var vals = new List<object>();
                        while (reader.TokenType != JsonToken.EndArray && reader.Read())
                            if (reader.Value != null)
                                vals.Add(reader.Value);
                        data[key] = vals;
                    }
                    else
                    {
                        data[key] = reader.Value;
                    }
                }
            }

            var finaltype = data.TryGetValue("$type", out object concrete)
                ? (Type)concrete
                : objectType;

            var result = (Activator.CreateInstance(finaltype) as IFilter)!;

            var prop = data["Expression"].ToString();
            if (prop.StartsWith('{'))
            {
                var exp = CoreUtils.StringToExpression(prop);
                prop = CoreUtils.ExpressionToString(typeof(object), exp);
            }
            var op = (Operator)int.Parse(data["Operator"].ToString());

            if(data.TryGetValue("IsNot", out var isNotValue) && isNotValue is bool b)
            {
                result.IsNot = b;
            }

            result.Property = prop;
            result.Operator = op;
            if(data.TryGetValue("Value", out var val))
            {
                if (op == Operator.InQuery || op == Operator.NotInQuery)
                {
                    var wrapper = Serialization.Deserialize<SubQuerySerializationWrapper>((string)val);
                    val = Activator.CreateInstance(typeof(SubQuery<>).MakeGenericType(CoreUtils.GetEntity(wrapper.Type)), null, null);
                    Serialization.DeserializeInto(wrapper.SerializedSubquery, val);
                    result.Value = val;
                }
                else if(op == Operator.InList || op == Operator.NotInList)
                {
                    result.Value = CoreUtils.ChangeType(val, result.Type.MakeArrayType());
                }
                else
                {
                    result.Value = CoreUtils.ChangeType(val, result.Type);
                }
            }
            else if(data.TryGetValue("FilterConstant", out var filterConstant))
            {
                result.Value = CoreUtils.ChangeType<FilterConstant>(filterConstant);
            }
            else if(data.TryGetValue("CustomValue", out var customValue))
            {
                result.Value = new CustomFilterValue(CoreUtils.ChangeType<byte[]>(customValue) ?? Array.Empty<byte>());
            }

            if (data.ContainsKey("Ors"))
            {
                var source = (data["Ors"] as List<object>)!;
                foreach (var srcitem in source)
                    result.Or((srcitem as IFilter)!);
            }

            if (data.ContainsKey("Ands"))
            {
                var source = (data["Ands"] as List<object>)!;
                foreach (var srcitem in source)
                    result.And((srcitem as IFilter)!);
            }

            return result;
        }

        public override bool CanConvert(Type objectType)
        {
            if (objectType.IsConstructedGenericType)
            {
                var ot = objectType.GetGenericTypeDefinition();
                var tt = typeof(Filter<>);
                if (ot == tt)
                    return true;
            }

            return false;
        }
    }
}