text: ﻿using System;
  using System.Collections.Generic;
  using System.Diagnostics.CodeAnalysis;
  using System.Linq;
  using System.Windows;
  using System.Windows.Media;
  using HandyControl.Expression.Drawing;
  
  namespace HandyControl.Expression.Media
  {
      public sealed class SketchGeometryEffect : GeometryEffect
      {
          private readonly long _randomSeed = DateTime.Now.Ticks;
  
          protected override GeometryEffect DeepCopy()
          {
              return new SketchGeometryEffect();
          }
  
          private static void DisturbPoints(RandomEngine random, double scale, IList<Point> points,
              IList<Vector> normals)
          {
              var count = points.Count;
              for (var i = 1; i < count; i++)
              {
                  var num3 = random.NextGaussian(0.0, 1.0 * scale);
                  var num4 = random.NextUniform(-0.5, 0.5) * scale;
                  var point = points[i];
                  var vector = normals[i];
                  var vector2 = normals[i];
                  var point2 = points[i];
                  var vector3 = normals[i];
                  var vector4 = normals[i];
                  points[i] = new Point(point.X + vector.X * num4 - vector2.Y * num3,
                      point2.Y + vector3.X * num3 + vector4.Y * num4);
              }
          }
  
          public override bool Equals(GeometryEffect geometryEffect)
          {
              return geometryEffect is SketchGeometryEffect;
          }
  
          private IEnumerable<SimpleSegment> GetEffectiveSegments(PathFigure pathFigure)
          {
              var startPoint = pathFigure.StartPoint;
              foreach (var iteratorVariable1 in pathFigure.AllSegments())
              foreach (var iteratorVariable2 in iteratorVariable1.PathSegment.GetSimpleSegments(
                  iteratorVariable1.StartPoint))
              {
                  yield return iteratorVariable2;
                  startPoint = iteratorVariable2.Points.Last();
              }
  
              if (pathFigure.IsClosed) yield return SimpleSegment.Create(startPoint, pathFigure.StartPoint);
          }
  
          [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")]
          protected override bool UpdateCachedGeometry(Geometry input)
          {
              var flag = false;
              var inputPath = input.AsPathGeometry();
              if (inputPath != null) return flag | UpdateSketchGeometry(inputPath);
              CachedGeometry = input;
              return flag;
          }
  
          private bool UpdateSketchGeometry(PathGeometry inputPath)
          {
              var flag = false;
              flag |= GeometryHelper.EnsureGeometryType(out var geometry, ref CachedGeometry,
                  () => new PathGeometry());
              flag |= geometry.Figures.EnsureListCount(inputPath.Figures.Count, () => new PathFigure());
              var random = new RandomEngine(_randomSeed);
              for (var i = 0; i < inputPath.Figures.Count; i++)
              {
                  var pathFigure = inputPath.Figures[i];
                  var isClosed = pathFigure.IsClosed;
                  var isFilled = pathFigure.IsFilled;
                  if (pathFigure.Segments.Count == 0)
                  {
                      flag |= geometry.Figures[i]
                          .SetIfDifferent(PathFigure.StartPointProperty, pathFigure.StartPoint);
                      flag |= geometry.Figures[i].Segments.EnsureListCount(0);
                  }
                  else
                  {
                      var list = new List<Point>(pathFigure.Segments.Count * 3);
                      foreach (var segment in GetEffectiveSegments(pathFigure))
                      {
                          var resultPolyline = new List<Point>
                          {
                              segment.Points[0]
                          };
                          segment.Flatten(resultPolyline, 0.0, null);
                          var polyline = new PolylineData(resultPolyline);
                          if (resultPolyline.Count > 1 && polyline.TotalLength > 4.0)
                          {
                              var a = polyline.TotalLength / 8.0;
                              var sampleCount = (int) Math.Max(2.0, Math.Ceiling(a));
                              var interval = polyline.TotalLength / sampleCount;
                              var scale = interval / 8.0;
                              var samplePoints = new List<Point>(sampleCount);
                              var sampleNormals = new List<Vector>(sampleCount);
                              var sampleIndex = 0;
                              PolylineHelper.PathMarch(polyline, 0.0, 0.0, delegate(MarchLocation location)
                              {
                                  if (location.Reason == MarchStopReason.CompletePolyline) return double.NaN;
                                  if (location.Reason != MarchStopReason.CompleteStep) return location.Remain;
                                  if (sampleIndex++ == sampleCount) return double.NaN;
                                  samplePoints.Add(location.GetPoint(polyline.Points));
                                  sampleNormals.Add(location.GetNormal(polyline));
                                  return interval;
                              });
                              DisturbPoints(random, scale, samplePoints, sampleNormals);
                              list.AddRange(samplePoints);
                          }
                          else
                          {
                              list.AddRange(resultPolyline);
                              list.RemoveLast();
                          }
                      }
  
                      if (!isClosed) list.Add(pathFigure.Segments.Last().GetLastPoint());
                      flag |= PathFigureHelper.SyncPolylineFigure(geometry.Figures[i], list, isClosed,
                          isFilled);
                  }
              }
  
              if (flag) CachedGeometry = PathGeometryHelper.FixPathGeometryBoundary(CachedGeometry);
              return flag;
          }
      }
  }