using SurfacePlot3D.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 class SphereMeshGenerator : MeshGeneratorBase
    {
        #region Constructor(s)

        public SphereMeshGenerator()
        {
            PropertyChanged(new DependencyPropertyChangedEventArgs());
        }

        #endregion

        public static IList<String> Properties = new List<String>() { "CenterX", "CenterY", "CenterZ", "Radius", "Slices", "Stacks", "PhiStart", "PhiEnd" };
        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 _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));
        private ICommand _increasePhiStartCommandIc; [XmlIgnore] public ICommand IncreasePhiStartCommandIc => _increasePhiStartCommandIc ?? (_increasePhiStartCommandIc = new RelayCommand<Object>(IncreasePhiStartCommandIcExecuted));
        private ICommand _decreasePhiStartCommandIc; [XmlIgnore] public ICommand DecreasePhiStartCommandIc => _decreasePhiStartCommandIc ?? (_decreasePhiStartCommandIc = new RelayCommand<Object>(DecreasePhiStartCommandIcExecuted));
        private ICommand _increasePhiEndCommandIc; [XmlIgnore] public ICommand IncreasePhiEndCommandIc => _increasePhiEndCommandIc ?? (_increasePhiEndCommandIc = new RelayCommand<Object>(IncreasePhiEndCommandIcExecuted));
        private ICommand _decreasePhiEndCommandIc; [XmlIgnore] public ICommand DecreasePhiEndCommandIc => _decreasePhiEndCommandIc ?? (_decreasePhiEndCommandIc = new RelayCommand<Object>(DecreasePhiEndCommandIcExecuted));

        private void DecreasePhiStartCommandIcExecuted(Object sender) { PhiStart--; }
        private void IncreasePhiStartCommandIcExecuted(Object sender) { PhiStart++; }
        private void DecreasePhiEndCommandIcExecuted(Object sender) { PhiEnd--; }
        private void IncreasePhiEndCommandIcExecuted(Object sender) { PhiEnd++; }
        private void DecreaseRadiusCommandIcExecuted(Object sender) { Radius -= MovementValue; }
        private void IncreaseRadiusCommandIcExecuted(Object sender) { Radius += MovementValue; }
        private void DecreaseStacksCommandIcExecuted(Object sender) { Stacks--; }
        private void IncreaseStacksCommandIcExecuted(Object sender) { Stacks++; }
        private void DecreaseSlicesCommandIcExecuted(Object sender) { Slices--; }
        private void IncreaseSlicesCommandIcExecuted(Object sender) { Slices++; }

        public int PhiStart { set { SetValue(PhiStartProperty, value); } get { return (int)GetValue(PhiStartProperty); } }
        public int PhiEnd { set { SetValue(PhiEndProperty, value); } get { return (int)GetValue(PhiEndProperty); } }


        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 Center { set { SetValue(CenterProperty, value); } get { return (Point3D)GetValue(CenterProperty); } }
        public Double CenterX { set { SetValue(CenterXProperty, value); } get { return (Double)GetValue(CenterXProperty); } }
        public Double CenterY { set { SetValue(CenterYProperty, value); } get { return (Double)GetValue(CenterYProperty); } }
        public Double CenterZ { set { SetValue(CenterZProperty, value); } get { return (Double)GetValue(CenterZProperty); } }
        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 static readonly DependencyProperty CenterProperty = DependencyProperty.Register("Center", typeof(Point3D), typeof(SphereMeshGenerator), new PropertyMetadata(new Point3D(), PropertyChanged));
        public static readonly DependencyProperty CenterXProperty = DependencyProperty.Register("CenterX", typeof(Double), typeof(SphereMeshGenerator), new PropertyMetadata(0.0D, CenterXYZPropertyChanged));
        public static readonly DependencyProperty CenterYProperty = DependencyProperty.Register("CenterY", typeof(Double), typeof(SphereMeshGenerator), new PropertyMetadata(0.0D, CenterXYZPropertyChanged));
        public static readonly DependencyProperty CenterZProperty = DependencyProperty.Register("CenterZ", typeof(Double), typeof(SphereMeshGenerator), new PropertyMetadata(0.0D, CenterXYZPropertyChanged));

        public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(SphereMeshGenerator), new PropertyMetadata(1.0D, PropertyChanged));
        public static readonly DependencyProperty SlicesProperty = DependencyProperty.Register("Slices", typeof(int), typeof(SphereMeshGenerator), new PropertyMetadata(36, PropertyChanged), ValidateSlices);
        public static readonly DependencyProperty StacksProperty = DependencyProperty.Register("Stacks", typeof(int), typeof(SphereMeshGenerator), new PropertyMetadata(18, PropertyChanged), ValidateStacks);

        public static readonly DependencyProperty PhiStartProperty = DependencyProperty.Register("PhiStart", typeof(int), typeof(SphereMeshGenerator), new PropertyMetadata(0, PropertyChanged));
        public static readonly DependencyProperty PhiEndProperty = DependencyProperty.Register("PhiEnd", typeof(int), typeof(SphereMeshGenerator), new PropertyMetadata(0, PropertyChanged));
        public static readonly DependencyProperty ThetaStartProperty = DependencyProperty.Register("ThetaStart", typeof(int), typeof(SphereMeshGenerator), new PropertyMetadata(0, PropertyChanged));
        public static readonly DependencyProperty ThetaStopProperty = DependencyProperty.Register("ThetaStop", typeof(int), typeof(SphereMeshGenerator), new PropertyMetadata(36, PropertyChanged));


        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; }

        protected static void CenterXYZPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            (obj as SphereMeshGenerator).PropertyChanged(args);
            (obj as SphereMeshGenerator).Center = new Point3D((obj as SphereMeshGenerator).CenterX, (obj as SphereMeshGenerator).CenterY, (obj as SphereMeshGenerator).CenterZ);
        }

        #region Static Validation

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

        #endregion

        public SphereMeshGenerator Duplicate()
        {
            SphereMeshGenerator meshGenerator = new SphereMeshGenerator();

            meshGenerator.CenterX = CenterX;
            meshGenerator.CenterY = CenterY;
            meshGenerator.CenterZ = CenterZ;
            meshGenerator.Center = Center;
            meshGenerator.Radius = Radius;
            meshGenerator.Slices = Slices;
            meshGenerator.Stacks = Stacks;
            meshGenerator.PhiStart = PhiStart;
            meshGenerator.PhiEnd = PhiEnd;

            return meshGenerator;
        }

        #region Overrides

        protected override void Triangulate(DependencyPropertyChangedEventArgs args, Point3DCollection vertices, Vector3DCollection normals, Int32Collection indices, PointCollection textures)
        {
            vertices.Clear(); normals.Clear(); indices.Clear(); textures.Clear();

            CustomMeshBuilder meshBuilder = new CustomMeshBuilder();

            //Point3D center, double radius = 1, int thetaDiv = 32, int phiDiv = 32,int phiStart=0,int phiEnd=0
            meshBuilder.AddSphere(Center, Radius, Slices, Stacks, ThetaStart, ThetaStop);

            vertices = meshBuilder.Positions;
            normals = meshBuilder.Normals;
            indices = meshBuilder.TriangleIndices;
            textures = meshBuilder.TextureCoordinates;

            Geometry = new MeshGeometry3D() { Positions = vertices, Normals = normals, TriangleIndices = indices, TextureCoordinates = textures };

            meshBuilder = null;

            OnMeshChanged(new MeshEventArgs(Geometry as MeshGeometry3D));

        }

        #endregion
    }
}
