using System;
using System.Collections.Generic;

namespace ConfusedPolarBear.Plugin.IntroSkipper;

#pragma warning disable CA1036 // Override methods on comparable types

/// <summary>
/// Range of contiguous time.
/// </summary>
public class TimeRange : IComparable
{
    /// <summary>
    /// Initializes a new instance of the <see cref="TimeRange"/> class.
    /// </summary>
    public TimeRange()
    {
        Start = 0;
        End = 0;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="TimeRange"/> class.
    /// </summary>
    /// <param name="start">Time range start.</param>
    /// <param name="end">Time range end.</param>
    public TimeRange(double start, double end)
    {
        Start = start;
        End = end;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="TimeRange"/> class.
    /// </summary>
    /// <param name="original">Original TimeRange.</param>
    public TimeRange(TimeRange original)
    {
        Start = original.Start;
        End = original.End;
    }

    /// <summary>
    /// Gets or sets the time range start (in seconds).
    /// </summary>
    public double Start { get; set; }

    /// <summary>
    /// Gets or sets the time range end (in seconds).
    /// </summary>
    public double End { get; set; }

    /// <summary>
    /// Gets the duration of this time range (in seconds).
    /// </summary>
    public double Duration => End - Start;

    /// <summary>
    /// Compare TimeRange durations.
    /// </summary>
    /// <param name="obj">Object to compare with.</param>
    /// <returns>int.</returns>
    public int CompareTo(object? obj)
    {
        if (!(obj is TimeRange tr))
        {
            throw new ArgumentException("obj must be a TimeRange");
        }

        return tr.Duration.CompareTo(Duration);
    }

    /// <summary>
    /// Tests if this TimeRange object intersects the provided TimeRange.
    /// </summary>
    /// <param name="tr">Second TimeRange object to test.</param>
    /// <returns>true if tr intersects the current TimeRange, false otherwise.</returns>
    public bool Intersects(TimeRange tr)
    {
        return
            (Start < tr.Start && tr.Start < End) ||
            (Start < tr.End && tr.End < End);
    }
}

#pragma warning restore CA1036

/// <summary>
/// Time range helpers.
/// </summary>
public static class TimeRangeHelpers
{
    /// <summary>
    /// Finds the longest contiguous time range.
    /// </summary>
    /// <param name="times">Sorted timestamps to search.</param>
    /// <param name="maximumDistance">Maximum distance permitted between contiguous timestamps.</param>
    /// <returns>The longest contiguous time range (if one was found), or null (if none was found).</returns>
    public static TimeRange? FindContiguous(double[] times, double maximumDistance)
    {
        if (times.Length == 0)
        {
            return null;
        }

        Array.Sort(times);

        var ranges = new List<TimeRange>();
        var currentRange = new TimeRange(times[0], times[0]);

        // For all provided timestamps, check if it is contiguous with its neighbor.
        for (var i = 0; i < times.Length - 1; i++)
        {
            var current = times[i];
            var next = times[i + 1];

            if (next - current <= maximumDistance)
            {
                currentRange.End = next;
                continue;
            }

            ranges.Add(new TimeRange(currentRange));
            currentRange = new TimeRange(next, next);
        }

        // Find and return the longest contiguous range.
        ranges.Sort();

        return (ranges.Count > 0) ? ranges[0] : null;
    }
}
