///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//

using System;

#if !SINGULARITY
using System.Net;
using System.Net.Sockets;
#endif

namespace System.Net.IP
{
    public sealed class IncompatibleAddressFamiliesException : SystemException
    {
        public IncompatibleAddressFamiliesException()
            : base("Attempting operation on addresses from incompatible "
                   + "families.")
        {
        }
    }

    public sealed class IPAddressBitwise
    {
        /// <summary>
        /// Perform the bitwise AND operation on two IPAddress
        /// objects and return a new IPAddress containing the
        /// result.
        ///
        /// <exception cref="IncompatibleAddressFamiliesException"></exception>
        /// </summary>
        public static IPAddress And(IPAddress l, IPAddress r)
        {
            if (l.AddressFamily != r.AddressFamily)
                throw new IncompatibleAddressFamiliesException();

            byte [] bl = l.GetAddressBytes();
            byte [] br = r.GetAddressBytes();

            if (l.AddressFamily == AddressFamily.InterNetwork) {
                bl[0] &= br[0];
                bl[1] &= br[1];
                bl[2] &= br[2];
                bl[3] &= br[3];
                int tmp = (bl[0] << 24) | (bl[1] << 16) | (bl[2] << 8) | bl[3];
                return new IPAddress(((long)tmp) & 0xffffffff);
            }
            bl[0] &= br[0];
            bl[1] &= br[1];
            bl[2] &= br[2];
            bl[3] &= br[3];
            bl[4] &= br[4];
            bl[5] &= br[5];
            bl[6] &= br[6];
            bl[7] &= br[7];
            bl[8] &= br[8];
            bl[9] &= br[9];
            bl[10] &= br[10];
            bl[11] &= br[11];
            bl[12] &= br[12];
            bl[13] &= br[13];
            bl[14] &= br[14];
            bl[15] &= br[15];
            return new IPAddress(bl);
        }

        /// <summary>
        /// Perform the bitwise OR operation on two
        /// IPAddress objects and return a new IPAddress
        /// containing the result.
        ///
        /// <exception cref="IncompatibleAddressFamiliesException"></exception>
        /// </summary>
        public static IPAddress Or(IPAddress l, IPAddress r)
        {
            if (l.AddressFamily != r.AddressFamily)
                throw new IncompatibleAddressFamiliesException();

            byte [] bl = l.GetAddressBytes();
            byte [] br = r.GetAddressBytes();

            if (l.AddressFamily == AddressFamily.InterNetwork) {
                bl[0] |= br[0];
                bl[1] |= br[1];
                bl[2] |= br[2];
                bl[3] |= br[3];
                int tmp = (bl[0] << 24) | (bl[1] << 16) | (bl[2] << 8) | bl[3];
                return new IPAddress(((long)tmp) & 0xffffffff);
            }

            bl[0] |= br[0];
            bl[1] |= br[1];
            bl[2] |= br[2];
            bl[3] |= br[3];
            bl[4] |= br[4];
            bl[5] |= br[5];
            bl[6] |= br[6];
            bl[7] |= br[7];
            bl[8] |= br[8];
            bl[9] |= br[9];
            bl[10] |= br[10];
            bl[11] |= br[11];
            bl[12] |= br[12];
            bl[13] |= br[13];
            bl[14] |= br[14];
            bl[15] |= br[15];
            return new IPAddress(bl);
        }

        /// <summary>
        /// Perform the bitwise eXclusive-OR operation on two
        /// IPAddress objects and return a new IPAddress
        /// containing the result.
        ///
        /// <exception cref="IncompatibleAddressFamiliesException"></exception>
        /// </summary>
        public static IPAddress Xor(IPAddress l, IPAddress r)
        {
            if (l.AddressFamily != r.AddressFamily)
                throw new IncompatibleAddressFamiliesException();

            byte [] bl = l.GetAddressBytes();
            byte [] br = r.GetAddressBytes();

            if (l.AddressFamily == AddressFamily.InterNetwork) {
                bl[0] ^= br[0];
                bl[1] ^= br[1];
                bl[2] ^= br[2];
                bl[3] ^= br[3];
                int tmp = (bl[0] << 24) | (bl[1] << 16) | (bl[2] << 8) | bl[3];
                return new IPAddress(((long)tmp) & 0xffffffff);
            }

            bl[0] ^= br[0];
            bl[1] ^= br[1];
            bl[2] ^= br[2];
            bl[3] ^= br[3];
            bl[4] ^= br[4];
            bl[5] ^= br[5];
            bl[6] ^= br[6];
            bl[7] ^= br[7];
            bl[8] ^= br[8];
            bl[9] ^= br[9];
            bl[10] ^= br[10];
            bl[11] ^= br[11];
            bl[12] ^= br[12];
            bl[13] ^= br[13];
            bl[14] ^= br[14];
            bl[15] ^= br[15];
            return new IPAddress(bl);
        }

        /// <summary>
        /// Perform the bitwise NOT operation on an IPAddress
        /// and return a new IPAddress containing the result.
        ///
        /// <exception cref="IncompatibleAddressFamiliesException"></exception>
        /// </summary>
        public static IPAddress Not(IPAddress l)
        {
            byte [] bl = l.GetAddressBytes();

            if (l.AddressFamily == AddressFamily.InterNetwork) {
                bl[0] = (byte) ~bl[0];
                bl[1] = (byte) ~bl[1];
                bl[2] = (byte) ~bl[2];
                bl[3] = (byte) ~bl[3];
                int tmp = (bl[0] << 24) | (bl[1] << 16) | (bl[2] << 8) | bl[3];
                return new IPAddress(((long)tmp) & 0xffffffff);
            }

            bl[0] = (byte) ~bl[0];
            bl[1] = (byte) ~bl[1];
            bl[2] = (byte) ~bl[2];
            bl[3] = (byte) ~bl[3];
            bl[4] = (byte) ~bl[4];
            bl[5] = (byte) ~bl[5];
            bl[6] = (byte) ~bl[6];
            bl[7] = (byte) ~bl[7];
            bl[8] = (byte) ~bl[8];
            bl[9] = (byte) ~bl[9];
            bl[10] = (byte) ~bl[10];
            bl[11] = (byte) ~bl[11];
            bl[12] = (byte) ~bl[12];
            bl[13] = (byte) ~bl[13];
            bl[14] = (byte) ~bl[14];
            bl[15] = (byte) ~bl[15];
            return new IPAddress(bl);
        }

        static IPAddressBitwise()
        {
        }

        private IPAddressBitwise()
        {
        }
    }

    public sealed class IPAddressCompare
    {
        public static bool LessThan(IPAddress l, IPAddress r)
        {
            if (l.AddressFamily != r.AddressFamily)
                throw new IncompatibleAddressFamiliesException();

            byte [] bl = l.GetAddressBytes();
            byte [] br = r.GetAddressBytes();

            uint ul = 0;
            uint ur = 0;

            for (int i = 0; i < bl.Length; i += 4) {
                ul = (uint)((bl[i + 0] << 24) | (bl[i + 1] << 16) |
                            (bl[i + 2] <<  8) | (bl[i + 3]));
                ur = (uint)((br[i + 0] << 24) | (br[i + 1] << 16) |
                            (br[i + 2] <<  8) | (br[i + 3]));
                if (ul > ur)
                    return false;
            }
            return ul < ur;
        }

        public static bool LessThanEqualTo(IPAddress l, IPAddress r)
        {
            if (l.AddressFamily != r.AddressFamily)
                throw new IncompatibleAddressFamiliesException();

            byte [] bl = l.GetAddressBytes();
            byte [] br = r.GetAddressBytes();

            uint ul = 0;
            uint ur = 0;

            for (int i = 0; i < bl.Length; i += 4) {
                ul = (uint)((bl[i + 0] << 24) | (bl[i + 1] << 16) |
                            (bl[i + 2] <<  8) | (bl[i + 3]));
                ur = (uint)((br[i + 0] << 24) | (br[i + 1] << 16) |
                            (br[i + 2] <<  8) | (br[i + 3]));
                if (ul > ur)
                    return false;
            }
            return ul <= ur;
        }

        public static bool GreaterThan(IPAddress l, IPAddress r)
        {
            return !LessThanEqualTo(l, r);
        }

        public static bool GreaterThanEqualTo(IPAddress l, IPAddress r)
        {
            return !LessThan(l, r);
        }

        private IPAddressCompare()
        {
        }
    }

#if TEST_IPADDRESS_UTILS
    public class Test
    {
        static void Output(string format, params object[] args)
        {
            Console.Write(format, args);
        }

        static void Main()
        {
            IPAddress a = IPAddress.Parse("10.0.0.1");
            IPAddress b = IPAddress.Parse("192.168.0.1");
            IPAddress c = IPAddress.Parse("170.170.170.170");
            IPAddress d = IPAddress.Parse("255.255.255.255");
            IPAddress e = IPAddress.Parse("85.85.85.85");

            IPAddress x = new IPAddress(IPAddress.IPv6Any.GetAddressBytes());

            Output("Expect: a < b  {0}\n",
                   IPAddressCompare.LessThan(a, b));
            Output("Expect: a <= a {0}\n",
                   IPAddressCompare.LessThanEqualTo(a, a));

            Output("c AND d == d {0}\n", IPAddressBitwise.And(c, d).Equals(c));
            Output("c AND e == d {0}\n", IPAddressBitwise.Or(c, e).Equals(d));
            Output("c XOR d == e {0}\n", IPAddressBitwise.Xor(c, e).Equals(d));
            Output("c XOR d == 0 {0}\n", IPAddressBitwise.Xor(c, c).Equals(IPAddress.Any));
            Output("NOT c == e {0}\n", IPAddressBitwise.Not(c).Equals(e));
        }
    }
#endif
} // namespace System.Net.IP
