//------------------------------------------------------------------------------
// <copyright file="_OverlappedAsyncResult.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

namespace System.Net.Sockets {
    using System;
    using System.Net;
    using System.Runtime.InteropServices;
    using System.Threading;
    using Microsoft.Win32;
    using System.Collections.Generic;

    //
    //  OverlappedAsyncResult - used to take care of storage for async Socket operation
    //   from the BeginSend, BeginSendTo, BeginReceive, BeginReceiveFrom calls.
    //
    unsafe internal class ReceiveMessageOverlappedAsyncResult: BaseOverlappedAsyncResult {

        //
        // internal class members
        //
        private UnsafeNclNativeMethods.OSSOCK.WSAMsg* m_Message;
        internal SocketAddress  SocketAddressOriginal;
        internal SocketAddress  m_SocketAddress;
        private WSABuffer*     m_WSABuffer;
        private byte[]         m_WSABufferArray;
        private byte[]         m_ControlBuffer;
        internal byte[]        m_MessageBuffer;
        internal SocketFlags   m_flags;

        private static readonly int s_ControlDataSize =  Marshal.SizeOf(typeof(UnsafeNclNativeMethods.OSSOCK.ControlData));
        private static readonly int s_ControlDataIPv6Size =  Marshal.SizeOf(typeof(UnsafeNclNativeMethods.OSSOCK.ControlDataIPv6));
        private static readonly int s_WSABufferSize=Marshal.SizeOf(typeof(WSABuffer));
        private static readonly int s_WSAMsgSize = Marshal.SizeOf(typeof(UnsafeNclNativeMethods.OSSOCK.WSAMsg));
            
        internal IPPacketInformation m_IPPacketInformation;

        //
        // the following two will be used only on WinNT to enable completion ports
        //
        //
        // Constructor. We take in the socket that's creating us, the caller's
        // state object, and the buffer on which the I/O will be performed.
        // We save the socket and state, pin the callers's buffer, and allocate
        // an event for the WaitHandle.
        //
        internal ReceiveMessageOverlappedAsyncResult(Socket socket, Object asyncState, AsyncCallback asyncCallback) :
            base(socket, asyncState, asyncCallback)
        { }

        internal IntPtr GetSocketAddressSizePtr()
        {
            return Marshal.UnsafeAddrOfPinnedArrayElement(m_SocketAddress.m_Buffer, m_SocketAddress.GetAddressSizeOffset());
        }
              
        internal SocketAddress SocketAddress {
            get {
                return m_SocketAddress;
            }
        }
        
        
        //
        // SetUnmanagedStructures -
        // Fills in Overlapped Structures used in an Async Overlapped Winsock call
        //   these calls are outside the runtime and are unmanaged code, so we need
        //   to prepare specific structures and ints that lie in unmanaged memory
        //   since the Overlapped calls can be Async
        //

        internal void SetUnmanagedStructures(byte[] buffer, int offset, int size, SocketAddress socketAddress, SocketFlags socketFlags)
        {
            m_MessageBuffer = new byte[s_WSAMsgSize];
            m_WSABufferArray = new byte[s_WSABufferSize];

            //ipv4 or ipv6?
            IPAddress ipAddress = (socketAddress.Family == AddressFamily.InterNetworkV6 
                ? socketAddress.GetIPAddress() : null);
            bool ipv4 = (((Socket)AsyncObject).AddressFamily == AddressFamily.InterNetwork
                || (ipAddress != null && ipAddress.IsIPv4MappedToIPv6)); // DualMode
            bool ipv6 = ((Socket)AsyncObject).AddressFamily == AddressFamily.InterNetworkV6;

            //prepare control buffer
            if (ipv4) {
                m_ControlBuffer = new byte[s_ControlDataSize];
            }
            else if (ipv6) {
                m_ControlBuffer = new byte[s_ControlDataIPv6Size];
            }

            //pin buffers
            object[] objectsToPin = new object[(m_ControlBuffer != null)?5:4];
            objectsToPin[0] = buffer;
            objectsToPin[1] = m_MessageBuffer;
            objectsToPin[2] = m_WSABufferArray;
            
            //prepare socketaddress buffer
            m_SocketAddress = socketAddress;
            m_SocketAddress.CopyAddressSizeIntoBuffer();
            objectsToPin[3] = m_SocketAddress.m_Buffer;
                
            if(m_ControlBuffer != null){
                objectsToPin[4] = m_ControlBuffer;
            }

            base.SetUnmanagedStructures(objectsToPin);

            //prepare data buffer
            m_WSABuffer = (WSABuffer*) Marshal.UnsafeAddrOfPinnedArrayElement(m_WSABufferArray, 0);
            m_WSABuffer->Length = size;
            m_WSABuffer->Pointer = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset);


            //setup structure
            m_Message = (UnsafeNclNativeMethods.OSSOCK.WSAMsg*) Marshal.UnsafeAddrOfPinnedArrayElement(m_MessageBuffer, 0);
            m_Message->socketAddress = Marshal.UnsafeAddrOfPinnedArrayElement(m_SocketAddress.m_Buffer,0);
            m_Message->addressLength = (uint)m_SocketAddress.Size;
            m_Message->buffers = Marshal.UnsafeAddrOfPinnedArrayElement(m_WSABufferArray,0);
            m_Message->count = 1;

            if(m_ControlBuffer != null){
                m_Message->controlBuffer.Pointer = Marshal.UnsafeAddrOfPinnedArrayElement(m_ControlBuffer, 0);
                m_Message->controlBuffer.Length = m_ControlBuffer.Length;
            }

            m_Message->flags = socketFlags;
        }

        internal void SetUnmanagedStructures(byte[] buffer, int offset, int size, SocketAddress socketAddress, SocketFlags socketFlags, ref OverlappedCache overlappedCache)
        {
            SetupCache(ref overlappedCache);
            SetUnmanagedStructures(buffer, offset, size, socketAddress, socketFlags);
        }

        unsafe private void InitIPPacketInformation(){
            
            IPAddress address = null;

            //ipv4
            if (m_ControlBuffer.Length == s_ControlDataSize) {
                UnsafeNclNativeMethods.OSSOCK.ControlData controlData = (UnsafeNclNativeMethods.OSSOCK.ControlData)Marshal.PtrToStructure(m_Message->controlBuffer.Pointer, typeof(UnsafeNclNativeMethods.OSSOCK.ControlData));
                if(controlData.length != UIntPtr.Zero)
                {
                    address = new IPAddress((long)controlData.address);
                }
                m_IPPacketInformation = new IPPacketInformation(((address!=null)?address:IPAddress.None),(int)controlData.index);
            }
            //ipv6
            else if (m_ControlBuffer.Length == s_ControlDataIPv6Size){
                UnsafeNclNativeMethods.OSSOCK.ControlDataIPv6 controlData = (UnsafeNclNativeMethods.OSSOCK.ControlDataIPv6)Marshal.PtrToStructure(m_Message->controlBuffer.Pointer, typeof(UnsafeNclNativeMethods.OSSOCK.ControlDataIPv6));
                if (controlData.length != UIntPtr.Zero)
                {
                    address = new IPAddress(controlData.address);
                }
                m_IPPacketInformation = new IPPacketInformation(((address!=null)?address:IPAddress.IPv6None),(int)controlData.index);
            }
            //other
            else{
                m_IPPacketInformation = new IPPacketInformation();
            }
        }


        //
        // This method is called after an asynchronous call is made for the user,
        // it checks and acts accordingly if the IO:
        // 1) completed synchronously.
        // 2) was pended.
        // 3) failed.
        //

        internal void SyncReleaseUnmanagedStructures()
        {
            InitIPPacketInformation();
            ForceReleaseUnmanagedStructures();
        }


        protected override void ForceReleaseUnmanagedStructures() {
            m_flags = m_Message->flags;
            base.ForceReleaseUnmanagedStructures();
        }

        internal override object PostCompletion(int numBytes)
        {
            InitIPPacketInformation();
            if (ErrorCode == 0) {
                if(Logging.On)LogBuffer(numBytes);
            }
            return (int)numBytes;
        }

        void LogBuffer(int size) {
            GlobalLog.Assert(Logging.On, "ReceiveMessageOverlappedAsyncResult#{0}::LogBuffer()|Logging is off!", ValidationHelper.HashString(this));
            Logging.Dump(Logging.Sockets, AsyncObject, "PostCompletion", m_WSABuffer->Pointer, Math.Min(m_WSABuffer->Length, size));
        }

    }; // class OverlappedAsyncResult




} // namespace System.Net.Sockets
