using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using IOFile = System.IO.File;

namespace StarEngine.Core
{
    /// <summary>
    /// 汎用的なファイルシステムのためのメソッドを提供します。
    /// </summary>
    public static class FileSystem
    {
        /// <summary>
        /// 指定されたパスのファイルのストリームを返します。
        /// GZIP 圧縮されているならば、
        /// 解凍されたストリームを返します。
        /// </summary>
        /// <param name="path">パス。</param>
        /// <returns></returns>
        public static Stream Load(string path)
        {
            if (!IOFile.Exists(path))
                throw new FileNotFoundException(path + " が見つかりません。", path);

            FileStream fileStream = IOFile.OpenRead(path);
            byte[] localHeader = new byte[2];
            fileStream.Read(localHeader, 0, localHeader.Length);
            fileStream.Seek(0, SeekOrigin.Begin);
            return new FileSystemStream(fileStream, localHeader[0] == 0x1f && localHeader[1] == 0x8b);
        }

        /// <summary>
        /// FileSystem クラスが扱うファイルのストリームを表します。
        /// </summary>
        private class FileSystemStream : Stream
        {
            internal FileSystemStream(FileStream fileStream, bool decompress)
            {
                this.FileStream = fileStream;
                if (decompress)
                    this.GZipStream = new GZipStream(fileStream, CompressionMode.Decompress);
            }

            private FileStream FileStream;
            private GZipStream GZipStream;

            private Stream InnerStream
            {
                get
                {
                    if (this.GZipStream != null)
                        return this.GZipStream;
                    else
                        return this.FileStream;
                }
            }

            protected override void Dispose(bool disposing)
            {
                if (this.GZipStream != null)
                    this.GZipStream.Dispose();
                this.FileStream.Dispose();
                base.Dispose(disposing);
            }

            public override bool CanRead
            {
                get { return this.InnerStream.CanRead; }
            }

            public override bool CanSeek
            {
                get { return this.InnerStream.CanSeek; }
            }

            public override bool CanWrite
            {
                get { return this.InnerStream.CanWrite; }
            }

            public override void Flush()
            {
                this.InnerStream.Flush();
            }

            public override long Length
            {
                get { return this.InnerStream.Length; }
            }

            public override long Position
            {
                get { return this.InnerStream.Position; }
                set { this.InnerStream.Position = value; }
            }

            public override int Read(byte[] buffer, int offset, int count)
            {
                return this.InnerStream.Read(buffer, offset, count);
            }

            public override long Seek(long offset, SeekOrigin origin)
            {
                return this.InnerStream.Seek(offset, origin);
            }

            public override void SetLength(long value)
            {
                this.InnerStream.SetLength(value);
            }

            public override void Write(byte[] buffer, int offset, int count)
            {
                this.InnerStream.Write(buffer, offset, count);
            }
        }
    }
}
