using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Diagnostics;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;

namespace Microsoft.Research.DynamicDataDisplay.Charts
{
    public class StackCanvas : Panel
    {
        public StackCanvas()
        {
            //ClipToBounds = true;
        }

        #region EndCoordinate attached property

        [AttachedPropertyBrowsableForChildren]
        public static double GetEndCoordinate(DependencyObject obj)
        {
            return (double)obj.GetValue(EndCoordinateProperty);
        }

        public static void SetEndCoordinate(DependencyObject obj, double value)
        {
            obj.SetValue(EndCoordinateProperty, value);
        }

        public static readonly DependencyProperty EndCoordinateProperty = DependencyProperty.RegisterAttached(
            "EndCoordinate",
            typeof(double),
            typeof(StackCanvas),
            new PropertyMetadata(Double.NaN, OnCoordinateChanged));

        #endregion

        #region Coordinate attached property

        [AttachedPropertyBrowsableForChildren]
        public static double GetCoordinate(DependencyObject obj)
        {
            return (double)obj.GetValue(CoordinateProperty);
        }

        public static void SetCoordinate(DependencyObject obj, double value)
        {
            obj.SetValue(CoordinateProperty, value);
        }

        public static readonly DependencyProperty CoordinateProperty = DependencyProperty.RegisterAttached(
            "Coordinate",
            typeof(double),
            typeof(StackCanvas),
            new PropertyMetadata(0.0, OnCoordinateChanged));

        private static void OnCoordinateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UIElement reference = d as UIElement;
            if (reference != null)
            {
                StackCanvas parent = VisualTreeHelper.GetParent(reference) as StackCanvas;
                if (parent != null)
                {
                    parent.InvalidateArrange();
                }
            }
        }
        #endregion

        #region AxisPlacement property

        public AxisPlacement Placement
        {
            get { return (AxisPlacement)GetValue(PlacementProperty); }
            set { SetValue(PlacementProperty, value); }
        }

        public static readonly DependencyProperty PlacementProperty =
            DependencyProperty.Register(
              "Placement",
              typeof(AxisPlacement),
              typeof(StackCanvas),
              new FrameworkPropertyMetadata(
                  AxisPlacement.Bottom,
                  FrameworkPropertyMetadataOptions.AffectsArrange));

        #endregion

        private bool IsHorizontal
        {
            get { return Placement == AxisPlacement.Top || Placement == AxisPlacement.Bottom; }
        }

        protected override Size MeasureOverride(Size constraint)
        {
            Size availableSize = constraint;
            Size size = new Size();

            bool isHorizontal = IsHorizontal;

            if (isHorizontal)
            {
                availableSize.Width = Double.PositiveInfinity;
                size.Width = constraint.Width;
            }
            else
            {
                availableSize.Height = Double.PositiveInfinity;
                size.Height = constraint.Height;
            }

            // measuring all children and determinimg self width and height
            foreach (UIElement element in base.Children)
            {
                if (element != null)
                {
                    Size childSize = GetChildSize(element, availableSize);
                    element.Measure(childSize);
                    Size desiredSize = element.DesiredSize;

                    if (isHorizontal)
                    {
                        size.Height = Math.Max(size.Height, desiredSize.Height);
                    }
                    else
                    {
                        size.Width = Math.Max(size.Width, desiredSize.Width);
                    }
                }
            }

            if (Double.IsPositiveInfinity(size.Width)) size.Width = 0;
            if (Double.IsPositiveInfinity(size.Height)) size.Height = 0;

            return size;
        }

        private Size GetChildSize(UIElement element, Size availableSize)
        {
            var coordinate = GetCoordinate(element);
            var endCoordinate = GetEndCoordinate(element);

            if (coordinate.IsNotNaN() && endCoordinate.IsNotNaN())
            {
                if (Placement.IsBottomOrTop())
                {
                    availableSize.Width = endCoordinate - coordinate;
                }
                else
                {
                    availableSize.Height = endCoordinate - coordinate;
                }
            }

            return availableSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            bool isHorizontal = IsHorizontal;

            foreach (FrameworkElement element in base.Children)
            {
                if (element == null)
                {
                    continue;
                }

                Size elementSize = element.DesiredSize;
                double x = 0.0;
                double y = 0.0;

                switch (Placement)
                {
                    case AxisPlacement.Left:
                        x = finalSize.Width - elementSize.Width;
                        break;
                    case AxisPlacement.Right:
                        x = 0;
                        break;
                    case AxisPlacement.Top:
                        y = finalSize.Height - elementSize.Height;
                        break;
                    case AxisPlacement.Bottom:
                        y = 0;
                        break;
                    default:
                        break;
                }

                double coordinate = GetCoordinate(element);

                if (!Double.IsNaN(GetEndCoordinate(element)))
                {
                    double endCoordinate = GetEndCoordinate(element);
                    double size = endCoordinate - coordinate;
                    if (size < 0)
                    {
                        size = -size;
                        coordinate -= size;
                    }
                    if (isHorizontal)
                        elementSize.Width = size;
                    else
                        elementSize.Height = size;
                }


                // shift for common tick labels, not for major ones.
                if (isHorizontal)
                {
                    x = coordinate;
                    if (element.HorizontalAlignment == HorizontalAlignment.Center)
                        x = coordinate - elementSize.Width / 2;
                }
                else
                {
                    if (element.VerticalAlignment == VerticalAlignment.Center)
                        y = coordinate - elementSize.Height / 2;
                    else if (element.VerticalAlignment == VerticalAlignment.Bottom)
                        y = coordinate - elementSize.Height;
                    else if (element.VerticalAlignment == VerticalAlignment.Top)
                        y = coordinate;
                }

                Rect bounds = new Rect(new Point(x, y), elementSize);
                element.Arrange(bounds);
            }

            return finalSize;
        }
    }
}
