/*
 * Copyright (c) 2002-2012 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 */

package com.jscape.util.net;


import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Alex Usun
 */
public class NetTools {

    private static final Pattern CIDR_PATTERN = Pattern.compile("^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(3[0-2]|[1-2]?\\d)$");

    public static boolean sameHost(String address1, String address2) {
        try {
            return InetAddress.getByName(address1).equals(InetAddress.getByName(address2));
        } catch (Throwable e) {
            return address1 != null && address1.equalsIgnoreCase(address2);
        }
    }

    public static boolean sameBinding(String address1, int port1, String address2, int port2) {
        return port1 == port2 && sameHost(address1, address2);
    }

    public static boolean sameBinding(TransportAddress address1, TransportAddress address2) {
        return address1.getPort() == address2.getPort() && sameHost(address1.getHost(), address2.getHost());
    }

    public static InetAddress increment(InetAddress address)
            throws UnknownHostException {
        byte[] rawAddress = address.getAddress();

        int index = rawAddress.length - 1;
        while (index >= 0 && rawAddress[index] == (byte) 0xFF) {
            rawAddress[index] = 0;
            index--;
        }

        if (index >= 0) {
            rawAddress[index]++;
            return InetAddress.getByAddress(rawAddress);
        }

        throw new UnknownHostException();
    }

    public static InetAddress[] getByCIDR(String data)
            throws UnknownHostException {
        Matcher matcher = CIDR_PATTERN.matcher(data);

        if (matcher.matches()) {
            // CIDR notation found x.x.x.x/numOfBits
            int address = Integer.parseInt(matcher.group(4)) & 0xFF;
            address |= (Integer.parseInt(matcher.group(3)) & 0xFF) << 8;
            address |= (Integer.parseInt(matcher.group(2)) & 0xFF) << 16;
            address |= (Integer.parseInt(matcher.group(1)) & 0xFF) << 24;

            int numOfBits = Integer.parseInt(matcher.group(5));
            int mask = 0xFFFFFFFF << (32 - numOfBits);

            if ((address & mask) != address) {
                throw new IllegalArgumentException("Logical AND of base IP and mask should produce base IP");
            }

            int addressCount = 1 << (32 - numOfBits);
            InetAddress[] addresses = new InetAddress[addressCount];

            for (int i = 0; i < addressCount; ++address, ++i) {
                addresses[i] = getByAddress(address);
            }

            return addresses;
        }

        // try to recognize IPv4
        return new InetAddress[]{ InetAddress.getByName(data) };
    }

    public static InetAddress getByAddress(int address)
            throws UnknownHostException {
        byte[] addr = new byte[4];
        addr[0] = (byte) ((address >>> 24) & 0xFF);
        addr[1] = (byte) ((address >>> 16) & 0xFF);
        addr[2] = (byte) ((address >>> 8) & 0xFF);
        addr[3] = (byte) (address & 0xFF);
        return InetAddress.getByAddress(addr);
    }

    public static String ipFor(String address) {
        try {
            return InetAddress.getByName(address).getHostAddress();
        } catch (Throwable e) {
            return address;
        }
    }

    private NetTools() {
    }
}
