using System;
using System.IO;
using System.Linq;

namespace Volpe.Cafe.IO.DSV
{
    /// <summary>
    /// Provides static methods for the creation and opening of a delimiter-separated values (DSV) file.
    /// </summary>
    /// <remarks>
    /// When escaping is enabled (i.e., <see cref="DsvReader.EscapeChar"/> or <see cref="DsvWriter.EscapeChar"/> is not
    /// the <see cref="NullChar"/>), the processing of DSV files (via the <see cref="DsvReader"/> and the
    /// <see cref="DsvWriter"/> classes) follow the RFC 4180 specification for the CSV file format (as outlined here:
    /// https://www.rfc-editor.org/rfc/rfc4180), but applicable in more generic terms to any DSV file. In particular,
    /// the DSV classes adapt the specifics related to the treatment of the delimiter character (e.g., comma or tab),
    /// the escape character (default is a double quote <c>'"'</c>, but may also be a single quote <c>'\''</c> or a
    /// backslash <c>'\\'</c>), and the various new line characters or string (CR or <c>'\r'</c>, LF or <c>'\n'</c>,
    /// CRLF or <c>"\r\n"</c>).
    /// </remarks>
    [Serializable]
    public static class DsvFile
    {
        //-----------------------------------------------------------------------------------------
        // NOTE:
        // We define a default newline string, instead of using environment-specific value, to get
        // the same exact output regardless of the platform used. This way, we can easily compare
        // outputs via text-based differs.
        //-----------------------------------------------------------------------------------------
        /// <summary>
        /// Defines a default new-line string used when writing DSV files.
        /// </summary>
        public const string DefaultNewLine = "\n";

        /// <summary>
        /// Defines a default escape character used to enclose fields that include special characters.
        /// </summary>
        public const char DefaultEscapeChar = '"';

        /// <summary>
        /// Defines the null character that is used for disabling escaping of values when reading from or writing to a
        /// DSV file.
        /// </summary>
        public const char NullChar = '\0';

        /// <summary>
        /// Defines a list of all allowable new line strings.
        /// </summary>
        public static readonly string[] NewLineStrings = { "\n", "\r", "\r\n" };


        /// <summary>
        /// Opens a DSV file at the specified <paramref name="path"/> for writing. If the file exists, it will be
        /// overwritten; otherwise, a new file will be created. The specified <paramref name="headers"/> will be
        /// written to the file upon successful opening.
        /// </summary>
        /// <param name="path">The complete file path to write to.</param>
        /// <param name="headers">An array of column headers to write to the DSV file.</param>
        /// <param name="delimiter">A character used for delimiting the columns in the DSV file.</param>
        /// <returns>A <see cref="DsvWriter"/> instance to use for writing the DSV file.</returns>
        /// <inheritdoc cref="DsvWriter(string, string[], char)" path="/exception"/>
        public static DsvWriter OpenWrite(string path, string[] headers, char delimiter)
        {
            ThrowHelper.ThrowIfHeadersNotValid(headers);
            return new DsvWriter(path, headers, delimiter);
        }

        /// <summary>
        /// Opens an existing DSV file at the specified <paramref name="path"/> for writing. If the file exists, it
        /// will be appended to; otherwise, an exception will be thrown.
        /// </summary>
        /// <param name="path">The complete file path to write to.</param>
        /// <param name="delimiter">A character used for delimiting the columns in the DSV file.</param>
        /// <returns>A <see cref="DsvWriter"/> instance to use for writing the DSV file.</returns>
        /// <exception cref="FileNotFoundException">The file cannot be found.</exception>
        /// <inheritdoc cref="StreamWriter(string, bool)" path="/exception"/>
        public static DsvWriter OpenAppend(string path, char delimiter)
        {
            ThrowHelper.ThrowIfFileNotFound(path);
            return new DsvWriter(path, null, delimiter);
        }

        /// <summary>
        /// Opens a DSV file at the specified <paramref name="path"/> for reading.
        /// </summary>
        /// <inheritdoc cref="DsvReader(string, char, char)" path="/param"/>
        /// <returns>A <see cref="DsvReader"/> instance to use for reading the DSV file.</returns>
        /// <inheritdoc cref="DsvReader(string, char, char)" path="/exception"/>
        public static DsvReader OpenRead(string path, char delimiter, char escapeChar = DefaultEscapeChar)
        {
            return new DsvReader(path, delimiter, escapeChar);
        }

        internal static bool IsValidNewLine(string value)
        {
            return NewLineStrings.Contains(value);
        }

    }
}
