Code/Wasm/Utils/IndentedTextWriter.cs
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable enable

using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WasmBox.Wasm {
    public class IndentedTextWriter : TextWriter {
        private readonly TextWriter _writer;
        private readonly string _tabString;
        private int _indentLevel;
        private bool _tabsPending;

        public const string DefaultTabString = "    ";

        public IndentedTextWriter(TextWriter writer) : this(writer, DefaultTabString) { }

        public IndentedTextWriter(TextWriter writer, string tabString) : base(CultureInfo.InvariantCulture) {
            ArgumentNullException.ThrowIfNull(writer);

            _writer = writer;
            _tabString = tabString;
            _tabsPending = true;
        }

        public override Encoding Encoding => _writer.Encoding;

        [AllowNull]
        public override string NewLine {
            get { return _writer.NewLine; }
            set { _writer.NewLine = value; }
        }

        public int Indent {
            get { return _indentLevel; }
            set { _indentLevel = Math.Max(value, 0); }
        }

        public TextWriter InnerWriter => _writer;

        public override void Close() => _writer.Close();

        /// <inheritdoc/>
        // public override ValueTask DisposeAsync() => _writer.DisposeAsync();

        public override void Flush() => _writer.Flush();

        /// <inheritdoc/>
        // public override Task FlushAsync() => _writer.FlushAsync();

        /// <summary>
        /// Clears all buffers for this <see cref="IndentedTextWriter"/> asynchronously and causes any buffered data to be
        /// written to the underlying device.
        /// </summary>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous flush operation.</returns>
        // public override Task FlushAsync(CancellationToken cancellationToken) =>
        //     cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
        //     GetType() != typeof(IndentedTextWriter) ? FlushAsync() :
        //     _writer.FlushAsync(cancellationToken);

        protected virtual void OutputTabs() {
            if (_tabsPending) {
                for (int i = 0; i < _indentLevel; i++) {
                    _writer.Write(_tabString);
                }
                _tabsPending = false;
            }
        }

        /// <summary>
        /// Asynchronously outputs tabs to the underlying <see cref="TextWriter"/> based on the current <see cref="Indent"/>.
        /// </summary>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // protected virtual async Task OutputTabsAsync()
        // {
        //     if (_tabsPending)
        //     {
        //         for (int i = 0; i < _indentLevel; i++)
        //         {
        //             await _writer.WriteAsync(_tabString).ConfigureAwait(false);
        //         }
        //         _tabsPending = false;
        //     }
        // }

        public override void Write(string? s) {
            OutputTabs();
            _writer.Write(s);
        }

        public override void Write(bool value) {
            OutputTabs();
            _writer.Write(value);
        }

        public override void Write(char value) {
            OutputTabs();
            _writer.Write(value);
        }

        public override void Write(char[]? buffer) {
            OutputTabs();
            _writer.Write(buffer);
        }

        public override void Write(char[] buffer, int index, int count) {
            OutputTabs();
            _writer.Write(buffer, index, count);
        }

        public override void Write(double value) {
            OutputTabs();
            _writer.Write(value);
        }

        public override void Write(float value) {
            OutputTabs();
            _writer.Write(value);
        }

        public override void Write(int value) {
            OutputTabs();
            _writer.Write(value);
        }

        public override void Write(long value) {
            OutputTabs();
            _writer.Write(value);
        }

        public override void Write(object? value) {
            OutputTabs();
            _writer.Write(value);
        }

        public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) {
            OutputTabs();
            _writer.Write(format, arg0);
        }

        public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) {
            OutputTabs();
            _writer.Write(format, arg0, arg1);
        }

        public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) {
            OutputTabs();
            _writer.Write(format, arg);
        }

        /// <summary>
        /// Asynchronously writes the specified <see cref="char"/> to the underlying <see cref="TextWriter"/>, inserting
        /// tabs at the start of every line.
        /// </summary>
        /// <param name="value">The <see cref="char"/> to write.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteAsync(char value)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteAsync(value).ConfigureAwait(false);
        // }

        /// <summary>
        /// Asynchronously writes the specified number of <see cref="char"/>s from the specified buffer
        /// to the underlying <see cref="TextWriter"/>, starting at the specified index, and outputting tabs at the
        /// start of every new line.
        /// </summary>
        /// <param name="buffer">The array to write from.</param>
        /// <param name="index">Index in the array to stort writing at.</param>
        /// <param name="count">The number of characters to write.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteAsync(char[] buffer, int index, int count)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteAsync(buffer, index, count).ConfigureAwait(false);
        // }

        /// <summary>
        /// Asynchronously writes the specified string to the underlying <see cref="TextWriter"/>, inserting tabs at the
        /// start of every line.
        /// </summary>
        /// <param name="value">The string to write.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteAsync(string? value)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteAsync(value).ConfigureAwait(false);
        // }

        /// <summary>
        /// Asynchronously writes the specified characters to the underlying <see cref="TextWriter"/>, inserting tabs at the
        /// start of every line.
        /// </summary>
        /// <param name="buffer">The characters to write.</param>
        /// <param name="cancellationToken">Token for canceling the operation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
        // }

        /// <summary>
        /// Asynchronously writes the contents of the specified <see cref="StringBuilder"/> to the underlying <see cref="TextWriter"/>, inserting tabs at the
        /// start of every line.
        /// </summary>
        /// <param name="value">The text to write.</param>
        /// <param name="cancellationToken">Token for canceling the operation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteAsync(value, cancellationToken).ConfigureAwait(false);
        // }

        public void WriteLineNoTabs(string? s) {
            _writer.WriteLine(s);
        }

        /// <summary>
        /// Asynchronously writes the specified string to the underlying <see cref="TextWriter"/> without inserting tabs.
        /// </summary>
        /// <param name="s">The string to write.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public Task WriteLineNoTabsAsync(string? s)
        // {
        //     return _writer.WriteLineAsync(s);
        // }

        public override void WriteLine(string? s) {
            OutputTabs();
            _writer.WriteLine(s);
            _tabsPending = true;
        }

        public override void WriteLine() {
            OutputTabs();
            _writer.WriteLine();
            _tabsPending = true;
        }

        public override void WriteLine(bool value) {
            OutputTabs();
            _writer.WriteLine(value);
            _tabsPending = true;
        }

        public override void WriteLine(char value) {
            OutputTabs();
            _writer.WriteLine(value);
            _tabsPending = true;
        }

        public override void WriteLine(char[]? buffer) {
            OutputTabs();
            _writer.WriteLine(buffer);
            _tabsPending = true;
        }

        public override void WriteLine(char[] buffer, int index, int count) {
            OutputTabs();
            _writer.WriteLine(buffer, index, count);
            _tabsPending = true;
        }

        public override void WriteLine(double value) {
            OutputTabs();
            _writer.WriteLine(value);
            _tabsPending = true;
        }

        public override void WriteLine(float value) {
            OutputTabs();
            _writer.WriteLine(value);
            _tabsPending = true;
        }

        public override void WriteLine(int value) {
            OutputTabs();
            _writer.WriteLine(value);
            _tabsPending = true;
        }

        public override void WriteLine(long value) {
            OutputTabs();
            _writer.WriteLine(value);
            _tabsPending = true;
        }

        public override void WriteLine(object? value) {
            OutputTabs();
            _writer.WriteLine(value);
            _tabsPending = true;
        }

        public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) {
            OutputTabs();
            _writer.WriteLine(format, arg0);
            _tabsPending = true;
        }

        public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) {
            OutputTabs();
            _writer.WriteLine(format, arg0, arg1);
            _tabsPending = true;
        }

        public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) {
            OutputTabs();
            _writer.WriteLine(format, arg);
            _tabsPending = true;
        }

        // [CLSCompliant(false)]
        public override void WriteLine(uint value) {
            OutputTabs();
            _writer.WriteLine(value);
            _tabsPending = true;
        }

        /// <inheritdoc/>
        // public override async Task WriteLineAsync()
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteLineAsync().ConfigureAwait(false);
        //     _tabsPending = true;
        // }

        // /// <summary>
        // /// Asynchronously writes the specified <see cref="char"/> to the underlying <see cref="TextWriter"/> followed by a line terminator, inserting tabs
        // /// at the start of every line.
        // /// </summary>
        // /// <param name="value">The character to write.</param>
        // /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteLineAsync(char value)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteLineAsync(value).ConfigureAwait(false);
        //     _tabsPending = true;
        // }

        // /// <summary>
        // /// Asynchronously writes the specified number of characters from the specified buffer followed by a line terminator,
        // /// to the underlying <see cref="TextWriter"/>, starting at the specified index within the buffer, inserting tabs at the start of every line.
        // /// </summary>
        // /// <param name="buffer">The buffer containing characters to write.</param>
        // /// <param name="index">The index within the buffer to start writing at.</param>
        // /// <param name="count">The number of characters to write.</param>
        // /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteLineAsync(char[] buffer, int index, int count)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteLineAsync(buffer, index, count).ConfigureAwait(false);
        //     _tabsPending = true;
        // }

        // /// <summary>
        // /// Asynchronously writes the specified string followed by a line terminator to the underlying <see cref="TextWriter"/>, inserting
        // /// tabs at the start of every line.
        // /// </summary>
        // /// <param name="value">The string to write.</param>
        // /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteLineAsync(string? value)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteLineAsync(value).ConfigureAwait(false);
        //     _tabsPending = true;
        // }

        // /// <summary>
        // /// Asynchronously writes the specified characters followed by a line terminator to the underlying <see cref="TextWriter"/>, inserting
        // /// tabs at the start of every line.
        // /// </summary>
        // /// <param name="buffer">The characters to write.</param>
        // /// <param name="cancellationToken">Token for canceling the operation.</param>
        // /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteLineAsync(buffer, cancellationToken).ConfigureAwait(false);
        //     _tabsPending = true;
        // }

        // /// <summary>
        // /// Asynchronously writes the contents of the specified <see cref="StringBuilder"/> followed by a line terminator to the
        // /// underlying <see cref="TextWriter"/>, inserting tabs at the start of every line.
        // /// </summary>
        // /// <param name="value">The text to write.</param>
        // /// <param name="cancellationToken">Token for canceling the operation.</param>
        // /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        // public override async Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default)
        // {
        //     await OutputTabsAsync().ConfigureAwait(false);
        //     await _writer.WriteLineAsync(value, cancellationToken).ConfigureAwait(false);
        //     _tabsPending = true;
        // }
    }
}