﻿#if NETFX_CORE

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.IO;
using System.Linq;
using System.Text;

using Windows.Storage;
using Windows.Storage.Streams;
using Windows.Foundation;

namespace BestHTTP.PlatformSupport.IO
{
    public class FileStream : Stream
    {
        private int readTimeout = -1;
        private int writeTimeout = 1000;
        internal const int DefaultBufferSize = 8192;
        private FileRandomAccessStream backend;
        private FileOptions fileOptions;
        private string name;

        public override bool CanRead { get { return this.backend.CanRead; } }
        public override bool CanSeek { get { return true; } }
        public override bool CanWrite { get { return this.backend.CanWrite; } }
        public virtual bool IsAsync { get { return (this.fileOptions & FileOptions.Asynchronous) > FileOptions.None; } }
        public override long Length { get { return (long)this.backend.Size; } }
        public override int ReadTimeout { get { return this.readTimeout; } set { this.readTimeout = value; } }
        public override int WriteTimeout { get { return this.writeTimeout; } set { this.writeTimeout = value; } }

        public string Name { get { return this.name; } }

        public override long Position
        {
            get { return (long)this.backend.Position; }
            set
            {
                try
                {
                    this.backend.Seek((ulong)value);
                }
                catch (Exception ex)
                {
                    throw FileStream.RethrowException(ex);
                }
            }
        }

        public FileStream(string file, FileMode mode)
            : this(file, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, FileShare.Read, 8192, FileOptions.None)
        {
        }

        public FileStream(string file, FileMode mode, FileAccess access)
            : this(file, mode, access, FileShare.Read, 8192, FileOptions.None)
        {
        }

        public FileStream(string file, FileMode mode, FileAccess access, FileShare share)
            : this(file, mode, access, share, 8192, FileOptions.None)
        {
        }

        public FileStream(string file, FileMode mode, FileAccess access, FileShare share, int bufferSize)
            : this(file, mode, access, share, bufferSize, FileOptions.None)
        {
        }

        public FileStream(string file, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
            : this(file, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
        {
        }

        public FileStream(string file, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
        {
            try
            {
                this.fileOptions = options;
                this.name = file;
                this.CheckAccess(mode, access);
                StorageFile storageFile;
                switch (mode)
                {
                    case FileMode.CreateNew:
                    case FileMode.Create:
                    case FileMode.OpenOrCreate:
                    case FileMode.Append:
                        storageFile = FileStream.CreateFile(file, mode, access);
                        break;
                    case FileMode.Open:
                    case FileMode.Truncate:
                        storageFile = FileStream.OpenFile(file, mode, access);
                        break;
                    default:
                        throw new ArgumentException("Unknown file mode");
                }
                IAsyncOperation<IRandomAccessStream> source = storageFile.OpenAsync(FileStream.GetAccessMode(access));
                WindowsRuntimeSystemExtensions.AsTask<IRandomAccessStream>(source).Wait();
                this.backend = (FileRandomAccessStream)source.GetResults();
                if (mode == FileMode.Truncate)
                {
                    this.backend.Size = 0UL;
                }
                else
                {
                    if (mode != FileMode.Append)
                        return;
                    this.backend.Seek(this.backend.Size);
                }
            }
            catch (Exception ex)
            {
                throw FileStream.RethrowException(ex);
            }
        }

        private void CheckAccess(FileMode mode, FileAccess access)
        {
            switch (mode)
            {
                case FileMode.CreateNew:
                    break;
                case FileMode.Create:
                    break;
                case FileMode.Open:
                    break;
                case FileMode.OpenOrCreate:
                    break;
                case FileMode.Truncate:
                    break;
                case FileMode.Append:
                    if (access == FileAccess.Write)
                        break;
                    else
                    {
                        System.Diagnostics.Debug.WriteLine("FileStream.CheckAccess: Bad access mode for Append");
                        throw new IOException("Bad access mode for Append");
                    }
                default:
                    throw new ArgumentException("Unknown file mode");
            }
        }

        private static StorageFile OpenFile(string file, FileMode mode, FileAccess access)
        {
            return FileHelper.GetFileForPathOrURI(file);
        }

        private static StorageFile CreateFile(string file, FileMode mode, FileAccess access)
        {
            IAsyncOperation<StorageFile> fileAsync = FileHelper.GetFolderForPathOrURI(file).CreateFileAsync(Path.GetFileName(file), FileStream.GetCollisionOption(mode, access));
            WindowsRuntimeSystemExtensions.AsTask<StorageFile>(fileAsync).Wait();
            if (fileAsync.Status != AsyncStatus.Completed)
            {
                System.Diagnostics.Debug.WriteLine("FileStream.CheckAccess: Failed to create file " + file);
                throw new IOException("Failed to create file " + file);
            }
            else
                return fileAsync.GetResults();
        }

        public override void Flush()
        {
            try
            {
                WindowsRuntimeSystemExtensions.AsTask<bool>(this.backend.FlushAsync()).Wait();
            }
            catch (Exception ex)
            {
                throw FileStream.RethrowException(ex);
            }
        }

        public void Flush(bool flushToDisc)
        {
            Flush();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            try
            {
                Windows.Storage.Streams.Buffer buffer1 = new Windows.Storage.Streams.Buffer((uint)count);
                WindowsRuntimeSystemExtensions.AsTask<IBuffer, uint>(this.backend.ReadAsync((IBuffer)buffer1, (uint)count, InputStreamOptions.ReadAhead)).Wait(this.readTimeout);
                int length = (int)buffer1.Length;
                DataReader dataReader = DataReader.FromBuffer((IBuffer)buffer1);
                bool flag = offset == 0 && buffer.Length == count && length == count;
                byte[] numArray = flag ? buffer : new byte[length];
                dataReader.ReadBytes(numArray);
                if (!flag)
                    Array.Copy((Array)numArray, 0, (Array)buffer, offset, numArray.Length);
                return length;
            }
            catch (Exception ex)
            {
                throw FileStream.RethrowException(ex);
            }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            try
            {
                switch (origin)
                {
                    case SeekOrigin.Begin:
                        if (offset > (long)this.backend.Size)
                        {
                            offset = (long)this.backend.Size;
                            break;
                        }
                        else
                            break;
                    case SeekOrigin.Current:
                        if ((long)this.backend.Position + offset > (long)this.backend.Size)
                        {
                            offset = (long)this.backend.Position + offset;
                            break;
                        }
                        else
                            break;
                    case SeekOrigin.End:
                        if (offset >= 0L)
                        {
                            offset = (long)this.backend.Size;
                            break;
                        }
                        else
                        {
                            offset += (long)this.backend.Size;
                            break;
                        }
                }
                if (offset < 0L)
                    offset = 0L;
                this.backend.Seek((ulong)offset);
                return offset;
            }
            catch (Exception ex)
            {
                throw FileStream.RethrowException(ex);
            }
        }

        public override void SetLength(long value)
        {
            try
            {
                this.backend.Size = (ulong)value;
            }
            catch (Exception ex)
            {
                throw FileStream.RethrowException(ex);
            }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            try
            {
                Windows.Storage.Streams.Buffer buffer1 = new Windows.Storage.Streams.Buffer((uint)count);
                byte[] numArray;
                if (offset == 0 && count == buffer.Length)
                {
                    numArray = buffer;
                }
                else
                {
                    numArray = new byte[count];
                    Array.Copy((Array)buffer, offset, (Array)numArray, 0, count);
                }
                DataWriter dataWriter = new DataWriter();
                dataWriter.WriteBytes(numArray);
                WindowsRuntimeSystemExtensions.AsTask<uint, uint>(this.backend.WriteAsync(dataWriter.DetachBuffer())).Wait(this.writeTimeout);
            }
            catch (Exception ex)
            {
                throw FileStream.RethrowException(ex);
            }
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            this.backend.Dispose();
        }

        public void Close()
        {
            base.Dispose();
        }

        private static Exception RethrowException(Exception e)
        {
            System.Diagnostics.Debug.WriteLine("FileStream.RethrowException: " + e.Message + "\n" + e.StackTrace);
            if (e.GetType() == typeof(IOException))
                return e;
            else
                return (Exception)new IOException(e.Message, e);
        }

        private static CreationCollisionOption GetCollisionOption(FileMode mode, FileAccess access)
        {
            CreationCollisionOption creationCollisionOption = CreationCollisionOption.GenerateUniqueName;
            switch (mode)
            {
                case FileMode.CreateNew:
                    creationCollisionOption = CreationCollisionOption.FailIfExists;
                    break;
                case FileMode.Create:
                case FileMode.Truncate:
                    creationCollisionOption = CreationCollisionOption.ReplaceExisting;
                    break;
                case FileMode.Open:
                case FileMode.OpenOrCreate:
                case FileMode.Append:
                    creationCollisionOption = CreationCollisionOption.OpenIfExists;
                    break;
            }
            return creationCollisionOption;
        }

        private static FileAccessMode GetAccessMode(FileAccess access)
        {
            switch (access)
            {
                case FileAccess.Read:
                    return FileAccessMode.Read;
                default:
                    return FileAccessMode.ReadWrite;
            }
        }
    }
}

#endif