context: /*
      Copyright (C) 2014-2019 de4dot@gmail.com
  
      This file is part of dnSpy
  
      dnSpy is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      dnSpy is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with dnSpy.  If not, see <http://www.gnu.org/licenses/>.
  */
  
  using System;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.Diagnostics;
  using System.Windows;
  using System.Windows.Media;
  using System.Windows.Media.TextFormatting;
  using dnSpy.Contracts.Text.Editor;
  using Microsoft.VisualStudio.Text;
  using Microsoft.VisualStudio.Text.Formatting;
  using Microsoft.VisualStudio.Text.Projection;
  using TF = Microsoft.VisualStudio.Text.Formatting;
  
  namespace dnSpy.Text.Formatting {
  	sealed class WpfTextViewLine : IFormattedLine, IDsTextViewLine {
  		IBufferGraph bufferGraph;
  		readonly int endColumn, startColumn;
  		readonly int linePartsIndex, linePartsLength;
  		double top;
  		readonly double width;
  		readonly double textLeft;
  		readonly double textWidth;
  		readonly double virtualSpaceWidth;
  		double deltaY;
  		readonly double endOfLineWidth;
  		TextViewLineChange change;
  		Rect visibleArea;
  		SnapshotSpan extentIncludingLineBreak;
  		ITextSnapshot visualSnapshot;
  		readonly int lineBreakLength;
  		VisibilityState visibilityState;
  		readonly bool isFirstTextViewLineForSnapshotLine;
  		readonly bool isLastTextViewLineForSnapshotLine;
  		LineTransform lineTransform;
  		ReadOnlyCollection<TextLine> textLines;
  		LinePartsCollection linePartsCollection;
  		double realTopSpace, scaledTopSpace;
  		double realBottomSpace;
  		double realTextHeight, scaledTextHeight;
  		double scaledHeight;
  		double realBaseline;
  
  		public double Bottom {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return Top + Height;
  			}
  		}
  
  		public double Height {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return scaledHeight;
  			}
  		}
  
  		public double Left {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return TextLeft;
  			}
  		}
  
  		public double Right {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return Left + Width;
  			}
  		}
  
  		public double Top {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return top;
  			}
  		}
  
  		public double Width {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return width;
  			}
  		}
  
  		public double TextBottom {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return Top + scaledTopSpace + TextHeight;
  			}
  		}
  
  		public double TextTop {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return Top + scaledTopSpace;
  			}
  		}
  
  		public double TextHeight {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return scaledTextHeight;
  			}
  		}
  
  		public double TextWidth {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return textWidth;
  			}
  		}
  
  		public double TextLeft {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return textLeft;
  			}
  		}
  
  		public double TextRight {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return TextLeft + TextWidth;
  			}
  		}
  
  		public double VirtualSpaceWidth {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return virtualSpaceWidth;
  			}
  		}
  
  		public double Baseline {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return realBaseline * lineTransform.VerticalScale;
  			}
  		}
  
  		public double DeltaY {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return deltaY;
  			}
  		}
  
  		public double EndOfLineWidth {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return endOfLineWidth;
  			}
  		}
  
  		public TextViewLineChange Change {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return change;
  			}
  		}
  
  		public object IdentityTag => this;
  		public bool IsFirstTextViewLineForSnapshotLine => isFirstTextViewLineForSnapshotLine;
  		public bool IsLastTextViewLineForSnapshotLine => isLastTextViewLineForSnapshotLine;
  		public bool IsValid { get; private set; }
  
  		public int Length {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return ExtentIncludingLineBreak.Length - LineBreakLength;
  			}
  		}
  
  		public int LengthIncludingLineBreak {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return ExtentIncludingLineBreak.Length;
  			}
  		}
  
  		public int LineBreakLength {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return lineBreakLength;
  			}
  		}
  
  		public LineTransform LineTransform {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return lineTransform;
  			}
  		}
  
  		public ITextSnapshot Snapshot => extentIncludingLineBreak.Snapshot;
  
  		public SnapshotPoint Start {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return extentIncludingLineBreak.Start;
  			}
  		}
  
  		public SnapshotPoint End {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return extentIncludingLineBreak.End - lineBreakLength;
  			}
  		}
  
  		public SnapshotPoint EndIncludingLineBreak {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return extentIncludingLineBreak.End;
  			}
  		}
  
  		public SnapshotSpan Extent {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return new SnapshotSpan(extentIncludingLineBreak.Snapshot, extentIncludingLineBreak.Start.Position, extentIncludingLineBreak.Length - lineBreakLength);
  			}
  		}
  
  		public IMappingSpan ExtentAsMappingSpan {
  			get {
  				if (Snapshot != visualSnapshot)
  					throw new NotSupportedException();
  				return bufferGraph.CreateMappingSpan(Extent, SpanTrackingMode.EdgeInclusive);
  			}
  		}
  
  		public SnapshotSpan ExtentIncludingLineBreak {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return extentIncludingLineBreak;
  			}
  		}
  
  		public IMappingSpan ExtentIncludingLineBreakAsMappingSpan {
  			get {
  				if (Snapshot != visualSnapshot)
  					throw new NotSupportedException();
  				return bufferGraph.CreateMappingSpan(ExtentIncludingLineBreak, SpanTrackingMode.EdgeInclusive);
  			}
  		}
  
  		public LineTransform DefaultLineTransform {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return new LineTransform(DEFAULT_TOP_SPACE, DEFAULT_BOTTOM_SPACE, DEFAULT_VERTICAL_SCALE, Right);
  			}
  		}
  
  		public ReadOnlyCollection<TextLine> TextLines {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return textLines;
  			}
  		}
  
  		public VisibilityState VisibilityState {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return visibilityState;
  			}
  		}
  
  		public Rect VisibleArea {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				return visibleArea;
  			}
  		}
  
  		public bool HasAdornments {
  			get {
  				if (!IsValid)
  					throw new ObjectDisposedException(nameof(WpfTextViewLine));
  				foreach (var part in linePartsCollection.LineParts) {
  					if (!(part.AdornmentElement is null))
  						return true;
  				}
  				return false;
  			}
  		}
  
  		bool IsLastVisualLine { get; }
  
  		TextLine TextLine {
  			get {
  				Debug.Assert(textLines.Count == 1);
  				return textLines[0];
  			}
  		}
  
  		public WpfTextViewLine(IBufferGraph bufferGraph, LinePartsCollection linePartsCollection, int linePartsIndex, int linePartsLength, int startColumn, int endColumn, ITextSnapshotLine bufferLine, SnapshotSpan span, ITextSnapshot visualSnapshot, TextLine textLine, double indentation, double virtualSpaceWidth) {
  			if (linePartsCollection is null)
  				throw new ArgumentNullException(nameof(linePartsCollection));
  			if (linePartsIndex < 0)
  				throw new ArgumentOutOfRangeException(nameof(linePartsIndex));
  			if (linePartsLength < 0)
  				throw new ArgumentOutOfRangeException(nameof(linePartsLength));
  			if (linePartsIndex + linePartsLength > linePartsCollection.LineParts.Count)
  				throw new ArgumentOutOfRangeException(nameof(linePartsLength));
  			if (bufferLine is null)
  				throw new ArgumentNullException(nameof(bufferLine));
  			if (span.Snapshot != bufferLine.Snapshot)
  				throw new ArgumentException();
  			if (textLine is null)
  				throw new ArgumentNullException(nameof(textLine));
  
  			IsValid = true;
  			this.linePartsIndex = linePartsIndex;
  			this.linePartsLength = linePartsLength;
  			this.bufferGraph = bufferGraph ?? throw new ArgumentNullException(nameof(bufferGraph));
  			this.linePartsCollection = linePartsCollection;
  			this.startColumn = startColumn;
  			this.endColumn = endColumn;
  			this.visualSnapshot = visualSnapshot ?? throw new ArgumentNullException(nameof(visualSnapshot));
  			textLines = new ReadOnlyCollection<TextLine>(new[] { textLine });
  			Debug.Assert(textLines.Count == 1);// Assumed by all code accessing TextLine prop
  
  			realTopSpace = 0;
  			realBottomSpace = 0;
  			realBaseline = TextLine.Baseline;
  			double baseLineHeight = TextLine.TextHeight - TextLine.Baseline;
  			var lineParts = linePartsCollection.LineParts;
  
gt: 			for (int i = 0; i < linePartsLength; i++) {