﻿using System;
using System.Globalization;
using System.IO;
using System.Linq;

namespace FSO.Files.Utils
{
    public interface BCFReadProxy : IDisposable
    {
        byte ReadByte();
        ushort ReadUInt16();
        short ReadInt16();
        int ReadInt32();
        uint ReadUInt32();
        float ReadFloat();
        string ReadPascalString();
        string ReadLongPascalString();
    }

    public interface BCFWriteProxy : IDisposable
    {
        void WriteByte(byte data);
        void WriteUInt16(ushort data);
        void WriteInt16(short data);
        void WriteInt32(int data);
        void WriteUInt32(uint data);
        void WriteFloat(float data);
        void WritePascalString(string data);
        void WriteLongPascalString(string data);

        void SetGrouping(int groupSize);
    }

    public class BCFReadString : BCFReadProxy
    {
        private StreamReader Reader;
        public int Version;
        private string[] NumBuf = new string[0];
        private int NumInd = 1;

        public BCFReadString(Stream input, bool version)
        {
            Reader = new StreamReader(input);

            if (!version) return;
            //skip to version
            var line = "";
            while (!line.StartsWith("version "))
                line = Reader.ReadLine();
            Version = int.Parse(line.Substring(8));
        }

        private string ReadNum()
        {
            //contrary to popular belief, this function that returns a string does indeed read a number
            if (NumInd >= NumBuf.Length)
            {
                NumBuf = Reader.ReadLine().Trim().Split(' ').ToArray();
                NumInd = 0;
            }
            return NumBuf[NumInd++];
        }

        public byte ReadByte() { return byte.Parse(ReadNum()); }
        public ushort ReadUInt16() { return ushort.Parse(ReadNum()); }
        public short ReadInt16() { return short.Parse(ReadNum()); }
        public int ReadInt32() { return int.Parse(ReadNum()); }
        public uint ReadUInt32() { return uint.Parse(ReadNum()); }
        public float ReadFloat() { return float.Parse(ReadNum(), CultureInfo.InvariantCulture); }
        public string ReadPascalString() { return Reader.ReadLine(); }
        public string ReadLongPascalString() { return Reader.ReadLine(); }

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

    public class BCFWriteString : BCFWriteProxy
    {
        private StreamWriter Writer;
        public int Version;
        private int GroupSize = 1;
        private int GroupInd;

        public BCFWriteString(Stream input, bool version)
        {
            Writer = new StreamWriter(input);

            if (!version) return;
            //write out default version
            Writer.WriteLine("version 300");
        }

        public void SetGrouping(int groupSize)
        {
            if (GroupInd > 0) Writer.WriteLine();
            GroupInd = 0;
            GroupSize = groupSize;
        }

        private void WriteNum(string num)
        {
            if (GroupSize-1 >= GroupInd)
            {
                Writer.WriteLine(num);
                GroupInd = 0;
            } else
            {
                Writer.Write(num + " ");
            }
        }

        public void WriteByte(byte data) { WriteNum(data.ToString()); }
        public void WriteUInt16(ushort data) { WriteNum(data.ToString()); }
        public void WriteInt16(short data) { WriteNum(data.ToString()); }
        public void WriteInt32(int data) { WriteNum(data.ToString()); }
        public void WriteUInt32(uint data) { WriteNum(data.ToString()); }
        public void WriteFloat(float data) { WriteNum(data.ToString(CultureInfo.InvariantCulture)); }
        public void WritePascalString(string data) { WriteNum(data); }
        public void WriteLongPascalString(string data) { WriteNum(data); }

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