/*
 * $Id: IPv4Address.java,v 1.3 2006/01/28 03:37:07 rampil Exp $
 * Copyright (c) 2005 LOGICAL-PARADOX.ORG
 */
package org.logical_paradox.common.net;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.StringTokenizer;

/**
 * IPv4Address
 * ネットマスクつきIPv4アドレスを表現するクラス
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.3 $
 */
public class IPv4Address extends IPAddress {
	/**
	 * コンストラクタ
	 * @param ip IPアドレス
	 * @param nm ネットマスク
	 */
	public IPv4Address(InetAddress ip, InetAddress nm) {
		super(ip, nm);
	}
	/**
	 * コンストラクタ
	 * @param ipm IPアドレス/ネットマスクペア
	 */
	public IPv4Address(IPv4MaskPair ipm) {
		super(ipm.getIPAddress(), ipm.getNetmask());
	}
	/**
	 * 指定された文字列"xxx.xxx.xxx.xxx/nn"から，IPアドレスとネットマスクのセットを生成する
	 * @param addr IPアドレス+ネットマスクビット数"xxx.xxx.xxx.xxx/nn"
	 * @return IPv4
	 * @throws IllegalArgumentException
	 */
	public static IPv4MaskPair getIPv4MaskPair(String addr) throws IllegalArgumentException {
		StringTokenizer st = new StringTokenizer(addr.trim(), "/");
		String partIPAddress = null;
		String partNetMaskBitLength = null;

		boolean rc = true;
		if(
			st.hasMoreTokens() == false ||
			(partIPAddress = st.nextToken()) == null ||
			partIPAddress.trim().length() == 0
		) {
			// IPアドレス部分の取得に失敗
			rc = false;
		} else if(
			st.hasMoreTokens() == false ||
			(partNetMaskBitLength = st.nextToken()) == null ||
			partNetMaskBitLength.trim().length() == 0
		) {
			// ネットマスクビット数の取得に失敗
			rc = false;
		} else if(st.hasMoreTokens() == true)  {
			// 意味なく長い場合
			rc = false;
		}

		int netMaskBit = 0;
		InetAddress[] ips = new InetAddress[2];

		if(rc == true) {
			try {
				/*
				 * 211.0/12などの形式を正しいものに変換する処理
				 */
				StringTokenizer ipaTokenizer = new StringTokenizer(partIPAddress + ".0.0.0", ".");
				StringBuffer fqIPAddress = new StringBuffer();
				for(int i = 0; i < 4; i++) {
					if(ipaTokenizer.hasMoreTokens() == false) {
						throw new IllegalArgumentException("IPアドレスがおかしい" + partIPAddress);
					}

					if(i > 0) {
	 					fqIPAddress.append('.');
					}
 					fqIPAddress.append(ipaTokenizer.nextToken());
				}

				ips[0] = InetAddress.getByName(fqIPAddress.toString());
				netMaskBit = Integer.parseInt(partNetMaskBitLength);
				ips[1] = getNetmaskAddress(netMaskBit);
			} catch(UnknownHostException uhe) {
				rc = false;
			} catch(NumberFormatException nfe) {
				rc = false;
			}
		}

		if(rc == false) {
			throw new IllegalArgumentException("指定されたIPアドレスのフォーマットがおかしい: '" + addr + "'");
		}

		return new IPv4MaskPair(ips[0], ips[1]);
	}

	public static InetAddress getNetmaskAddress(int bitLength) throws UnknownHostException {
		int netmaskAsInt = ((1 << (32-bitLength)) -1) ^ 0xffffffff;
		byte[] netmaskBits = new byte[4];

		for(int i = 0; i < 4; i++) {
			netmaskBits[3-i] = (byte)((netmaskAsInt >> (8*i)) & 0xff);
		}

		return InetAddress.getByAddress(netmaskBits);
	}
	@Override
	public boolean includes(InetAddress ip) {
		byte[] ipBytes = ip.getAddress();
		byte[] thisIPBytes = getIPAddress().getAddress();
		byte[] netmaskBytes = getNetmask().getAddress();

		if(ipBytes.length != thisIPBytes.length ||
			ipBytes.length != netmaskBytes.length
		) {
			return false;
		}

		// 指定されたIPアドレスに対して，ネットマスクを適用する
		for(int i = 0; i < ipBytes.length; i++) {
			ipBytes[i] &= netmaskBytes[i];
			thisIPBytes[i] &= netmaskBytes[i];

			if(ipBytes[i] != thisIPBytes[i]) {
				return false;
			}
		}

		return true;
	}
	@Override
	public boolean includes(String ip) {
		boolean rc = false;
		try {
			rc = includes(InetAddress.getByName(ip));
		} catch(UnknownHostException e) {
			rc = false;
		}
		return rc;
	}
}

// end of IPv4Address.java
