using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using C1.Silverlight.Chart;

namespace C1.Silverlight.Chart.Extended
{
  /// <summary>
  /// RangeSlider for chart.
  /// </summary>
  [TemplatePart(Name = ChartRangeSlider.BackgroundImageGridElementName, Type = typeof(Image))]
  public class ChartRangeSlider : C1RangeSlider
  {
    internal const string BackgroundImageGridElementName = "BackgroundImageGrid";
    Grid backgroundImageGrid;
    internal event SizeChangedEventHandler BackgroundImageSizeChanged;

    /// <summary>
    /// Invoked when applying template.
    /// </summary>
    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      backgroundImageGrid = GetTemplateChild(BackgroundImageGridElementName) as Grid;

      if (backgroundImageGrid != null)
      {
        backgroundImageGrid.SizeChanged += new SizeChangedEventHandler(backgroundImage_SizeChanged);
      }
    }

    /// <summary>
    /// Gets or sets the source of background image.
    /// </summary>
    public ImageSource BackgroundImageSource
    {
      get { return (ImageSource)GetValue(BackgroundImageSourceProperty); }
      set { SetValue(BackgroundImageSourceProperty, value); }
    }

    /// <summary>
    /// Using a DependencyProperty as the backing store for BackgroundImageSource.
    /// </summary>
    public static readonly DependencyProperty BackgroundImageSourceProperty =
        DependencyProperty.Register("BackgroundImageSource", typeof(ImageSource), typeof(ChartRangeSlider),
        new PropertyMetadata(null));


    void backgroundImage_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      if (BackgroundImageSizeChanged != null)
        BackgroundImageSizeChanged(this, e);
    }
  }

  /// <summary>
  /// Axis range slider user control that includes chart range slider and implements
  /// IAxisScrollBar interface.
  /// </summary>
  [TemplatePart(Name = AxisRangeSlider.RangeSilderElementName, Type = typeof(ChartRangeSlider))]
  public class AxisRangeSlider : ContentControl, IAxisScrollBar
  {
    #region fields
    
    internal const string RangeSilderElementName = "RangeSlider";
    ChartRangeSlider _slider;
    double _lower = 0, _upper = 1;
    double _value = 0, _scale = 1;
    private Axis _axis;
    bool notify = true;

    event SizeChangedEventHandler BackgroundImageSizeChanged;

    #endregion

    /// <summary>
    /// Creates an instance of AxisRangeSlider
    /// </summary>
    public AxisRangeSlider()
    {
      DefaultStyleKey = typeof(AxisRangeSlider);
    }

    public C1Chart BackgroundChart
    {
      get { return (C1Chart)GetValue(BackgroundChartProperty); }
      set { SetValue(BackgroundChartProperty, value); }
    }

    // Using a DependencyProperty as the backing store for BackgroundChart.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty BackgroundChartProperty =
        DependencyProperty.Register("BackgroundChart", typeof(C1Chart), typeof(AxisRangeSlider), new PropertyMetadata(null));

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      _slider = GetTemplateChild(RangeSilderElementName) as ChartRangeSlider;
      if (_slider != null)
      {
        UpdateValue(); UpdateScale();
        _slider.LowerValueChanged += (s, e) => OnAxisRangeChanged();
        _slider.UpperValueChanged += (s, e) => OnAxisRangeChanged();
        _slider.BackgroundImageSizeChanged += new SizeChangedEventHandler(_slider_BackgroundImageSizeChanged);
        _slider.BackgroundImageSource = BackgroundImageSource;
      }
    }

    public double UpperValue
    {
      get { return _upper; }
      set
      {
        _upper = value;
        if (_slider != null)
          _slider.UpperValue = _upper;
      }
    }

    public double LowerValue
    {
      get { return _lower; }
      set
      {
        _lower = value;
        if (_slider != null)
          _slider.LowerValue = _lower;
      }
    }

    #region IAxisScrollBar Members

    /// <summary>
    /// Gets or sets the axis associated with scrollbar.
    /// </summary>
    public Axis Axis
    {
      get { return _axis; }
      set
      {
        if (_axis != value)
        {
          if (_axis != null)
          {
            ((INotifyPropertyChanged)_axis).PropertyChanged -= (Axis_PropertyChanged);
          }
          _axis = value;
          if (_axis != null)
          {
            ((INotifyPropertyChanged)_axis).PropertyChanged += (Axis_PropertyChanged);
            notify = false;
            AxisValue = _axis.Value;
            AxisScale = _axis.Scale;
            notify = true;
          }
        }
      }
    }

    /// <summary>
    /// Gets the scrollbar margin.
    /// </summary>
    public virtual Thickness ScrollBarMargin
    {
      get
      {
        if (_slider != null)
        {
          Thumb thumb = _slider.FindVisualChildByName<Thumb>("LowerThumb");
          if (thumb != null)
          {
            double w = thumb.ActualWidth;
            if (w == 0)
              w = thumb.DesiredSize.Width;
            return new Thickness(-(0.5 * w + thumb.Margin.Left), 0
              , -(0.5 * w + thumb.Margin.Right), 0);
          }
        }
        return new Thickness();
      }
    }

    /// <summary>
    /// Gets the scrollbar position.
    /// </summary>
    public AxisScrollBarPosition ScrollBarPosition
    {
      get;
      set;
    }

    /// <summary>
    /// Fires when axis range was changed
    /// </summary>
    public event AxisRangeChangedEventHandler AxisRangeChanged;

    #endregion

    #region implementation

    /// <summary>
    /// Gets or sets the source of background image for slider.
    /// </summary>
    ImageSource BackgroundImageSource
    {
      get { return (ImageSource)GetValue(BackgroundImageSourceProperty); }
      set { SetValue(BackgroundImageSourceProperty, value); }
    }

    /// <summary>
    /// Using a DependencyProperty as the backing store for BackgroundImageSource.
    /// </summary>
    static readonly DependencyProperty BackgroundImageSourceProperty =
        DependencyProperty.Register("BackgroundImageSource", typeof(ImageSource), typeof(AxisRangeSlider),
        new PropertyMetadata(null, OnBackgroundImageSourceChanged));

    double AxisValue
    {
      get
      {
        if (_slider != null)
        {
          double delta = _slider.UpperValue - _slider.LowerValue;
          double max = 1 - delta;

          if (Axis != null && Axis.AxisType == AxisType.Y)
            return 1 - (_slider.LowerValue) / max;
          else
            return (_slider.LowerValue) / max;
        }
        else
          return _value;
      }
      set
      {
        _value = value;
        if (_slider != null)
          UpdateValue();
      }
    }

    double AxisScale
    {
      get
      {
        if (_slider != null)
          return (_slider.UpperValue - _slider.LowerValue);
        else
          return _scale;
      }
      set
      {
        _scale = value;
        if (_slider != null)
          UpdateScale();
      }
    }

    void OnAxisRangeChanged()
    {
      if (_slider != null)
      {
        _lower = _slider.LowerValue;
        _upper = _slider.UpperValue;
      }
      if (notify && AxisRangeChanged != null)
        AxisRangeChanged(this, new AxisRangeChangedEventArgs(AxisValue, AxisScale));
    }

    void Axis_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      notify = false;
      try
      {
        if (e.PropertyName == "Value")
          AxisValue = Axis.Value;
        else if (e.PropertyName == "Scale")
          AxisScale = Axis.Scale;
      }
      finally
      {
        notify = true;
      }
    }

    static void OnBackgroundImageSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
      var slider = obj as AxisRangeSlider;
      if (slider._slider != null)
        slider._slider.BackgroundImageSource = slider.BackgroundImageSource;
    }

    void _slider_BackgroundImageSizeChanged(object sender, SizeChangedEventArgs e)
    {
      if (BackgroundImageSizeChanged != null)
        BackgroundImageSizeChanged(this, e);

      if (BackgroundChart != null)
        BackgroundImageSource = RenderChart.RenderChartToBitmap(BackgroundChart, e.NewSize, Axis.AxisType == AxisType.Y);
      else
        BackgroundImageSource = null;
    }

    void UpdateValue()
    {
      double distance = _slider.UpperValue - _slider.LowerValue;
      _slider.LowerValue = _value;
      _slider.UpperValue = _slider.LowerValue + distance;
    }

    void UpdateScale()
    {
      double pos = 0.5 * (_slider.UpperValue - _slider.LowerValue);
      _slider.LowerValue = Math.Max(0, pos - 0.5 * _scale);
      double up = _slider.LowerValue + _scale;
      if (up > 1)
      {
        _slider.UpperValue = 1;
        _slider.LowerValue = _slider.UpperValue - 1 * _scale;
      }
      else
        _slider.UpperValue = up;
    }

    #endregion
  }

}
