using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using DatabaseEditor.BL;
using DatabaseEditor.BL.Attributes;
using DatabaseEditor.BL.Filters.Conditions;
using DatabaseEditor.BL.Filters.Operators;
using DatabaseEditor.Messages;
using GalaSoft.MvvmLight.CommandWpf;
using PropertyChanged;

namespace DatabaseEditor.ViewModels
{
    [ImplementPropertyChanged]
    public class FilterViewModel<T> : ViewModelBase
    {
        private List<KeyValuePair<string, Type>> _searchableProperties;

        public FilterViewModel()
        {
            Conditions = new ObservableCollection<PropertyCondition<T>>();
        }

        public string SearchBoxText { get; set; }

        public KeyValuePair<string, Type> SearchBoxProperty { get; set; }

        public LogicOperator SelectedLogicOperator { get; set; }

        public KeyValuePair<string, Type> SelectedProperty { get; set; }

        public string SelectedOperator { get; set; }

        public string FilterValue { get; set; }

        public List<string> LogicOperators => Enum.GetNames(typeof(LogicOperator)).ToList();

        public List<KeyValuePair<string, Type>> SearchableProperties
        {
            get
            {
                if (_searchableProperties == null)
                {
                    _searchableProperties =
                        typeof(T).GetProperties()
                            .Where(property => Attribute.IsDefined(property, typeof(SearchablePropertyAttribute)))
                            .Select(property => new KeyValuePair<string, Type>(property.Name, property.PropertyType))
                            .ToList();
                }
                return _searchableProperties;
            }
        }

        public List<string> AppliableOperators
        {
            get
            {
                if (SelectedProperty.Value == typeof(string))
                {
                    return Enum.GetNames(typeof(StringOperator)).ToList();
                }
                else if (SelectedProperty.Value == typeof(int))
                {
                    return Enum.GetNames(typeof(IntegerOperator)).ToList();
                }
                return null;
            }
        }

        public ObservableCollection<PropertyCondition<T>> Conditions { get; set; }

        public RelayCommand AddConditionCommand => new RelayCommand(AddCondition, () => IsRuleComplete);

        public RelayCommand<PropertyCondition<T>> RemoveConditionCommand
            => new RelayCommand<PropertyCondition<T>>(RemoveCondition);

        public RelayCommand<string> SearchboxChangedCommand => new RelayCommand<string>(SearboxChanged);

        public RelayCommand ApplyFilterCommand => new RelayCommand(ApplyFilter);

        private void AddCondition()
        {
            PropertyCondition<T> condition = null;
            if (SelectedProperty.Value == typeof(string))
            {
                condition = new StringCondition<T>(SelectedLogicOperator, SelectedProperty.Key,
                    (StringOperator) Enum.Parse(typeof(StringOperator), SelectedOperator), FilterValue);
            }
            if (SelectedProperty.Value == typeof(int))
            {
                if (Parser.IsParseable(FilterValue, typeof(int)))
                {
                    condition = new IntegerCondition<T>(SelectedLogicOperator, SelectedProperty.Key,
                        (IntegerOperator) Enum.Parse(typeof(IntegerOperator), SelectedOperator),
                        Int32.Parse(FilterValue));
                }
            }
            Conditions.Add(condition);
        }

        private void RemoveCondition(PropertyCondition<T> condition)
        {
            Conditions.Remove(condition);
        }

        private void SearboxChanged(string text)
        {
            Conditions.Clear();
            if (SearchBoxProperty.Value == typeof(string))
            {
                Conditions.Add(new StringCondition<T>(LogicOperator.AND, SearchBoxProperty.Key,
                    StringOperator.StartsWith, text));
            }
            if (SearchBoxProperty.Value == typeof(int))
            {
                if (Parser.IsParseable(text, typeof(int)))
                {
                    Conditions.Add(new IntegerCondition<T>(LogicOperator.AND, SearchBoxProperty.Key,
                        IntegerOperator.StartsWith, Int32.Parse(text)));
                }
                else return;
            }
            //ApplyFilter();
        }

        private void ApplyFilter()
        {
            MessengerInstance.Send(new FilterChangedMessage<T>(Conditions.ToList()));
        }

        private bool IsRuleComplete
        {
            get
            {
                if (!SelectedProperty.Equals(default(KeyValuePair<string, Type>))
                    && SelectedOperator != null
                    && FilterValue != String.Empty
                    && Parser.IsParseable(FilterValue, SelectedProperty.Value))
                    return true;
                return false;
            }
        }
    }
}