using System;
using System.Collections.Generic;
using global::System.IO.Pipes;
using System.Threading.Tasks;

namespace Pilz.IO
{
    public class ManagedPipeClient : ManagedPipe
    {
        private PipeStream pipeStream;
        private byte[] _Buf = new byte[1024];

        public ManagedPipeClient(string pipeName) : this(pipeName, ".")
        {
        }

        public ManagedPipeClient(string pipeName, string serverName) : this(pipeName, serverName, -1)
        {
        }

        public ManagedPipeClient(string pipeName, int connectionTimeout) : this(pipeName, ".", connectionTimeout)
        {
        }

        public ManagedPipeClient(string pipeName, string serverName, int connectionTimeout)
        {
            var clnt = new NamedPipeClientStream(serverName, pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
            clnt.Connect(connectionTimeout);
            if (!clnt.IsConnected)
            {
                throw new TimeoutException("Connection timeout!");
            }

            SetPipe(clnt);
        }

        public ManagedPipeClient(PipeStream pipe)
        {
            SetPipe(pipe);
        }

        private void SetPipe(PipeStream pipe)
        {
            pipeStream = pipe;
            pipeStream.BeginRead(_Buf, 0, _Buf.Length, EndRead, null);
        }

        private void EndRead(IAsyncResult ar)
        {
            if (IsDisposed)
                return;
            int bytesCount = pipeStream.EndRead(ar);
            if (bytesCount == 0) // leere Datenübermittlung signalisiert Verbindungsabbruch
            {
                Dispose();
                return;
            }

            var list = new List<byte>();
            for (int i = 0, loopTo = bytesCount - 1; i <= loopTo; i++)
                list.Add(_Buf[i]);
            while (bytesCount == _Buf.Length)
            {
                bytesCount = pipeStream.Read(_Buf, 0, _Buf.Length);
                for (int i = 0, loopTo1 = bytesCount - 1; i <= loopTo1; i++)
                    list.Add(_Buf[i]);
            }

            var deargs = new DataEventArgs(list.ToArray());
            OnRetriveData(deargs);

            pipeStream.BeginRead(_Buf, 0, _Buf.Length, EndRead, null);
        }

        public override Task SendAsnyc(byte[] bytes)
        {
            return Task.Run(() => Send(bytes));
        }

        public override void Send(byte[] data)
        {
            pipeStream.Write(data, 0, data.Length);
        }

        protected override void Dispose(bool disposing)
        {
            pipeStream.Dispose();
        }
    }
}