using CtrEditor.ObjetosSim;
using System.Collections.ObjectModel;
using System.Windows;

namespace CtrEditor
{
    public class ObjectAlignment
    {
        private readonly ObservableCollection<osBase> _selectedObjects;
        private readonly osBase _referenceObject;

        public ObjectAlignment(ObservableCollection<osBase> selectedObjects, osBase referenceObject = null)
        {
            _selectedObjects = selectedObjects;
            _referenceObject = referenceObject ?? selectedObjects.FirstOrDefault();
        }

        public void AlignLeft()
        {
            if (_selectedObjects.Count <= 1) return;
            
            float leftMost = _selectedObjects.Min(obj => obj.Left);
            foreach (var obj in _selectedObjects)
            {
                obj.Left = leftMost;
            }
        }

        public void AlignRight()
        {
            if (_selectedObjects.Count <= 1) return;
            
            float rightMost = _selectedObjects.Max(obj => obj.Left + obj.Ancho);
            foreach (var obj in _selectedObjects)
            {
                obj.Left = rightMost - obj.Ancho;
            }
        }

        public void AlignTop()
        {
            if (_selectedObjects.Count <= 1) return;
            
            float topMost = _selectedObjects.Min(obj => obj.Top);
            foreach (var obj in _selectedObjects)
            {
                obj.Top = topMost;
            }
        }

        public void AlignBottom()
        {
            if (_selectedObjects.Count <= 1) return;
            
            float bottomMost = _selectedObjects.Max(obj => obj.Top + obj.Alto);
            foreach (var obj in _selectedObjects)
            {
                obj.Top = bottomMost - obj.Alto;
            }
        }

        public void AlignCenterHorizontally()
        {
            if (_selectedObjects.Count <= 1) return;
            
            float averageY = _selectedObjects.Average(obj => obj.Top + obj.Alto / 2);
            foreach (var obj in _selectedObjects)
            {
                obj.Top = averageY - obj.Alto / 2;
            }
        }

        public void AlignCenterVertically()
        {
            if (_selectedObjects.Count <= 1) return;
            
            float averageX = _selectedObjects.Average(obj => obj.Left + obj.Ancho / 2);
            foreach (var obj in _selectedObjects)
            {
                obj.Left = averageX - obj.Ancho / 2;
            }
        }

        public void DistributeHorizontally()
        {
            if (_selectedObjects.Count <= 2) return;

            var objectsWithCenters = _selectedObjects
                .Select(obj => new 
                {
                    Object = obj,
                    Center = GetObjectCenter(obj),
                    Dimensions = GetEffectiveDimensions(obj)
                })
                .OrderBy(x => x.Center.X)
                .ToList();

            float leftMost = (float)objectsWithCenters.First().Center.X;
            float rightMost = (float)objectsWithCenters.Last().Center.X;
            float totalDistance = rightMost - leftMost;
            float spacing = totalDistance / (_selectedObjects.Count - 1);

            for (int i = 1; i < objectsWithCenters.Count - 1; i++)
            {
                var obj = objectsWithCenters[i];
                var targetX = leftMost + (spacing * i);
                float deltaX = (float)(targetX - obj.Center.X);
                obj.Object.Left += deltaX;
            }
        }

        public void DistributeVertically()
        {
            if (_selectedObjects.Count <= 2) return;

            var objectsWithCenters = _selectedObjects
                .Select(obj => new 
                {
                    Object = obj,
                    Center = GetObjectCenter(obj),
                    Dimensions = GetEffectiveDimensions(obj)
                })
                .OrderBy(x => x.Center.Y)
                .ToList();

            float topMost = (float)objectsWithCenters.First().Center.Y;
            float bottomMost = (float)objectsWithCenters.Last().Center.Y;
            float totalDistance = bottomMost - topMost;
            float spacing = totalDistance / (_selectedObjects.Count - 1);

            for (int i = 1; i < objectsWithCenters.Count - 1; i++)
            {
                var obj = objectsWithCenters[i];
                var targetY = topMost + (spacing * i);
                float deltaY = (float)(targetY - obj.Center.Y);
                obj.Object.Top += deltaY;
            }
        }

        public void EqualWidth()
        {
            if (_selectedObjects.Count <= 1) return;
            
            float referenceWidth = GetEffectiveDimensions(_referenceObject).Width;
            foreach (var obj in _selectedObjects.Where(o => o != _referenceObject))
            {
                var currentDims = GetEffectiveDimensions(obj);
                SetEffectiveDimensions(obj, referenceWidth, currentDims.Height);
            }
        }

        public void EqualHeight()
        {
            if (_selectedObjects.Count <= 1) return;
            
            float referenceHeight = GetEffectiveDimensions(_referenceObject).Height;
            foreach (var obj in _selectedObjects.Where(o => o != _referenceObject))
            {
                var currentDims = GetEffectiveDimensions(obj);
                SetEffectiveDimensions(obj, currentDims.Width, referenceHeight);
            }
        }

        public void EqualAngle()
        {
            if (_selectedObjects.Count <= 1) return;
            
            float referenceAngle = _referenceObject.Angulo;
            foreach (var obj in _selectedObjects.Where(o => o != _referenceObject))
            {
                obj.Angulo = referenceAngle;
            }
        }

        public void JoinHorizontally()
        {
            if (_selectedObjects.Count <= 1) return;

            var sortedObjects = _selectedObjects
                .OrderBy(obj => GetObjectCenter(obj).X)
                .ToList();

            for (int i = 1; i < sortedObjects.Count; i++)
            {
                var previousObj = sortedObjects[i - 1];
                var currentObj = sortedObjects[i];
                var previousCenter = GetObjectCenter(previousObj);
                var currentCenter = GetObjectCenter(currentObj);
                var previousDims = GetEffectiveDimensions(previousObj);
                
                float offset = previousDims.Width / 2;
                float newX = (float)(previousCenter.X + offset);
                float deltaX = (float)(newX - (currentCenter.X - GetEffectiveDimensions(currentObj).Width / 2));
                currentObj.Left += deltaX;
            }
        }

        public void JoinVertically()
        {
            if (_selectedObjects.Count <= 1) return;

            var sortedObjects = _selectedObjects
                .OrderBy(obj => GetObjectCenter(obj).Y)
                .ToList();

            for (int i = 1; i < sortedObjects.Count; i++)
            {
                var previousObj = sortedObjects[i - 1];
                var currentObj = sortedObjects[i];
                var previousCenter = GetObjectCenter(previousObj);
                var currentCenter = GetObjectCenter(currentObj);
                var previousDims = GetEffectiveDimensions(previousObj);
                
                float offset = previousDims.Height / 2;
                float newY = (float)(previousCenter.Y + offset);
                float deltaY = (float)(newY - (currentCenter.Y - GetEffectiveDimensions(currentObj).Height / 2));
                currentObj.Top += deltaY;
            }
        }

        private Point GetObjectCenter(osBase obj)
        {
            double angleRad = obj.Angulo * Math.PI / 180.0;
            float centerX = obj.Left + (obj.Ancho / 2);
            float centerY = obj.Top + (obj.Alto / 2);

            if (obj.Angulo != 0)
            {
                var rotatedX = obj.Left + (Math.Cos(angleRad) * obj.Ancho / 2 - Math.Sin(angleRad) * obj.Alto / 2);
                var rotatedY = obj.Top + (Math.Sin(angleRad) * obj.Ancho / 2 + Math.Cos(angleRad) * obj.Alto / 2);
                
                return new Point(rotatedX, rotatedY);
            }

            return new Point(centerX, centerY);
        }

        private bool IsObjectVertical(osBase obj)
        {
            double normalizedAngle = obj.Angulo % 180;
            if (normalizedAngle < 0) normalizedAngle += 180;
            return normalizedAngle >= 45 && normalizedAngle < 135;
        }

        private (float Width, float Height) GetEffectiveDimensions(osBase obj)
        {
            if (IsObjectVertical(obj))
            {
                return (obj.Alto, obj.Ancho);
            }
            return (obj.Ancho, obj.Alto);
        }

        private void SetEffectiveDimensions(osBase obj, float width, float height)
        {
            if (IsObjectVertical(obj))
            {
                obj.Alto = width;
                obj.Ancho = height;
            }
            else
            {
                obj.Ancho = width;
                obj.Alto = height;
            }
        }
    }
}