////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  Note:
//    This file was ported from the Coriolis codebase to Singularity.
//

namespace System.Net
{

    using System;
    using System.Net.Sockets;
    using System.Text;
    using System.Globalization;

    // a little perf app measured these times when comparing the internal
    // buffer implemented as a managed byte[] or unmanaged memory IntPtr
    // that's why we use byte[]
    // byte[] total ms:19656
    // IntPtr total ms:25671

    public class SocketAddress {

        internal int m_Size;
        internal byte[]! m_Buffer;

        private const int WriteableOffset = 2;
        private const int MaxSize = 32; // IrDA requires 32 bytes
        private bool m_changed = true;
        private int m_hash;

        public AddressFamily Family {
            get {
                int family;
#if BIGENDIAN
                family = ((int)m_Buffer[0]<<8) | m_Buffer[1];
#else
                family = m_Buffer[0] | ((int)m_Buffer[1]<<8);
#endif
                return (AddressFamily)family;
            }
        }
        //
        // Size of this SocketAddress
        //
        public int Size {
            get {
                return m_Size;
            }
        }

        //
        // access to unmanaged serialized data. this doesn't
        // allow access to the first 2 bytes of unmanaged memory
        // that are supposed to contain the address family which
        // is readonly.
        //
        public byte this[int offset] {
            get {
                //
                // access
                //
                if (offset < 0 || offset >= Size) {
                    throw new IndexOutOfRangeException();
                }
                return m_Buffer[offset];
            }
            set {
                if (offset < 0 || offset >= Size) {
                    throw new IndexOutOfRangeException();
                }
                if (m_Buffer[offset] != value) {
                    m_changed = true;
                }
                m_Buffer[offset] = value;
            }
        }

        public SocketAddress(AddressFamily family) : this(family, MaxSize) {
        }

        public SocketAddress(AddressFamily family, int size) {
            if (size < WriteableOffset) {
                //
                // it doesn't make sense to create a socket address with less than
                // 2 bytes, that's where we store the address family.
                //
                throw new ArgumentOutOfRangeException("size");
            }
            m_Size = size;
            byte[]! buf = m_Buffer = new byte[(size/IntPtr.Size+2)*IntPtr.Size];//sizeof DWORD

#if BIGENDIAN
            buf[0] = unchecked((byte)((int)family>>8));
            buf[1] = unchecked((byte)((int)family   ));
#else
            buf[0] = unchecked((byte)((int)family   ));
            buf[1] = unchecked((byte)((int)family>>8));
#endif
        }

        //
        // Can be called after the above method did work
        //
        internal int GetAddressSizeOffset()
        {
            return m_Buffer.Length-IntPtr.Size;
        }

        public override bool Equals(object comparand) {
            SocketAddress castedComparand = comparand as SocketAddress;
            if (castedComparand == null || this.Size != castedComparand.Size) {
                return false;
            }
            for (int i = 0; i < this.Size; i++) {
                if (this[i] != castedComparand[i]) {
                    return false;
                }
            }
            return true;
        }

        public override int GetHashCode() {
            if (m_changed) {
                m_changed = false;
                m_hash = 0;

                int i;
                int size = Size & ~3;

                for (i = 0; i < size; i += 4) {
                    m_hash ^= (int)m_Buffer[i]
                            | ((int)m_Buffer[i+1] << 8)
                            | ((int)m_Buffer[i+2] << 16)
                            | ((int)m_Buffer[i+3] << 24);
                }
                if ((Size & 3) != 0) {

                    int remnant = 0;
                    int shift = 0;

                    for (; i < Size; ++i) {
                        remnant |= ((int)m_Buffer[i]) << shift;
                        shift += 8;
                    }
                    m_hash ^= remnant;
                }
            }
            return m_hash;
        }

        public override string! ToString() {
            StringBuilder bytes = new StringBuilder();
            for (int i = WriteableOffset; i < this.Size; i++) {
                if (i > WriteableOffset) {
                    bytes.Append(",");
                }
                bytes.Append(this[i].ToString());
            }
            return Family.ToString() + ":" + Size.ToString() + ":{" + bytes.ToString() + "}";
        }

    } // class SocketAddress


} // namespace System.Net
