using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Storage.Streams;

namespace Plane.Communication
{
    /// <summary>
    /// 所有以字节流形式进行通信的 <see cref="IConnection"/> 实现者的基类。
    /// </summary>
    public abstract class StreamConnection : ExceptionThrownEventSource, IConnection
    {
        protected bool _canFlush = true;
        protected IInputStream _inputStream;
        protected Stream _outputStream;
        private bool _reopening;

        public bool IsOpen { get; protected set; }

        public virtual void Close()
        {
            IsOpen = false;
            try
            {
                _inputStream?.Dispose();
            }
            catch { }
            try
            {
                _outputStream?.Dispose();
            }
            catch { }
        }

        public abstract Task OpenAsync();

        public virtual async Task<int> ReadAsync(byte[] buffer, int offset, int count)
        {
            try
            {
                IBuffer buf = new Windows.Storage.Streams.Buffer((uint)count);
                buf = await _inputStream.ReadAsync(buf, (uint)count, InputStreamOptions.None);
                buf.CopyTo(0, buffer, offset, count);
                return count;
            }
            catch (Exception ex)
            {
                RaiseExceptionThrown(ex);
                await ReopenIfNeededAsync();
                return 0;
            }
        }

        public virtual async Task WriteAsync(byte[] buffer, int offset, int count)
        {
            try
            {
                await _outputStream.WriteAsync(buffer, offset, count).ConfigureAwait(false);
                if (_canFlush)
                {
                    await _outputStream.FlushAsync().ConfigureAwait(false);
                }
            }
            catch (NotImplementedException) // 不支持 Flush。
            {
                _canFlush = false;
            }
            catch (Exception ex)
            {
                RaiseExceptionThrown(ex);
                await ReopenIfNeededAsync();
            }
        }

        private async Task ReopenIfNeededAsync()
        {
            if (!_reopening)
            {
                _reopening = true;
                Close();
                try
                {
                    await OpenAsync();
                }
                catch (Exception ex)
                {
                    RaiseExceptionThrown(ex);
                    await Task.Delay(1000);
                }
                _reopening = false;
            }
        }
    }
}
