///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   Socket.cs
//
//  Note:
//

using Microsoft.SingSharp;
using Microsoft.SingSharp.Runtime;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Directory;
using NetStack.Contracts;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace System.Net.Sockets
{
    public class Socket : IDisposable
    {
        InternalSocket internalSocket;
        bool disposed = false;

        // ================== Private Methods ==================

        private Socket(InternalSocket internalSocket)
        {
            this.internalSocket = internalSocket;
        }

        // ================== Public Methods ==================

        public Socket(AddressFamily addressFamily,
                      SocketType    socketType,
                      ProtocolType  protocolType)
        {
            // We only support TCP streams

            if (addressFamily != AddressFamily.InterNetwork) {
                throw new SocketException(SocketErrors.WSAEAFNOSUPPORT);
            }

            if (socketType == SocketType.Stream &&
                protocolType == ProtocolType.Tcp)
            {
                internalSocket = new TcpSocket();
                return;
            }

            if (socketType == SocketType.Dgram &&
                protocolType == ProtocolType.Udp)
            {
                internalSocket = new UdpSocket();
                return;
            }
            throw new SocketException(SocketErrors.WSAEPROTONOSUPPORT);
        }

        public void Dispose()
        {
            if (!disposed) {
                internalSocket.Dispose();
                disposed = true;
            }
        }

        public Socket Accept()
        {
            InternalSocket s = internalSocket.Accept();
            if (s != null)
                return new Socket(s);
            return null;
        }

        public void Bind(EndPoint localEP)
        {
            internalSocket.Bind(localEP);
        }

        public void Close()
        {
            internalSocket.Close();
        }

        public void Connect(EndPoint remoteEP)
        {
            internalSocket.Connect(remoteEP);
        }

        //
        //public object GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName)
        //{
        //  // TODO
        //  throw new NotSupportedException();
        //}
//
        //public void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue)
        //{
        //  // TODO
        //  throw new NotSupportedException();
        //}
//
        //public byte[] GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionLength)
        //{
        //  // TODO
        //  throw new NotSupportedException();
        //}
        //

        public void Listen(int backlog)
        {
            internalSocket.Listen(backlog);
        }

        public bool Poll(int microSeconds, SelectMode mode)
        {
            return internalSocket.Poll(microSeconds, mode);
        }

        public int Receive(byte[] buffer, int size, SocketFlags socketFlags)
        {
            return Receive(buffer, 0, size, socketFlags);
        }

        public int Receive(byte[] buffer, SocketFlags socketFlags)
        {
            return Receive(buffer, 0, buffer!=null ? buffer.Length : 0, socketFlags);
        }

        public int Receive(byte[] buffer)
        {
            return Receive(buffer, 0, buffer!=null ? buffer.Length : 0, SocketFlags.None);
        }

        public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags)
        {
            if (buffer == null) {
                throw new ArgumentNullException("buffer");
            }
            return internalSocket.Receive(buffer, offset, size, socketFlags);
        }

        //
        //public static void Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds)
        //{
        //  // TODO
        //  throw new NotSupportedException();
        //}
        //

        public int Send(byte[] buffer, int size, SocketFlags socketFlags)
        {
            return Send(buffer, 0, size, socketFlags);
        }

        public int Send(byte[] buffer, SocketFlags socketFlags)
        {
            return Send(buffer, 0, buffer!=null ? buffer.Length : 0, socketFlags);
        }

        public int Send(byte[] buffer)
        {
            return Send(buffer, 0, buffer!=null ? buffer.Length : 0, SocketFlags.None);
        }

        public int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags)
        {
            if (buffer == null) {
                throw new ArgumentException("buffer");
            }
            return internalSocket.Send(buffer, offset, size, socketFlags);
        }

        //
        //public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
        //{
        //  // TODO
        //  throw new NotSupportedException();
        //}
//
        //public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue)
        //{
        //  // TODO
        //  throw new NotSupportedException();
        //}
//
        //public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue)
        //{
        //  SetSocketOption(optionLevel, optionName, (optionValue?1:0));
        //}
        //

        public void Shutdown(SocketShutdown how)
        {
            internalSocket.Shutdown(how);
        }

        // ================== Properties ==================

        public static bool SupportsIPv4
        {
            get { return true; }
        }

        public static bool SupportsIPv6
        {
            get { return false; }
        }

        public int Available
        {
            get { return internalSocket.Available; }
        }

        public EndPoint LocalEndPoint
        {
            get { return internalSocket.LocalEndPoint; }
        }

        public EndPoint RemoteEndPoint
        {
            get { return internalSocket.RemoteEndPoint; }
        }

        public bool Blocking
        {
            get { return internalSocket.Blocking; }
        }

        public bool Connected
        {
            get { return internalSocket.Connected; }
        }

        public AddressFamily AddressFamily
        {
            get { return internalSocket.AddressFamily; }
        }

        public SocketType SocketType
        {
            get { return internalSocket.SocketType; }
        }

        public ProtocolType ProtocolType
        {
            get { return internalSocket.ProtocolType; }
        }

        // ================== Singularity-Specific ==================
        public int Send([Claims] byte[]! in ExHeap buffer, SocketFlags socketFlags)
        {
            return internalSocket.Send(buffer, socketFlags);
        }
    }
}
