using FieldVisualizer.Entities.ColorMaps;
using FieldVisualizer.Entities.ColorMaps.Factories;
using FieldVisualizer.Entities.Values;
using FieldVisualizer.Entities.Values.Primitives;
using FieldVisualizer.Infrastructure.Commands;
using FieldVisualizer.Services.ColorServices;
using FieldVisualizer.Services.PrimitiveServices;
using FieldVisualizer.Services.ValueRanges;
using FieldVisualizer.Windows.UserControls;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace FieldVisualizer.ViewModels.FieldViewerViewModels
{
    public class FieldViewerViewModel : ViewModelBase, IDataErrorInfo
    {
        private ICommand setCrossLineCommand;
        private IPrimitiveSet primitiveSet;
        private double dX, dY;
        private ColorMapsTypes _ColorMapType = ColorMapsTypes.LiraSpectrum;
        private IColorMap _ColorMap;
        private IValueRange valueRange;
        private IEnumerable<IValueRange> valueRanges;
        private IEnumerable<IValueColorRange> valueColorRanges;
        private bool setMinValue;
        private bool setMaxValue;
        private double crossLineX;
        private double crossLineY;
        private double sumAboveLine;
        private double sumUnderLine;
        private Line previosLine;
        const int RangeNumber = 16;
        private const int zoomFactor = 1000;
        private RelayCommand rebuildCommand;

        public ICommand RebuildCommand => rebuildCommand ??= new RelayCommand(o => ProcessPrimitives(), o => PrimitiveValidation());
        public ICommand ZoomInCommand { get; }
        public ICommand ZoomOutCommand { get; }
        public ICommand ChangeColorMapCommand { get; }
        public ICommand SetUserColorsCommand { get; }
        public ICommand SetCrossLineCommand
        {
            get
            {
                if (setCrossLineCommand == null)
                {
                    setCrossLineCommand = new RelayCommand(SetCrossLine);
                }

                return setCrossLineCommand;
            }
        }

        public IPrimitiveSet PrimitiveSet
        { get
            {
                return primitiveSet;
            }
            set
            {
                primitiveSet = value;
                AreaTotal = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Sum(x => x.Area);
                AreaNeg = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value < 0d).Sum(x => x.Area);
                AreaZero = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value == 0d).Sum(x => x.Area);
                AreaPos = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value > 0d).Sum(x => x.Area);
                SumTotal = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Sum(x => x.Value);
                SumNeg = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value < 0d).Sum(x => x.Value);
                SumPos = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value > 0d).Sum(x => x.Value);
                OnPropertyChanged(nameof(PrimitiveSet));
                OnPropertyChanged(nameof(AreaTotal));
                OnPropertyChanged(nameof(AreaNeg));
                OnPropertyChanged(nameof(AreaZero));
                OnPropertyChanged(nameof(AreaPos));
                OnPropertyChanged(nameof(SumTotal));
                OnPropertyChanged(nameof(SumNeg));
                OnPropertyChanged(nameof(SumPos));

            }
        }
        public IValueRange UserValueRange { get; set; }
        public bool SetMinValue
        {
            get
            {
                return setMinValue;
            }
            set
            {
                setMinValue = value;
                OnPropertyChanged(nameof(SetMinValue));
                //OnPropertyChanged(nameof(UserMinValue));
            }
        }
        public bool SetMaxValue
        {
            get
            {
                return setMaxValue;
            }
            set
            {
                setMaxValue = value;
                OnPropertyChanged(nameof(SetMaxValue));
                //OnPropertyChanged(nameof(UserMaxValue));
            }
        }
        public double UserMinValue
        {
            get
            {
                return UserValueRange.BottomValue;
            }
            set
            {
                UserValueRange.BottomValue = value;
                OnPropertyChanged(nameof(UserMinValue));
            }
        }
        public double UserMaxValue
        {
            get
            {
                return UserValueRange.TopValue;
            }
            set
            {
                double tmpVal = UserValueRange.TopValue;
                try
                {
                    UserValueRange.TopValue = value;
                    OnPropertyChanged(nameof(UserMaxValue));
                }
                catch (Exception ex)
                {
                    UserValueRange.TopValue = tmpVal;
                }
            }
        }
        public double AreaTotal { get; private set; }
        public double AreaNeg { get; private set; }
        public double AreaZero { get; private set; }
        public double AreaPos { get; private set; }
        public double SumTotal { get; private set;}
        public double SumNeg { get; private set; }
        public double SumPos { get; private set; }
        public Viewbox WorkPlaneBox { get; set; }
        public VerticalLegend Legend { get; set; }
        public Canvas WorkPlaneCanvas { get; set; }
        public double ScrolWidth { get; set; }
        public double ScrolHeight { get; set; }
        public double CrossLineX { get => crossLineX; set => SetProperty(ref crossLineX, value); }
        public double CrossLineY { get => crossLineY; set => SetProperty(ref crossLineY, value); }

        public double SumAboveLine { get => sumAboveLine; set => SetProperty(ref sumAboveLine, value); }
        public double SumUnderLine { get => sumUnderLine; set => SetProperty(ref sumUnderLine, value); }

        public string Error { get; }
        public string Title { get; set; }
        public string SubTitle { get; set; }

        public string this[string columnName]
        {
            get
            {
                string error = String.Empty;
                return error;
            }
        }



        public FieldViewerViewModel()
        {
            ZoomInCommand = new RelayCommand(o => Zoom(1.2), o => PrimitiveValidation());
            ZoomOutCommand = new RelayCommand(o => Zoom(0.8), o => PrimitiveValidation());
            ChangeColorMapCommand = new RelayCommand(o => ChangeColorMap(), o => PrimitiveValidation());
            SetUserColorsCommand = new RelayCommand(o => ColorRefresh(), o => (SetMinValue || SetMaxValue));
            UserValueRange = new ValueRange() { BottomValue = 0, TopValue = 0 };
            SetMinValue = false;
            SetMaxValue = false;
        }
        public void ColorRefresh()
        {
            if (PrimitiveValidation() == false) { return; }
            _ColorMap = ColorMapFactory.GetColorMap(_ColorMapType);
            SetColor();
            if ((PrimitiveSet is null) == false)
            {
                ProcessPrimitives();
                Legend.ValueColorRanges = valueColorRanges;
                Legend.Refresh();
            }
        }
        public void Refresh()
        {
            SetMinValue = false;
            SetMaxValue = false;
            ColorRefresh();
        }
        private void ProcessPrimitives()
        {
            WorkPlaneCanvas.Children.Clear();
            Title = PrimitiveSet.Name;
            OnPropertyChanged(nameof(Title));
            SubTitle = PrimitiveSet.SubTitle;
            OnPropertyChanged(nameof(SubTitle));
            double sizeX = PrimitiveOperations.GetSizeX(PrimitiveSet.ValuePrimitives) * zoomFactor;
            double sizeY = PrimitiveOperations.GetSizeY(PrimitiveSet.ValuePrimitives) * zoomFactor;
            dX = PrimitiveOperations.GetMinMaxX(PrimitiveSet.ValuePrimitives)[0];
            dY = PrimitiveOperations.GetMinMaxY(PrimitiveSet.ValuePrimitives)[1];
            WorkPlaneCanvas.Width = Math.Abs(sizeX);
            WorkPlaneCanvas.Height = Math.Abs(sizeY);
            WorkPlaneBox.Width = (ScrolWidth - 50);
            WorkPlaneBox.Height = (ScrolHeight - 50);
            var logic = new AddPrimitivesToCanvasLogic()
            {
                WorkPlaneCanvas = WorkPlaneCanvas,
                DX = dX,
                DY = dY,
                ValueRange = valueRange,
                ColorMap = _ColorMap,
                ValueColorRanges = valueColorRanges,
            };       
            logic.ProcessPrimitives(PrimitiveSet.ValuePrimitives);

        }
        private void Zoom(double coefficient)
        {
            WorkPlaneBox.Width *= coefficient;
            WorkPlaneBox.Height *= coefficient;
        }
        private void ChangeColorMap()
        {
            //Iterate all available color maps one by one
            try
            {
                _ColorMapType++;
                IColorMap colorMap = ColorMapFactory.GetColorMap(_ColorMapType);
            }
            catch (Exception ex) { _ColorMapType = 0; }
            ColorRefresh();
        }
        private bool PrimitiveValidation()
        {
            if (PrimitiveSet == null || PrimitiveSet.ValuePrimitives.Count() == 0) { return false; }
            else return true;
        }
        private void SetColor()
        {
            valueRange = PrimitiveOperations.GetValueRange(PrimitiveSet.ValuePrimitives);
            //if bottom value is greater than top value
            if (SetMinValue
                & SetMaxValue
                & (UserValueRange.BottomValue > UserValueRange.TopValue))
            {
                UserValueRange.TopValue = UserValueRange.BottomValue;
            }
            if (SetMinValue == true)
            {
                valueRange.BottomValue = UserValueRange.BottomValue;
            }
            else
            {
                UserValueRange.BottomValue = valueRange.BottomValue;
            }
            if (SetMaxValue == true)
            {
                valueRange.TopValue = UserValueRange.TopValue;
            }
            else
            {
                UserValueRange.TopValue = valueRange.TopValue;
            }
            valueRanges = ValueRangeOperations.DivideValueRange(valueRange, RangeNumber);
            valueColorRanges = ColorOperations.GetValueColorRanges(valueRange, valueRanges, _ColorMap);
        }
        private void SetCrossLine(object commandParameter)
        {
            AddCrossLine();
            AddSummaryInfoCrossLine();
        }
        private void AddCrossLine()
        {
            double width = WorkPlaneCanvas.ActualWidth;
            double heigth = WorkPlaneCanvas.ActualHeight;
            Line line = new Line();
            if (crossLineX == 0d)
            {
                line.X1 = - width / 2d - dX;
                line.Y1 = - crossLineY + dY;
                line.X2 = width / 2d - dX;
                line.Y2 = - crossLineY + dY;
            }
            else if (crossLineY == 0d)
            {
                line.X1 = crossLineX - dX;
                line.Y1 = heigth / 2 + dY;
                line.X2 = crossLineX - dX;
                line.Y2 = -heigth / 2 + dY;
            }
            else
            {
                line.X1 = - width / 2d - dX;
                line.Y1 = -(crossLineY / crossLineX * width / 2 + crossLineY) - dY;
                line.X2 = width / 2d - dX;
                line.Y2 = -(-crossLineY / crossLineX * width / 2 + crossLineY) - dY ;
            }
            SolidColorBrush brush = new SolidColorBrush();
            brush.Color = Colors.Red;
            line.Fill = brush;
            line.Stroke = brush;
            line.StrokeThickness = (width + heigth) / 100;
            if (previosLine != null)
            {
                try { WorkPlaneCanvas.Children.Remove(previosLine);}
                catch (Exception) {}
            }
            previosLine = line;
            WorkPlaneCanvas.Children.Add(line);
        }
        private double GetPointOfCrossLine(double x)
        {
            double y;
            if (crossLineX == 0d)
            {
                y = crossLineY;
            }
            else
            {
                y = -crossLineY / crossLineX - crossLineY; 
            }
            return y;
        }
        private void AddSummaryInfoCrossLine()
        {
            SumAboveLine = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x=>x.CenterY >= GetPointOfCrossLine(x.CenterX)).Sum(x => x.Value);
            SumUnderLine = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.CenterY <= GetPointOfCrossLine(x.CenterX)).Sum(x => x.Value);
        }
    }
}
