using Earthblend.Tts.Common.Core.MeshGenerators.Base;
using Earthblend.Tts.Common.Core;
using Earthblend.Tts.Common.Core.CustomEventArgs;
using Earthblend.Tts.Shell.Members;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Xml.Serialization;
using Earthblend.DynamicCompile;
using Earthblend.Tts.Common.Core.Members;
using Earthblend.Nts.Common.MeshGenerators.Base;

namespace Earthblend.Tts.Common.Core.MeshGenerators
{
    public class HoseMeshGenerator : MeshGeneratorBase
    {
        public HoseMeshGenerator()
        {
            PropertyChanged(new DependencyPropertyChangedEventArgs());
        }


        private HoseConfigurationDataItem _selectedHoseConfigurationDataItem = null;

        [XmlIgnore] public HoseConfigurationDataItem SelectedHoseConfigurationDataItem
        {
            get { return _selectedHoseConfigurationDataItem; }
            set
            {
                if (_selectedHoseConfigurationDataItem == value) return;
                SetProperty(ref _selectedHoseConfigurationDataItem, value);
            }
        }

        #region Commands


        private ICommand _decreaseVectorXCommandIc; [XmlIgnore] public ICommand DecreaseVectorXCommandIc => _decreaseVectorXCommandIc ?? (_decreaseVectorXCommandIc = new RelayCommand<Object>(DecreaseVectorXCommandIcExecuted));
        public void DecreaseVectorXCommandIcExecuted(Object sender) 
        { 
            if (sender == null) return; 
            SelectedHoseConfigurationDataItem = (HoseConfigurationDataItem)sender; 
            SelectedHoseConfigurationDataItem.DirectionX -= MovementValue;
            ActuateTriangulation = !ActuateTriangulation;
        }
        private ICommand _increaseVectorXCommandIc; [XmlIgnore] public ICommand IncreaseVectorXCommandIc => _increaseVectorXCommandIc ?? (_increaseVectorXCommandIc = new RelayCommand<Object>(IncreaseVectorXCommandIcExecuted));
        public void IncreaseVectorXCommandIcExecuted(Object sender) 
        { 
            if (sender == null) return; 
            SelectedHoseConfigurationDataItem = (HoseConfigurationDataItem)sender; 
            SelectedHoseConfigurationDataItem.DirectionX += MovementValue;
            ActuateTriangulation = !ActuateTriangulation;
        }

        private ICommand _decreaseVectorYCommandIc; [XmlIgnore] public ICommand DecreaseVectorYCommandIc => _decreaseVectorYCommandIc ?? (_decreaseVectorYCommandIc = new RelayCommand<Object>(DecreaseVectorYCommandIcExecuted));
        public void DecreaseVectorYCommandIcExecuted(Object sender)
        {
            if (sender == null) return;
            SelectedHoseConfigurationDataItem = (HoseConfigurationDataItem)sender;
            SelectedHoseConfigurationDataItem.DirectionY -= MovementValue;
            ActuateTriangulation = !ActuateTriangulation;
        }
        private ICommand _increaseVectorYCommandIc; [XmlIgnore] public ICommand IncreaseVectorYCommandIc => _increaseVectorYCommandIc ?? (_increaseVectorYCommandIc = new RelayCommand<Object>(IncreaseVectorYCommandIcExecuted));
        public void IncreaseVectorYCommandIcExecuted(Object sender)
        {
            if (sender == null) return;
            SelectedHoseConfigurationDataItem = (HoseConfigurationDataItem)sender;
            SelectedHoseConfigurationDataItem.DirectionY += MovementValue;
            ActuateTriangulation = !ActuateTriangulation;
        }

        private ICommand _decreaseVectorZCommandIc; [XmlIgnore] public ICommand DecreaseVectorZCommandIc => _decreaseVectorZCommandIc ?? (_decreaseVectorZCommandIc = new RelayCommand<Object>(DecreaseVectorZCommandIcExecuted));
        public void DecreaseVectorZCommandIcExecuted(Object sender)
        {
            if (sender == null) return;
            SelectedHoseConfigurationDataItem = (HoseConfigurationDataItem)sender;
            SelectedHoseConfigurationDataItem.DirectionZ -= MovementValue;
            ActuateTriangulation = !ActuateTriangulation;
        }
        private ICommand _increaseVectorZCommandIc; [XmlIgnore] public ICommand IncreaseVectorZCommandIc => _increaseVectorZCommandIc ?? (_increaseVectorZCommandIc = new RelayCommand<Object>(IncreaseVectorZCommandIcExecuted));
        public void IncreaseVectorZCommandIcExecuted(Object sender)
        {
            if (sender == null) return;
            SelectedHoseConfigurationDataItem = (HoseConfigurationDataItem)sender;
            SelectedHoseConfigurationDataItem.DirectionZ += MovementValue;
            ActuateTriangulation = !ActuateTriangulation;
        }

        private ICommand _addSegmentCommandIc; [XmlIgnore] public ICommand AddSegmentCommandIc => _addSegmentCommandIc ?? (_addSegmentCommandIc = new RelayCommand<Object>(AddSegmentCommandIcExecuted));
        public void AddSegmentCommandIcExecuted(Object sender) { if (HoseConfigList == null) HoseConfigList = new ObservableCollection<HoseConfigurationDataItem>(); HoseConfigList.Add(new HoseConfigurationDataItem()); }

        private ICommand _RemoveSegmentCommandIc; [XmlIgnore] public ICommand RemoveSegmentCommandIc => _RemoveSegmentCommandIc ?? (_RemoveSegmentCommandIc = new RelayCommand<Object>(RemoveSegmentCommandIcExecuted));
        public void RemoveSegmentCommandIcExecuted(Object sender)
        {
            if (HoseConfigList == null) return;
            if (SelectedHoseConfigurationDataItem == null) return;
            HoseConfigList.Remove(SelectedHoseConfigurationDataItem);
            ActuateTriangulation = !ActuateTriangulation;
        }


        private ICommand _decreaseThetaStartCommandIc; [XmlIgnore] public ICommand DecreaseThetaStartCommandIc => _decreaseThetaStartCommandIc ?? (_decreaseThetaStartCommandIc = new RelayCommand<Object>(DecreaseThetaStartCommandIcExecuted));
        public void DecreaseThetaStartCommandIcExecuted(Object sender) { ThetaStart -= 1; }

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



        private ICommand _decreaseDiameterCommandIc; [XmlIgnore] public ICommand DecreaseDiameterCommandIc => _decreaseDiameterCommandIc ?? (_decreaseDiameterCommandIc = new RelayCommand<Object>(DecreaseDiameterCommandIcExecuted));
        public void DecreaseDiameterCommandIcExecuted(Object sender) { Diameter -= MovementValue; }

        private ICommand _increaseDiameterCommandIc; [XmlIgnore] public ICommand IncreaseDiameterCommandIc => _increaseDiameterCommandIc ?? (_increaseDiameterCommandIc = new RelayCommand<Object>(IncreaseDiameterCommandIcExecuted));
        public void IncreaseDiameterCommandIcExecuted(Object sender) { Diameter += MovementValue; }


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

        private ICommand _increasePoint1XCommandIc; [XmlIgnore] public ICommand IncreasePoint1XCommandIc => _increasePoint1XCommandIc ?? (_increasePoint1XCommandIc = new RelayCommand<Object>(IncreasePoint1XCommandIcExecuted));
        public void IncreasePoint1XCommandIcExecuted(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 _increasePoint1YCommandIc; [XmlIgnore] public ICommand IncreasePoint1YCommandIc => _increasePoint1YCommandIc ?? (_increasePoint1YCommandIc = new RelayCommand<Object>(IncreasePoint1YCommandIcExecuted));
        public void IncreasePoint1YCommandIcExecuted(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 _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 _increasePoint2XCommandIc; [XmlIgnore] public ICommand IncreasePoint2XCommandIc => _increasePoint2XCommandIc ?? (_increasePoint2XCommandIc = new RelayCommand<Object>(IncreasePoint2XCommandIcExecuted));
        public void IncreasePoint2XCommandIcExecuted(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 _increasePoint2YCommandIc; [XmlIgnore] public ICommand IncreasePoint2YCommandIc => _increasePoint2YCommandIc ?? (_increasePoint2YCommandIc = new RelayCommand<Object>(IncreasePoint2YCommandIcExecuted));
        public void IncreasePoint2YCommandIcExecuted(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 _increasePoint2ZCommandIc; [XmlIgnore] public ICommand IncreasePoint2ZCommandIc => _increasePoint2ZCommandIc ?? (_increasePoint2ZCommandIc = new RelayCommand<Object>(IncreasePoint2ZCommandIcExecuted));
        public void IncreasePoint2ZCommandIcExecuted(Object sender) { Point2Z += MovementValue; }

        private ICommand _decreaseSlicesCommandIc; [XmlIgnore] public ICommand DecreaseSlicesCommandIc => _decreaseSlicesCommandIc ?? (_decreaseSlicesCommandIc = new RelayCommand<Object>(DecreaseSlicesCommandIcExecuted));
        public void DecreaseSlicesCommandIcExecuted(Object sender) { Slices -= 1; }

        private ICommand _increaseSlicesCommandIc; [XmlIgnore] public ICommand IncreaseSlicesCommandIc => _increaseSlicesCommandIc ?? (_increaseSlicesCommandIc = new RelayCommand<Object>(IncreaseSlicesCommandIcExecuted));
        public void IncreaseSlicesCommandIcExecuted(Object sender) { Slices += 1; }


        private ICommand _decreaseInnerRadiusCommandIc; [XmlIgnore] public ICommand DecreaseInnerRadiusCommandIc => _decreaseInnerRadiusCommandIc ?? (_decreaseInnerRadiusCommandIc = new RelayCommand<Object>(DecreaseInnerRadiusCommandIcExecuted));
        public void DecreaseInnerRadiusCommandIcExecuted(Object sender) { InnerRadius -= MovementValue; }

        private ICommand _increaseInnerRadiusCommandIc; [XmlIgnore] public ICommand IncreaseInnerRadiusCommandIc => _increaseInnerRadiusCommandIc ?? (_increaseInnerRadiusCommandIc = new RelayCommand<Object>(IncreaseInnerRadiusCommandIcExecuted));
        public void IncreaseInnerRadiusCommandIcExecuted(Object sender) { InnerRadius += MovementValue; }

        private ICommand _decreaseRadiusCommandIc; [XmlIgnore] public ICommand DecreaseRadiusCommandIc => _decreaseRadiusCommandIc ?? (_decreaseRadiusCommandIc = new RelayCommand<Object>(DecreaseRadiusCommandIcExecuted));
        public void DecreaseRadiusCommandIcExecuted(Object sender) { Radius -= MovementValue; }
        private ICommand _increaseRadiusCommandIc; [XmlIgnore] public ICommand IncreaseRadiusCommandIc => _increaseRadiusCommandIc ?? (_increaseRadiusCommandIc = new RelayCommand<Object>(IncreaseRadiusCommandIcExecuted));
        public void IncreaseRadiusCommandIcExecuted(Object sender) { Radius += MovementValue; }




        private ICommand _decreaseHeadLengthCommandIc; [XmlIgnore] public ICommand DecreaseHeadLengthCommandIc => _decreaseHeadLengthCommandIc ?? (_decreaseHeadLengthCommandIc = new RelayCommand<Object>(DecreaseHeadLengthCommandIcExecuted));
        public void DecreaseHeadLengthCommandIcExecuted(Object sender) { HeadLength -= MovementValue; }
        private ICommand _increaseHeadLengthCommandIc; [XmlIgnore] public ICommand IncreaseHeadLengthCommandIc => _increaseHeadLengthCommandIc ?? (_increaseHeadLengthCommandIc = new RelayCommand<Object>(IncreaseHeadLengthCommandIcExecuted));
        public void IncreaseHeadLengthCommandIcExecuted(Object sender) { HeadLength += MovementValue; }

        #endregion

        public static IList<String> Properties = new List<String>() { "HeadLength", "Point1X", "Point1Y", "Point1Z", "Point2X", "Point2Y", "Point2Z", "Radius", "Slices" }; //, "Stacks" };

        public static readonly DependencyProperty HeadLengthProperty = DependencyProperty.Register("HeadLength", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(1.0D, PropertyChanged));
        public Double HeadLength { set { SetValue(HeadLengthProperty, value); } get { return (Double)GetValue(HeadLengthProperty); } }

        public HoseMeshGenerator Duplicate()
        {
            HoseMeshGenerator meshGenerator = new HoseMeshGenerator();
            meshGenerator.HeadLength = HeadLength;
            meshGenerator.Radius = Radius;
            meshGenerator.Slices = Slices;

            meshGenerator.Point1X = Point1X;
            meshGenerator.Point1Y = Point1Y;
            meshGenerator.Point1Z = Point1Z;
            meshGenerator.Point2X = Point2X;
            meshGenerator.Point2Y = Point2Y;
            meshGenerator.Point2Z = Point2Z;

            return meshGenerator;
        }

        public static readonly DependencyProperty HoseConfigListProperty = DependencyProperty.Register("HoseConfigList", typeof(ObservableCollection<HoseConfigurationDataItem>), typeof(HoseMeshGenerator), new PropertyMetadata(null, PropertyChanged));
        public static readonly DependencyProperty ActuateTriangulationProperty = DependencyProperty.Register("ActuateTriangulation", typeof(Boolean), typeof(HoseMeshGenerator), new PropertyMetadata(false, PropertyChanged));

        public static readonly DependencyProperty SlicesProperty = DependencyProperty.Register("Slices", typeof(int), typeof(HoseMeshGenerator), new PropertyMetadata(36, PropertyChanged));
        public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(1D, PropertyChanged));
        public static readonly DependencyProperty ThetaStartProperty = DependencyProperty.Register("ThetaStart", typeof(int), typeof(HoseMeshGenerator), new PropertyMetadata(default(int), PropertyChanged));
        public static readonly DependencyProperty ThetaStopProperty = DependencyProperty.Register("ThetaStop", typeof(int), typeof(HoseMeshGenerator), new PropertyMetadata(36, PropertyChanged));
        public static readonly DependencyProperty DiameterProperty = DependencyProperty.Register("Diameter", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(1D, PropertyChanged));

        public static readonly DependencyProperty Point1XProperty = DependencyProperty.Register("Point1X", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(0D, PropertyChanged));
        public static readonly DependencyProperty Point1YProperty = DependencyProperty.Register("Point1Y", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(-1D, PropertyChanged));
        public static readonly DependencyProperty Point1ZProperty = DependencyProperty.Register("Point1Z", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(0D, PropertyChanged));
        public static readonly DependencyProperty Point2XProperty = DependencyProperty.Register("Point2X", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(1D, PropertyChanged));
        public static readonly DependencyProperty Point2YProperty = DependencyProperty.Register("Point2Y", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(0D, PropertyChanged));
        public static readonly DependencyProperty Point2ZProperty = DependencyProperty.Register("Point2Z", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(0D, PropertyChanged));
        public static readonly DependencyProperty InnerRadiusProperty = DependencyProperty.Register("InnerRadius", typeof(Double), typeof(HoseMeshGenerator), new PropertyMetadata(1D, PropertyChanged));

        public ObservableCollection<HoseConfigurationDataItem> HoseConfigList { set { SetValue(HoseConfigListProperty, value); } get { return (ObservableCollection<HoseConfigurationDataItem>)GetValue(HoseConfigListProperty); } }

        public Double InnerRadius { set { SetValue(InnerRadiusProperty, value); } get { return (Double)GetValue(InnerRadiusProperty); } }
        public Boolean ActuateTriangulation { set { SetValue(ActuateTriangulationProperty, value); } get { return (Boolean)GetValue(ActuateTriangulationProperty); } }

        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 ThetaStart { set { SetValue(ThetaStartProperty, value); } get { return (int)GetValue(ThetaStartProperty); } }
        public int ThetaStop { set { SetValue(ThetaStopProperty, value); } get { return (int)GetValue(ThetaStopProperty); } }

        public Double Diameter { set { SetValue(DiameterProperty, value); } get { return (Double)GetValue(DiameterProperty); } }

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


        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();

            var hoseVectors = new List<Vector3D>() { };// new Vector3D(0, 0, 0), new Vector3D(1, 2, 0), new Vector3D(1, 2, 0), new Vector3D(1.1, 2, 3), new Vector3D(2, 4, 2) };

            if(HoseConfigList != null)
            {
                List<Vector3D> hoseList = new List<Vector3D>();
                foreach(var h in HoseConfigList)
                {
                    hoseList.Add(h.Direction);
                }
                hoseVectors = hoseList;
            }

            var edges = new List<int>() { 0, 1, 2, 3 };
            edges.Clear();
            for (int i = 0; i <= hoseVectors.Count-2; i++) edges.Add(i);
            meshBuilder.AddPipes(hoseVectors, edges, Diameter, Slices, 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));
        }
    }
}