//------------------------------------------------------------------------------
// <auto-generated />
// This comment is here to prevent StyleCop from analyzing a file originally from Serilog.
//------------------------------------------------------------------------------

// Copyright 2013-2019 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Modified by OpenTelemetry Authors.

namespace OpenTelemetry.AutoInstrumentation.Logging;

/// <summary>
/// A sink wrapper that periodically flushes the wrapped sink to disk.
/// </summary>
internal sealed class PeriodicFlushToDiskSink : ISink, IDisposable
{
    readonly ISink _sink;
    readonly Timer _timer;
    int _flushRequired;

    /// <summary>
    /// Construct a <see cref="PeriodicFlushToDiskSink"/> that wraps
    /// <paramref name="sink"/> and flushes it at the specified <paramref name="flushInterval"/>.
    /// </summary>
    /// <param name="sink">The sink to wrap.</param>
    /// <param name="flushInterval">The interval at which to flush the underlying sink.</param>
    /// <exception cref="ArgumentNullException">When <paramref name="sink"/> is <code>null</code></exception>
    public PeriodicFlushToDiskSink(RollingFileSink sink, TimeSpan flushInterval)
    {
        _sink = sink ?? throw new ArgumentNullException(nameof(sink));

        _timer = new Timer(_ => FlushToDisk(sink), null, flushInterval, flushInterval);
    }

    /// <inheritdoc />
    public void Write(string message)
    {
        _sink.Write(message);
        Interlocked.Exchange(ref _flushRequired, 1);
    }

    /// <inheritdoc />
    public void Dispose()
    {
        _timer.Dispose();
        (_sink as IDisposable)?.Dispose();
    }

    void FlushToDisk(RollingFileSink flushable)
    {
        try
        {
            if (Interlocked.CompareExchange(ref _flushRequired, 0, 1) == 1)
            {
                // May throw ObjectDisposedException, since we're not trying to synchronize
                // anything here in the wrapper.
                flushable.FlushToDisk();
            }
        }
        catch (Exception)
        {
            // Ignore
        }
    }
}
