text: ﻿using System;
  using System.Windows;
  using System.Windows.Media;
  using System.Windows.Shapes;
  using HandyControl.Data;
  using HandyControl.Expression.Drawing;
  using HandyControl.Expression.Media;
  
  namespace HandyControl.Expression.Shapes
  {
      public abstract class PrimitiveShape : Shape, IGeometrySourceParameters, IShape
      {
          private IGeometrySource _geometrySource;
  
          static PrimitiveShape()
          {
              StretchProperty.OverrideMetadata(typeof(PrimitiveShape),
                  new DrawingPropertyMetadata(Stretch.Fill, DrawingPropertyMetadataOptions.AffectsRender));
              StrokeThicknessProperty.OverrideMetadata(typeof(PrimitiveShape),
                  new DrawingPropertyMetadata(ValueBoxes.Double1Box, DrawingPropertyMetadataOptions.AffectsRender));
          }
  
          protected sealed override Geometry DefiningGeometry =>
              GeometrySource.Geometry ?? Geometry.Empty;
  
          private IGeometrySource GeometrySource => _geometrySource ?? (_geometrySource = CreateGeometrySource());
  
          Stretch IGeometrySourceParameters.Stretch => Stretch;
  
          Brush IGeometrySourceParameters.Stroke => Stroke;
  
          double IGeometrySourceParameters.StrokeThickness => StrokeThickness;
  
          public event EventHandler RenderedGeometryChanged;
  
          public void InvalidateGeometry(InvalidateGeometryReasons reasons)
          {
              if (GeometrySource.InvalidateGeometry(reasons)) InvalidateArrange();
          }
  
          public Thickness GeometryMargin => GeometrySource.LogicalBounds.Subtract(RenderedGeometry.Bounds);
  
          Brush IShape.Fill
          {
              get => Fill;
              set => Fill = value;
          }
  
          Stretch IShape.Stretch
          {
              get => Stretch;
              set => Stretch = value;
          }
  
          Brush IShape.Stroke
          {
              get => Stroke;
              set => Stroke = value;
          }
  
          double IShape.StrokeThickness
          {
              get => StrokeThickness;
              set => StrokeThickness = value;
          }
  
          protected override Size ArrangeOverride(Size finalSize)
          {
              if (GeometrySource.UpdateGeometry(this, finalSize.Bounds())) RealizeGeometry();
              base.ArrangeOverride(finalSize);
              return finalSize;
          }
  
          protected abstract IGeometrySource CreateGeometrySource();
  
          protected override Size MeasureOverride(Size availableSize) => new Size(base.StrokeThickness, base.StrokeThickness);
  
          private void RealizeGeometry() => RenderedGeometryChanged?.Invoke(this, EventArgs.Empty);
      }
  }