using Earthblend.DynamicCompile;
using Earthblend.Nts.Common.MeshGenerators.Base;
using Earthblend.Tts.Common.Core;
using Earthblend.Tts.Shell;
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Xml.Serialization;

namespace Earthblend.Tts.Common.Core.MeshGenerators.Base
{
    public abstract class CylindricalMeshBase : MeshGeneratorBase
    {
        private ICommand _decreaseThetaStartCommandIc;[XmlIgnore] public ICommand DecreaseThetaStartCommandIc => _decreaseThetaStartCommandIc ?? (_decreaseThetaStartCommandIc = new RelayCommand<Object>(DecreaseThetaStartCommandIcExecuted));
        public void DecreaseThetaStartCommandIcExecuted(Object sender) { ThetaStart -= 1; }
        private ICommand _decreaseThetaStopCommandIc;[XmlIgnore] public ICommand DecreaseThetaStopCommandIc => _decreaseThetaStopCommandIc ?? (_decreaseThetaStopCommandIc = new RelayCommand<Object>(DecreaseThetaStopCommandIcExecuted));
        public void DecreaseThetaStopCommandIcExecuted(Object sender) { ThetaStop -= 1; }


        private ICommand _increaseThetaStartCommandIc;[XmlIgnore] public ICommand IncreaseThetaStartCommandIc => _increaseThetaStartCommandIc ?? (_increaseThetaStartCommandIc = new RelayCommand<Object>(IncreaseThetaStartCommandIcExecuted));
        public void IncreaseThetaStartCommandIcExecuted(Object sender) { ThetaStart += 1; }
        private ICommand _increaseThetaStopCommandIc;[XmlIgnore] public ICommand IncreaseThetaStopCommandIc => _increaseThetaStopCommandIc ?? (_increaseThetaStopCommandIc = new RelayCommand<Object>(IncreaseThetaStopCommandIcExecuted));
        public void IncreaseThetaStopCommandIcExecuted(Object sender) { ThetaStop += 1; }



        private ICommand _decreasePoint1XCommandIc;[XmlIgnore] public ICommand DecreasePoint1XCommandIc => _decreasePoint1XCommandIc ?? (_decreasePoint1XCommandIc = new RelayCommand<Object>(DecreasePoint1XCommandIcExecuted));
        public void DecreasePoint1XCommandIcExecuted(Object sender) { Point1X-=MovementValue; }

        private ICommand _decreasePoint1YCommandIc;[XmlIgnore] public ICommand DecreasePoint1YCommandIc => _decreasePoint1YCommandIc ?? (_decreasePoint1YCommandIc = new RelayCommand<Object>(DecreasePoint1YCommandIcExecuted));
        public void DecreasePoint1YCommandIcExecuted(Object sender) { Point1Y -= MovementValue; }

        private ICommand _decreasePoint1ZCommandIc;[XmlIgnore] public ICommand DecreasePoint1ZCommandIc => _decreasePoint1ZCommandIc ?? (_decreasePoint1ZCommandIc = new RelayCommand<Object>(DecreasePoint1ZCommandIcExecuted));
        public void DecreasePoint1ZCommandIcExecuted(Object sender) { Point1Z -= MovementValue; }


        private ICommand _increasePoint1XCommandIc;[XmlIgnore] public ICommand IncreasePoint1XCommandIc => _increasePoint1XCommandIc ?? (_increasePoint1XCommandIc = new RelayCommand<Object>(IncreasePoint1XCommandIcExecuted));
        public void IncreasePoint1XCommandIcExecuted(Object sender) { Point1X += MovementValue; }
        private ICommand _increasePoint1YCommandIc;[XmlIgnore] public ICommand IncreasePoint1YCommandIc => _increasePoint1YCommandIc ?? (_increasePoint1YCommandIc = new RelayCommand<Object>(IncreasePoint1YCommandIcExecuted));
        public void IncreasePoint1YCommandIcExecuted(Object sender) { Point1Y += MovementValue; }
        private ICommand _increasePoint1ZCommandIc;[XmlIgnore] public ICommand IncreasePoint1ZCommandIc => _increasePoint1ZCommandIc ?? (_increasePoint1ZCommandIc = new RelayCommand<Object>(IncreasePoint1ZCommandIcExecuted));
        public void IncreasePoint1ZCommandIcExecuted(Object sender) { Point1Z += MovementValue; }

        private ICommand _decreasePoint2XCommandIc;[XmlIgnore] public ICommand DecreasePoint2XCommandIc => _decreasePoint2XCommandIc ?? (_decreasePoint2XCommandIc = new RelayCommand<Object>(DecreasePoint2XCommandIcExecuted));
        public void DecreasePoint2XCommandIcExecuted(Object sender) { Point2X -= MovementValue; }

        private ICommand _decreasePoint2YCommandIc;[XmlIgnore] public ICommand DecreasePoint2YCommandIc => _decreasePoint2YCommandIc ?? (_decreasePoint2YCommandIc = new RelayCommand<Object>(DecreasePoint2YCommandIcExecuted));
        public void DecreasePoint2YCommandIcExecuted(Object sender) { Point2Y -= MovementValue; }

        private ICommand _decreasePoint2ZCommandIc;[XmlIgnore] public ICommand DecreasePoint2ZCommandIc => _decreasePoint2ZCommandIc ?? (_decreasePoint2ZCommandIc = new RelayCommand<Object>(DecreasePoint2ZCommandIcExecuted));
        public void DecreasePoint2ZCommandIcExecuted(Object sender) { Point2Z -= MovementValue; }


        private ICommand _increasePoint2XCommandIc;[XmlIgnore] public ICommand IncreasePoint2XCommandIc => _increasePoint2XCommandIc ?? (_increasePoint2XCommandIc = new RelayCommand<Object>(IncreasePoint2XCommandIcExecuted));
        public void IncreasePoint2XCommandIcExecuted(Object sender) { Point2X += MovementValue; }
        private ICommand _increasePoint2YCommandIc;[XmlIgnore] public ICommand IncreasePoint2YCommandIc => _increasePoint2YCommandIc ?? (_increasePoint2YCommandIc = new RelayCommand<Object>(IncreasePoint2YCommandIcExecuted));
        public void IncreasePoint2YCommandIcExecuted(Object sender) { Point2Y += MovementValue; }
        private ICommand _increasePoint2ZCommandIc;[XmlIgnore] public ICommand IncreasePoint2ZCommandIc => _increasePoint2ZCommandIc ?? (_increasePoint2ZCommandIc = new RelayCommand<Object>(IncreasePoint2ZCommandIcExecuted));
        public void IncreasePoint2ZCommandIcExecuted(Object sender) { Point2Z += MovementValue; }

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

        public void DecreaseRadiusCommandIcExecuted(Object sender) { Radius -= MovementValue; }
        public void IncreaseRadiusCommandIcExecuted(Object sender) { Radius += MovementValue; }
        public void DecreaseLengthCommandIcExecuted(Object sender) { Length -= MovementValue; }
        public void IncreaseLengthCommandIcExecuted(Object sender) { Length += MovementValue; }
        public void DecreaseStacksCommandIcExecuted(Object sender) { if(Stacks-1>0) Stacks--; }
        public void IncreaseSlicesCommandIcExecuted(Object sender) { Slices++; }
        public void IncreaseStacksCommandIcExecuted(Object sender) { Stacks++; }
        public void DecreaseSlicesCommandIcExecuted(Object sender) { if(Slices-1>0) Slices--; }

        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(1.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(1, 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 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).PropertyChanged(args);
        }
        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.Normals = null;
            mesh.TriangleIndices = null;
            mesh.TextureCoordinates = 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.Normals = normals;
            mesh.Positions = vertices;
        }
    }
}

