// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Globalization;
using Microsoft.Extensions.Internal;

namespace Microsoft.AspNetCore.Razor.Language
{
	public struct SourceSpan : IEquatable<SourceSpan>
	{
		public static readonly SourceSpan Undefined = new SourceSpan( SourceLocation.Undefined, 0 );

		public SourceSpan( int absoluteIndex, int length )
			: this( null, absoluteIndex, -1, -1, length )
		{
		}

		public SourceSpan( SourceLocation location, int contentLength )
			: this( location.FilePath, location.AbsoluteIndex, location.LineIndex, location.CharacterIndex, contentLength, lineCount: 1, endCharacterIndex: 0 )
		{
		}

		public SourceSpan( string filePath, int absoluteIndex, int lineIndex, int characterIndex, int length )
			: this( filePath: filePath, absoluteIndex: absoluteIndex, lineIndex: lineIndex, characterIndex: characterIndex, length: length, lineCount: 0, endCharacterIndex: 0 )
		{
		}

		public SourceSpan( string filePath, int absoluteIndex, int lineIndex, int characterIndex, int length, int lineCount, int endCharacterIndex )
		{
			AbsoluteIndex = absoluteIndex;
			LineIndex = lineIndex;
			CharacterIndex = characterIndex;
			Length = length;
			FilePath = filePath;
			LineCount = lineCount;
			EndCharacterIndex = endCharacterIndex;
		}

		public SourceSpan( int absoluteIndex, int lineIndex, int characterIndex, int length )
			: this( filePath: null, absoluteIndex: absoluteIndex, lineIndex: lineIndex, characterIndex: characterIndex, length: length )
		{
		}

		public int Length { get; }

		public int AbsoluteIndex { get; }

		public int LineIndex { get; }

		public int CharacterIndex { get; }

		public int LineCount { get; }

		public int EndCharacterIndex { get; }

		public string FilePath { get; }

		public bool Equals( SourceSpan other )
		{
			return
				string.Equals( FilePath, other.FilePath, StringComparison.Ordinal ) &&
				AbsoluteIndex == other.AbsoluteIndex &&
				LineIndex == other.LineIndex &&
				CharacterIndex == other.CharacterIndex &&
				Length == other.Length;
		}

		public override bool Equals( object obj )
		{
			return obj is SourceSpan span && Equals( span );
		}

		public override int GetHashCode()
		{
			var hash = HashCodeCombiner.Start();
			hash.Add( FilePath, StringComparer.Ordinal );
			hash.Add( AbsoluteIndex );
			hash.Add( LineIndex );
			hash.Add( CharacterIndex );
			hash.Add( Length );

			return hash;
		}

		public override string ToString()
		{
			return string.Format(
				CultureInfo.CurrentCulture, "({0}:{1},{2} [{3}] {4})",
				AbsoluteIndex,
				LineIndex,
				CharacterIndex,
				Length,
				FilePath );
		}

		public static bool operator ==( SourceSpan left, SourceSpan right )
		{
			return left.Equals( right );
		}

		public static bool operator !=( SourceSpan left, SourceSpan right )
		{
			return !left.Equals( right );
		}
	}
}
