/*************************************************************************************

   Extended WPF Toolkit

   Copyright (C) 2007-2013 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license 

   For more features, controls, and fast professional support,
   pick up the Plus Edition at http://xceed.com/wpf_toolkit

   Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids

  ***********************************************************************************/

using System;
using System.Windows;
using System.Windows.Media;
using Xceed.Wpf.Toolkit.Core.Utilities;

namespace Xceed.Wpf.Toolkit.Zoombox
{
  public class ZoomboxViewFinderDisplay : FrameworkElement
  {
    #region Constructors

    static ZoomboxViewFinderDisplay()
    {
      ZoomboxViewFinderDisplay.DefaultStyleKeyProperty.OverrideMetadata( typeof( ZoomboxViewFinderDisplay ), new FrameworkPropertyMetadata( typeof( ZoomboxViewFinderDisplay ) ) );
    }

    public ZoomboxViewFinderDisplay()
    {
    }

    #endregion

    #region Background Property

    public static readonly DependencyProperty BackgroundProperty =
      DependencyProperty.Register( "Background", typeof( Brush ), typeof( ZoomboxViewFinderDisplay ),
        new FrameworkPropertyMetadata( new SolidColorBrush( Color.FromArgb( 0xC0, 0xFF, 0xFF, 0xFF ) ), FrameworkPropertyMetadataOptions.AffectsRender ) );

    public Brush Background
    {
      get
      {
        return ( Brush )this.GetValue( ZoomboxViewFinderDisplay.BackgroundProperty );
      }
      set
      {
        this.SetValue( ZoomboxViewFinderDisplay.BackgroundProperty, value );
      }
    }

    #endregion

    #region ContentBounds Property

    private static readonly DependencyPropertyKey ContentBoundsPropertyKey =
      DependencyProperty.RegisterReadOnly( "ContentBounds", typeof( Rect ), typeof( ZoomboxViewFinderDisplay ),
        new FrameworkPropertyMetadata( ( Rect )Rect.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender ) );

    public static readonly DependencyProperty ContentBoundsProperty = ZoomboxViewFinderDisplay.ContentBoundsPropertyKey.DependencyProperty;

    internal Rect ContentBounds
    {
      get
      {
        return ( Rect )this.GetValue( ZoomboxViewFinderDisplay.ContentBoundsProperty );
      }
      set
      {
        this.SetValue( ZoomboxViewFinderDisplay.ContentBoundsPropertyKey, value );
      }
    }

    #endregion

    #region ShadowBrush Property

    public static readonly DependencyProperty ShadowBrushProperty =
      DependencyProperty.Register( "ShadowBrush", typeof( Brush ), typeof( ZoomboxViewFinderDisplay ),
        new FrameworkPropertyMetadata( new SolidColorBrush( Color.FromArgb( 0x80, 0xFF, 0xFF, 0xFF ) ), FrameworkPropertyMetadataOptions.AffectsRender ) );

    public Brush ShadowBrush
    {
      get
      {
        return ( Brush )this.GetValue( ZoomboxViewFinderDisplay.ShadowBrushProperty );
      }
      set
      {
        this.SetValue( ZoomboxViewFinderDisplay.ShadowBrushProperty, value );
      }
    }

    #endregion

    #region ViewportBrush Property

    public static readonly DependencyProperty ViewportBrushProperty =
      DependencyProperty.Register( "ViewportBrush", typeof( Brush ), typeof( ZoomboxViewFinderDisplay ),
        new FrameworkPropertyMetadata( Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender ) );

    public Brush ViewportBrush
    {
      get
      {
        return ( Brush )this.GetValue( ZoomboxViewFinderDisplay.ViewportBrushProperty );
      }
      set
      {
        this.SetValue( ZoomboxViewFinderDisplay.ViewportBrushProperty, value );
      }
    }

    #endregion

    #region ViewportPen Property

    public static readonly DependencyProperty ViewportPenProperty =
      DependencyProperty.Register( "ViewportPen", typeof( Pen ), typeof( ZoomboxViewFinderDisplay ),
        new FrameworkPropertyMetadata( new Pen( new SolidColorBrush( Color.FromArgb( 0x80, 0x00, 0x00, 0x00 ) ), 1d ), FrameworkPropertyMetadataOptions.AffectsRender ) );

    public Pen ViewportPen
    {
      get
      {
        return ( Pen )this.GetValue( ZoomboxViewFinderDisplay.ViewportPenProperty );
      }
      set
      {
        this.SetValue( ZoomboxViewFinderDisplay.ViewportPenProperty, value );
      }
    }

    #endregion

    #region ViewportRect Property

    public static readonly DependencyProperty ViewportRectProperty =
      DependencyProperty.Register( "ViewportRect", typeof( Rect ), typeof( ZoomboxViewFinderDisplay ),
        new FrameworkPropertyMetadata( Rect.Empty, FrameworkPropertyMetadataOptions.AffectsRender ) );

    public Rect ViewportRect
    {
      get
      {
        return ( Rect )this.GetValue( ZoomboxViewFinderDisplay.ViewportRectProperty );
      }
      set
      {
        this.SetValue( ZoomboxViewFinderDisplay.ViewportRectProperty, value );
      }
    }

    #endregion

    #region VisualBrush Property

    private static readonly DependencyPropertyKey VisualBrushPropertyKey =
      DependencyProperty.RegisterReadOnly( "VisualBrush", typeof( VisualBrush ), typeof( ZoomboxViewFinderDisplay ),
        new FrameworkPropertyMetadata( ( VisualBrush )null ) );

    public static readonly DependencyProperty VisualBrushProperty = ZoomboxViewFinderDisplay.VisualBrushPropertyKey.DependencyProperty;

    internal VisualBrush VisualBrush
    {
      get
      {
        return ( VisualBrush )this.GetValue( ZoomboxViewFinderDisplay.VisualBrushProperty );
      }
      set
      {
        this.SetValue( ZoomboxViewFinderDisplay.VisualBrushPropertyKey, value );
      }
    }

    #endregion

    #region AvailableSize Internal Property

    internal Size AvailableSize
    {
      get
      {
        return _availableSize;
      }
    }

    private Size _availableSize = Size.Empty;

    #endregion

    #region Scale Internal Property

    internal double Scale
    {
      get
      {
        return _scale;
      }
      set
      {
        _scale = value;
      }
    }

    private double _scale = 1d;

    #endregion

    protected override Size ArrangeOverride( Size finalSize )
    {
      // Note that we do not call the Arrange method on any children
      // because a ViewFinderDisplay has no children

      // the control's RenderSize should always match its DesiredSize
      return this.DesiredSize;
    }

    protected override Size MeasureOverride( Size availableSize )
    {
      // Note that we do not call the Measure method on any children
      // because a ViewFinderDisplay has no children.  It is merely used 
      // as a surface for the view finder's VisualBrush.

      // store the available size for use by the Zoombox control
      _availableSize = availableSize;

      // Simulate size-to-content for the display panel by ensuring a width and height
      // based on the content bounds. Otherwise, the display panel may have no size, since it doesn't 
      // contain content.
      double width = DoubleHelper.IsNaN( this.ContentBounds.Width ) ? 0 : Math.Max( 0, this.ContentBounds.Width );
      double height = DoubleHelper.IsNaN( this.ContentBounds.Height ) ? 0 : Math.Max( 0, this.ContentBounds.Height );
      Size displayPanelSize = new Size( width, height );

      // Now ensure that the result fits within the available size while maintaining
      // the width/height ratio of the content bounds
      if( displayPanelSize.Width > availableSize.Width || displayPanelSize.Height > availableSize.Height )
      {
        double aspectX = availableSize.Width / displayPanelSize.Width;
        double aspectY = availableSize.Height / displayPanelSize.Height;
        double scale = ( aspectX < aspectY ) ? aspectX : aspectY;
        displayPanelSize = new Size( displayPanelSize.Width * scale, displayPanelSize.Height * scale );
      }

      return displayPanelSize;
    }

    protected override void OnRender( DrawingContext dc )
    {
      base.OnRender( dc );

      dc.DrawRectangle( this.Background, null, this.ContentBounds );

      dc.DrawRectangle( this.VisualBrush, null, this.ContentBounds );

      if( this.ViewportRect.IntersectsWith( new Rect( this.RenderSize ) ) )
      {
        // draw shadow rectangles over the non-viewport regions
        Rect r1 = new Rect( new Point( 0, 0 ), new Size( this.RenderSize.Width, Math.Max( 0, this.ViewportRect.Top ) ) );
        Rect r2 = new Rect( new Point( 0, this.ViewportRect.Top ), new Size( Math.Max( 0, this.ViewportRect.Left ), this.ViewportRect.Height ) );
        Rect r3 = new Rect( new Point( this.ViewportRect.Right, this.ViewportRect.Top ), new Size( Math.Max( 0, this.RenderSize.Width - this.ViewportRect.Right ), this.ViewportRect.Height ) );
        Rect r4 = new Rect( new Point( 0, this.ViewportRect.Bottom ), new Size( this.RenderSize.Width, Math.Max( 0, this.RenderSize.Height - this.ViewportRect.Bottom ) ) );
        dc.DrawRectangle( this.ShadowBrush, null, r1 );
        dc.DrawRectangle( this.ShadowBrush, null, r2 );
        dc.DrawRectangle( this.ShadowBrush, null, r3 );
        dc.DrawRectangle( this.ShadowBrush, null, r4 );

        // draw the rectangle around the viewport region
        dc.DrawRectangle( this.ViewportBrush, this.ViewportPen, this.ViewportRect );
      }
      else
      {
        // if no part of the Rect is visible, just draw a 
        // shadow over the entire content bounds
        dc.DrawRectangle( this.ShadowBrush, null, new Rect( this.RenderSize ) );
      }
    }
  }
}
