using SurfacePlot3D.Common;
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.Windows.Media.Media3D;
using System.Windows.Media;
using System.Windows;
using System.Xml.Serialization;
using SurfacePlot3D.Members;

namespace SurfacePlot3D.MeshGenerators
{
    public abstract class CylindricalMeshBase : MeshGeneratorBase
    {
        #region Private Commands

        private ICommand _decreaseThetaStartCommandIc;
        private ICommand _decreaseThetaStopCommandIc;
        private ICommand _increaseThetaStartCommandIc;
        private ICommand _increaseThetaStopCommandIc;
        private ICommand _decreasePoint1XCommandIc;
        private ICommand _decreasePoint1YCommandIc;
        private ICommand _decreasePoint1ZCommandIc;

        private ICommand _increasePoint1XCommandIc;
        private ICommand _increasePoint1YCommandIc;
        private ICommand _decreasePoint2XCommandIc;
        private ICommand _decreasePoint2YCommandIc;
        private ICommand _decreasePoint2ZCommandIc;
        private ICommand _increasePoint2XCommandIc;
        private ICommand _increasePoint2YCommandIc;

        private ICommand _decreaseRadiusCommandIc;
        private ICommand _increaseRadiusCommandIc;
        private ICommand _decreaseLengthCommandIc;
        private ICommand _increaseLengthCommandIc;
        private ICommand _decreaseStacksCommandIc;
        private ICommand _increaseStacksCommandIc;
        private ICommand _decreaseSlicesCommandIc;
        private ICommand _increaseSlicesCommandIc;
        private ICommand _increasePoint1ZCommandIc;
        private ICommand _increasePoint2ZCommandIc;

        #endregion

        #region Public Commands

        [XmlIgnore] public ICommand DecreaseThetaStartCommandIc => _decreaseThetaStartCommandIc ?? (_decreaseThetaStartCommandIc = new RelayCommand<Object>(DecreaseThetaStartCommandIcExecuted));
        [XmlIgnore] public ICommand DecreaseThetaStopCommandIc => _decreaseThetaStopCommandIc ?? (_decreaseThetaStopCommandIc = new RelayCommand<Object>(DecreaseThetaStopCommandIcExecuted));
        [XmlIgnore] public ICommand IncreaseThetaStartCommandIc => _increaseThetaStartCommandIc ?? (_increaseThetaStartCommandIc = new RelayCommand<Object>(IncreaseThetaStartCommandIcExecuted));
        [XmlIgnore] public ICommand IncreaseThetaStopCommandIc => _increaseThetaStopCommandIc ?? (_increaseThetaStopCommandIc = new RelayCommand<Object>(IncreaseThetaStopCommandIcExecuted));
        [XmlIgnore] public ICommand DecreasePoint1XCommandIc => _decreasePoint1XCommandIc ?? (_decreasePoint1XCommandIc = new RelayCommand<Object>(DecreasePoint1XCommandIcExecuted));
        [XmlIgnore] public ICommand DecreasePoint1YCommandIc => _decreasePoint1YCommandIc ?? (_decreasePoint1YCommandIc = new RelayCommand<Object>(DecreasePoint1YCommandIcExecuted));
        [XmlIgnore] public ICommand DecreasePoint1ZCommandIc => _decreasePoint1ZCommandIc ?? (_decreasePoint1ZCommandIc = new RelayCommand<Object>(DecreasePoint1ZCommandIcExecuted));
        [XmlIgnore] public ICommand IncreasePoint1XCommandIc => _increasePoint1XCommandIc ?? (_increasePoint1XCommandIc = new RelayCommand<Object>(IncreasePoint1XCommandIcExecuted));
        [XmlIgnore] public ICommand IncreasePoint1YCommandIc => _increasePoint1YCommandIc ?? (_increasePoint1YCommandIc = new RelayCommand<Object>(IncreasePoint1YCommandIcExecuted));
        [XmlIgnore] public ICommand DecreasePoint2XCommandIc => _decreasePoint2XCommandIc ?? (_decreasePoint2XCommandIc = new RelayCommand<Object>(DecreasePoint2XCommandIcExecuted));
        [XmlIgnore] public ICommand DecreasePoint2YCommandIc => _decreasePoint2YCommandIc ?? (_decreasePoint2YCommandIc = new RelayCommand<Object>(DecreasePoint2YCommandIcExecuted));
        [XmlIgnore] public ICommand DecreasePoint2ZCommandIc => _decreasePoint2ZCommandIc ?? (_decreasePoint2ZCommandIc = new RelayCommand<Object>(DecreasePoint2ZCommandIcExecuted));
        [XmlIgnore] public ICommand IncreasePoint2XCommandIc => _increasePoint2XCommandIc ?? (_increasePoint2XCommandIc = new RelayCommand<Object>(IncreasePoint2XCommandIcExecuted));
        [XmlIgnore] public ICommand IncreasePoint1ZCommandIc => _increasePoint1ZCommandIc ?? (_increasePoint1ZCommandIc = new RelayCommand<Object>(IncreasePoint1ZCommandIcExecuted));
        [XmlIgnore] public ICommand IncreasePoint2YCommandIc => _increasePoint2YCommandIc ?? (_increasePoint2YCommandIc = new RelayCommand<Object>(IncreasePoint2YCommandIcExecuted));

        [XmlIgnore] public ICommand DecreaseRadiusCommandIc => _decreaseRadiusCommandIc ?? (_decreaseRadiusCommandIc = new RelayCommand<Object>(DecreaseRadiusCommandIcExecuted));
        [XmlIgnore] public ICommand IncreaseRadiusCommandIc => _increaseRadiusCommandIc ?? (_increaseRadiusCommandIc = new RelayCommand<Object>(IncreaseRadiusCommandIcExecuted));
        [XmlIgnore] public ICommand DecreaseLengthCommandIc => _decreaseLengthCommandIc ?? (_decreaseLengthCommandIc = new RelayCommand<Object>(DecreaseLengthCommandIcExecuted));
        [XmlIgnore] public ICommand IncreaseLengthCommandIc => _increaseLengthCommandIc ?? (_increaseLengthCommandIc = new RelayCommand<Object>(IncreaseLengthCommandIcExecuted));
        [XmlIgnore] public ICommand DecreaseStacksCommandIc => _decreaseStacksCommandIc ?? (_decreaseStacksCommandIc = new RelayCommand<Object>(DecreaseStacksCommandIcExecuted));
        [XmlIgnore] public ICommand IncreaseStacksCommandIc => _increaseStacksCommandIc ?? (_increaseStacksCommandIc = new RelayCommand<Object>(IncreaseStacksCommandIcExecuted));
        [XmlIgnore] public ICommand DecreaseSlicesCommandIc => _decreaseSlicesCommandIc ?? (_decreaseSlicesCommandIc = new RelayCommand<Object>(DecreaseSlicesCommandIcExecuted));
        [XmlIgnore] public ICommand IncreaseSlicesCommandIc => _increaseSlicesCommandIc ?? (_increaseSlicesCommandIc = new RelayCommand<Object>(IncreaseSlicesCommandIcExecuted));
        [XmlIgnore] public ICommand IncreasePoint2ZCommandIc => _increasePoint2ZCommandIc ?? (_increasePoint2ZCommandIc = new RelayCommand<Object>(IncreasePoint2ZCommandIcExecuted));

        #endregion

        #region Public Methods

        public void Refresh() { PropertyChanged(new DependencyPropertyChangedEventArgs()); }

        #endregion

        private void DecreaseThetaStartCommandIcExecuted(Object sender) { ThetaStart -= 1; }
        private void DecreaseThetaStopCommandIcExecuted(Object sender) { ThetaStop -= 1; }
        private void IncreaseThetaStartCommandIcExecuted(Object sender) { ThetaStart += 1; }
        private void IncreaseThetaStopCommandIcExecuted(Object sender) { ThetaStop += 1; }
        private void DecreasePoint1XCommandIcExecuted(Object sender) { Point1X -= MovementValue; }
        private void DecreasePoint1YCommandIcExecuted(Object sender) { Point1Y -= MovementValue; }
        private void DecreasePoint1ZCommandIcExecuted(Object sender) { Point1Z -= MovementValue; }
        private void IncreasePoint1XCommandIcExecuted(Object sender) { Point1X += MovementValue; }
        private void IncreasePoint1YCommandIcExecuted(Object sender) { Point1Y += MovementValue; }
        private void IncreasePoint1ZCommandIcExecuted(Object sender) { Point1Z += MovementValue; }
        private void DecreasePoint2XCommandIcExecuted(Object sender) { Point2X -= MovementValue; }
        private void DecreasePoint2YCommandIcExecuted(Object sender) { Point2Y -= MovementValue; }
        private void DecreasePoint2ZCommandIcExecuted(Object sender) { Point2Z -= MovementValue; }
        private void IncreasePoint2XCommandIcExecuted(Object sender) { Point2X += MovementValue; }
        public void IncreasePoint2YCommandIcExecuted(Object sender) { Point2Y += MovementValue; }
        private void IncreasePoint2ZCommandIcExecuted(Object sender) { Point2Z += MovementValue; }
        private void DecreaseRadiusCommandIcExecuted(Object sender) { Radius -= MovementValue; }
        private void IncreaseRadiusCommandIcExecuted(Object sender) { Radius += MovementValue; }
        private void DecreaseLengthCommandIcExecuted(Object sender) { Length -= MovementValue; }
        private void IncreaseLengthCommandIcExecuted(Object sender) { Length += MovementValue; }
        private void DecreaseStacksCommandIcExecuted(Object sender) { if (Stacks - 1 > 0) Stacks--; }
        private void IncreaseSlicesCommandIcExecuted(Object sender) { Slices += Convert.ToInt16(MovementValue); Refresh(); }
        private void IncreaseStacksCommandIcExecuted(Object sender) { Stacks++; }
        private void DecreaseSlicesCommandIcExecuted(Object sender) { if (Slices - 1 > 0) Slices -= Convert.ToInt16(MovementValue); Refresh(); }

        public Double Point1X { set { SetValue(Point1XProperty, value); } get { return (Double)GetValue(Point1XProperty); } }
        public Double Point1Y { set { SetValue(Point1YProperty, value); } get { return (Double)GetValue(Point1YProperty); } }
        public Double Point1Z { set { SetValue(Point1ZProperty, value); } get { return (Double)GetValue(Point1ZProperty); } }

        public Double Point2X { set { SetValue(Point2XProperty, value); } get { return (Double)GetValue(Point2XProperty); } }
        public Double Point2Y { set { SetValue(Point2YProperty, value); } get { return (Double)GetValue(Point2YProperty); } }
        public Double Point2Z { set { SetValue(Point2ZProperty, value); } get { return (Double)GetValue(Point2ZProperty); } }


        public static readonly DependencyProperty Point1XProperty = DependencyProperty.Register("Point1X", typeof(Double), typeof(CylindricalMeshBase), new PropertyMetadata(0D, Point1PropertyChanged));
        public static readonly DependencyProperty Point1YProperty = DependencyProperty.Register("Point1Y", typeof(Double), typeof(CylindricalMeshBase), new PropertyMetadata(-1D, Point1PropertyChanged));
        public static readonly DependencyProperty Point1ZProperty = DependencyProperty.Register("Point1Z", typeof(Double), typeof(CylindricalMeshBase), new PropertyMetadata(0D, Point1PropertyChanged));

        public static readonly DependencyProperty Point2XProperty = DependencyProperty.Register("Point2X", typeof(Double), typeof(CylindricalMeshBase), new PropertyMetadata(0D, Point2PropertyChanged));
        public static readonly DependencyProperty Point2YProperty = DependencyProperty.Register("Point2Y", typeof(Double), typeof(CylindricalMeshBase), new PropertyMetadata(1.0D, Point2PropertyChanged));
        public static readonly DependencyProperty Point2ZProperty = DependencyProperty.Register("Point2Z", typeof(Double), typeof(CylindricalMeshBase), new PropertyMetadata(0D, Point2PropertyChanged));
        public static readonly DependencyProperty Point1Property = DependencyProperty.Register("Point1", typeof(Point3D), typeof(CylindricalMeshBase), new PropertyMetadata(new Point3D(0, -1, 0), PropertyChanged));
        public static readonly DependencyProperty Point2Property = DependencyProperty.Register("Point2", typeof(Point3D), typeof(CylindricalMeshBase), new PropertyMetadata(new Point3D(0, 1, 0), PropertyChanged));
        public static readonly DependencyProperty LengthProperty = DependencyProperty.Register("Length", typeof(Double), typeof(CylindricalMeshBase), new PropertyMetadata(2.0, PropertyChanged));
        public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(Double), typeof(CylindricalMeshBase), new PropertyMetadata(1.0, PropertyChanged));
        public static readonly DependencyProperty SlicesProperty = DependencyProperty.Register("Slices", typeof(int), typeof(CylindricalMeshBase), new PropertyMetadata(36, PropertyChanged), ValidateSlices);
        public static readonly DependencyProperty StacksProperty = DependencyProperty.Register("Stacks", typeof(int), typeof(CylindricalMeshBase), new PropertyMetadata(36, PropertyChanged), ValidateStacks);
        public static readonly DependencyProperty ThetaDivProperty = DependencyProperty.Register("ThetaDiv", typeof(int), typeof(CylindricalMeshBase), new PropertyMetadata(1, PropertyChanged));
        public static readonly DependencyProperty ThetaStartProperty = DependencyProperty.Register("ThetaStart", typeof(int), typeof(CylindricalMeshBase), new PropertyMetadata(0, PropertyChanged));
        public static readonly DependencyProperty ThetaStopProperty = DependencyProperty.Register("ThetaStop", typeof(int), typeof(CylindricalMeshBase), new PropertyMetadata(36, PropertyChanged));
        public static readonly DependencyProperty Mod3DProperty = DependencyProperty.Register("Mod3D", typeof(ObservableCollection<ResolvedGeometryMod>), typeof(CylindricalMeshBase), new PropertyMetadata(new ObservableCollection<ResolvedGeometryMod>(), PropertyChanged));
        public ObservableCollection<ResolvedGeometryMod> Mod3D { set { SetValue(Mod3DProperty, value); } get { return (ObservableCollection<ResolvedGeometryMod>)GetValue(Mod3DProperty); } }


        public int ThetaStart { set { SetValue(ThetaStartProperty, value); } get { return (int)GetValue(ThetaStartProperty); } }
        public int ThetaStop { set { SetValue(ThetaStopProperty, value); } get { return (int)GetValue(ThetaStopProperty); } }

        public int ThetaDiv { set { SetValue(ThetaDivProperty, value); } get { return (int)GetValue(ThetaDivProperty); } }
        public double Length { set { SetValue(LengthProperty, value); } get { return (double)GetValue(LengthProperty); } }
        public double Radius { set { SetValue(RadiusProperty, value); } get { return (double)GetValue(RadiusProperty); } }
        public int Slices { set { SetValue(SlicesProperty, value); } get { return (int)GetValue(SlicesProperty); } }
        public int Stacks { set { SetValue(StacksProperty, value); } get { return (int)GetValue(StacksProperty); } }

        public Point3D Point1 { set { SetValue(Point1Property, value); } get { return (Point3D)GetValue(Point1Property); } }
        public Point3D Point2 { set { SetValue(Point2Property, value); } get { return (Point3D)GetValue(Point2Property); } }

        static bool ValidateSlices(object obj) { return (int)obj > 2; }
        static bool ValidateStacks(object obj) { return (int)obj > 0; }

        protected static void Point1PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var meshGenerator = obj as CylindricalMeshBase;
            if (meshGenerator == null) return;

            meshGenerator.Point1 = new Point3D(meshGenerator.Point1X, meshGenerator.Point1Y, meshGenerator.Point1Z);
            (obj as CylindricalMeshBase).Refresh();
        }
        protected static void Point2PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var meshGenerator = obj as CylindricalMeshBase;
            if (meshGenerator == null) return;

            meshGenerator.Point2 = new Point3D(meshGenerator.Point2X, meshGenerator.Point2Y, meshGenerator.Point2Z);
            (obj as CylindricalMeshBase).PropertyChanged(args);
        }

        protected virtual void PropertyChanged(DependencyPropertyChangedEventArgs args)
        {
            // Get the MeshGeometry3D for local convenience.
            MeshGeometry3D mesh = Geometry;

            // Obtain the four collection of the MeshGeometry3D.
            Point3DCollection vertices = mesh.Positions;
            Vector3DCollection normals = mesh.Normals;
            Int32Collection indices = mesh.TriangleIndices;
            PointCollection textures = mesh.TextureCoordinates;

            // Set the MeshGeometry3D collections to null while updating.
            mesh.Positions = null;
            mesh.TriangleIndices = null;
            mesh.TextureCoordinates = null;
            mesh.Normals = null;

            // Call the abstract method to fill the collections.
            Triangulate(args, vertices, normals, indices, textures);

            // Set the updated collections to the MeshGeometry3D.
            mesh.TextureCoordinates = textures;
            mesh.TriangleIndices = indices;
            mesh.Positions = vertices;
            mesh.Normals = normals;
        }
    }
}