using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace BrotliCalc.Helpers{
    abstract class Table{
        protected readonly IList<string> columns;

        protected Table(string[] columns){
            this.columns = columns;
        }

        protected IEnumerable<string> ReadRowAsStrings(object?[] values){
            if (values.Length != columns.Count){
                throw new ArgumentException($"Amount of entries must match the amount of columns ({values.Length} != {columns.Count}).", nameof(values));
            }

            return values.Select(RowValueToString);
        }

        public abstract void AddRow(params object?[] values);
        protected abstract string RowValueToString(object? value);

        internal class CSV : Table, IDisposable{
            private static readonly HashSet<TypeCode> OmitQuotes = new HashSet<TypeCode>{
                TypeCode.SByte, TypeCode.Byte,
                TypeCode.Int16, TypeCode.UInt16,
                TypeCode.Int32, TypeCode.UInt32,
                TypeCode.Int64, TypeCode.UInt64,
                TypeCode.Single,
                TypeCode.Double,
                TypeCode.Decimal
            };

            private static bool CanOmitQuotes(object value){
                return OmitQuotes.Contains(Type.GetTypeCode(value.GetType()));
            }

            private readonly StreamWriter writer;

            public CSV(string path, string[] columns) : base(columns){
                this.writer = new StreamWriter(path);
                this.writer.WriteLine(string.Join(',', this.columns));
                this.writer.Flush();
            }

            protected override string RowValueToString(object? value){
                return value == null ? "?" : CanOmitQuotes(value) ? value.ToString()! : $"\"{value}\"";
            }

            public override void AddRow(params object?[] values){
                writer.Write(string.Join(",", ReadRowAsStrings(values)));
                writer.WriteLine();
                writer.Flush();
            }

            public void Dispose(){
                writer.Dispose();
            }
        }
    }
}