// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace SixLabors.PolygonClipper {
///
/// Represents a status line for the sweep line algorithm, maintaining a sorted collection of sweep events.
///
/// Performance Characteristics:
/// - **Insertion**: O(n) in the worst case. The operation consists of:
/// 1. A binary search (O(log n)) to determine the correct insertion point.
/// 2. A shift operation to move subsequent elements in the list (O(k)), where k is the number of elements
/// after the insertion index. In the worst case, this can approach O(n).
/// - **Removal**: O(n) in the worst case. After finding the index of the element to remove, subsequent
/// elements in the list need to be shifted (O(k)), where k is the number of elements after the removed index.
/// - **Next/Previous Access**: O(1) after the index is known, as the list provides constant-time indexing.
///
/// The implementation ensures efficient neighbor traversal (next/previous) at O(1), making it suitable for
/// algorithms where neighboring elements are accessed frequently. The use of `BinarySearch` minimizes the cost
/// of insertion/removal compared to naive search-based approaches.
///
[DebuggerDisplay("Count = {Count}")]
internal sealed class StatusLine
{
private const int DefaultCapacity = 16;
private readonly List sortedEvents;
private readonly SegmentComparer comparer = new();
public StatusLine()
: this(DefaultCapacity)
{
}
public StatusLine(int capacity)
=> this.sortedEvents = new List(capacity > 0 ? capacity : DefaultCapacity);
///
/// Gets the number of events in the status line.
///
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.sortedEvents.Count;
}
///
/// Gets the minimum sweep event in the status line (first in sort order).
///
public SweepEvent Min
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.sortedEvents[0];
}
///
/// Gets the maximum sweep event in the status line (last in sort order).
///
public SweepEvent Max
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.sortedEvents[^1];
}
///
/// Gets the retained list capacity.
///
public int RetainedCapacity => this.sortedEvents.Capacity;
///
/// Gets the event at the specified index.
///
/// The index of the event.
/// The sweep event at the given index.
public SweepEvent this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.sortedEvents[index];
}
///
/// Clears active events and ensures the desired capacity.
///
/// Desired minimum capacity.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(int capacity)
{
this.sortedEvents.Clear();
if (capacity > this.sortedEvents.Capacity)
{
this.sortedEvents.EnsureCapacity(capacity);
}
}
///
/// Adds a sweep event into the status line, maintaining sorted order.
///
/// The sweep event to insert.
/// The index where the event was inserted.
public int Add(SweepEvent e)
{
int index = this.sortedEvents.BinarySearch(e, this.comparer);
if (index < 0)
{
index = ~index; // Get the correct insertion point
}
this.sortedEvents.Insert(index, e);
e.PosSL = index;
return index;
}
///
/// Removes a sweep event from the status line.
///
/// The index of the event to remove.
///
/// Thrown if is less than 0 or greater than or equal to the number of events.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int index)
=> this.sortedEvents.RemoveAt(index);
///
/// Finds the current index of a sweep event in the status line.
///
/// The event to locate.
/// The index of the event, or -1 if it is not present.
public int IndexOf(SweepEvent e)
{
List events = this.sortedEvents;
int count = events.Count;
int hint = e.PosSL;
if ((uint)hint < (uint)count && ReferenceEquals(events[hint], e))
{
return hint;
}
int index = events.BinarySearch(e, this.comparer);
if (index >= 0)
{
if (ReferenceEquals(events[index], e))
{
e.PosSL = index;
return index;
}
// BinarySearch can return any comparer-equal slot. Scan local ties by reference.
for (int i = index - 1; i >= 0 && this.comparer.Compare(events[i], e) == 0; i--)
{
if (ReferenceEquals(events[i], e))
{
e.PosSL = i;
return i;
}
}
for (int i = index + 1; i < count && this.comparer.Compare(events[i], e) == 0; i++)
{
if (ReferenceEquals(events[i], e))
{
e.PosSL = i;
return i;
}
}
}
// Fail-safe reference lookup for correctness if comparer order is temporarily unstable.
for (int i = 0; i < count; i++)
{
if (ReferenceEquals(events[i], e))
{
e.PosSL = i;
return i;
}
}
return -1;
}
///
/// Gets the next sweep event relative to the given index.
///
/// The reference index.
/// The next sweep event, or null if none exists.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SweepEvent? Next(int index)
{
if (index >= 0 && index < this.sortedEvents.Count - 1)
{
return this.sortedEvents[index + 1];
}
return null;
}
///
/// Gets the previous sweep event relative to the given index.
///
/// The reference index.
/// The previous sweep event, or null if none exists.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SweepEvent? Prev(int index)
{
if (index > 0 && index < this.sortedEvents.Count)
{
return this.sortedEvents[index - 1];
}
return null;
}
}
}