package com.bocloud.ims.service.utils;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.common.utils.ListTool;
import com.googlecode.ipv6.IPv6AddressRange;
import com.googlecode.ipv6.IPv6Network;
import com.googlecode.ipv6.IPv6NetworkMask;

public class IPv6Address extends IPAddress {

	public static final String IPv6Binary = "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	public static final String IPV6_SID_REGEX = "^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$";
	public static final String IPV6_COMPRESS_REGEX = "^((?:[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4})*)?)::((?:([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4})?)$";
	public static final String IPV6_COMPRESS_REGEX_BORDER = "^(::(?:[0-9a-fA-F]{1,4})(?::[0-9a-fA-F]{1,4}){5})|((?:[0-9a-fA-F]{1,4})(?::[0-9a-fA-F]{1,4}){5}::)$";

	@Override
	public boolean validate(String address) {
		if (StringUtils.isEmpty(address)) {
			return false;
		}
		int num = address.split(":").length - 1;
		if (num > 7) {
			return false;
		}
		boolean result = false;
		if (num == 7) {
			result = Pattern.matches(IPV6_SID_REGEX, address.trim());
			if (!result) {
				result = Pattern.matches(IPV6_COMPRESS_REGEX_BORDER, address.trim());
			}
		} else {
			result = Pattern.matches(IPV6_COMPRESS_REGEX, address.trim());
		}
		return result;
	}

	@Override
	public String getDefaultGeteway(String cidr) {
		if (StringUtils.isEmpty(cidr)) {
			return null;
		}
		int index = cidr.indexOf("/");
		if (index == -1) {
			return null;
		}
		// ip地址
		com.googlecode.ipv6.IPv6Address address = com.googlecode.ipv6.IPv6Address.fromString(cidr.substring(0, index));
		IPv6NetworkMask mask = IPv6NetworkMask.fromPrefixLength(Integer.parseInt(cidr.substring(index + 1)));
		IPv6Network iPv6Network = IPv6Network.fromAddressAndMask(address, mask);
		String network = iPv6Network.toLongString();
		String value = network.substring(0, network.indexOf("/"));
		return compress(value.substring(0, value.length() - 1) + "1");
	}

	@Override
	public boolean isInRange(String address, String cidr) {
		if (StringUtils.isEmpty(address) || StringUtils.isEmpty(cidr) || !validate(address)) {
			return false;
		}
		IPv6AddressRange range = getRange(cidr);
		return range.contains(com.googlecode.ipv6.IPv6Address.fromString(address));
	}

	@Override
	public boolean isInRange(List<String> pools, String cidr) {
		if (ListTool.isEmpty(pools) || StringUtils.isEmpty(cidr)) {
			return false;
		}
		for (String pool : pools) {
			String[] startEndIp = pool.split("-");
			boolean result = checkIpMask(startEndIp[0], startEndIp[1], cidr);
			if (!result) {
				return false;
			}
		}
		return true;
	}

	@Override
	public boolean isInPools(String address, List<String> pools) {
		if (ListTool.isEmpty(pools) || StringUtils.isEmpty(address) || !validate(address)) {
			return false;
		}
		com.googlecode.ipv6.IPv6Address currentIp = com.googlecode.ipv6.IPv6Address.fromString(address);
		for (String pool : pools) {
			String[] startEndIp = pool.split("-");
			if (!validate(startEndIp[0])) {
				continue;
			}
			if (!validate(startEndIp[1])) {
				continue;
			}
			com.googlecode.ipv6.IPv6Address startIp = com.googlecode.ipv6.IPv6Address.fromString(startEndIp[0]);
			com.googlecode.ipv6.IPv6Address endIp = com.googlecode.ipv6.IPv6Address.fromString(startEndIp[1]);
			IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(startIp, endIp);
			if (range.contains(currentIp)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean checkIpMask(String startIp, String endIp, String cidr) {
		if (!validate(startIp)) {
			return false;
		}
		if (!validate(endIp)) {
			return false;
		}
		IPv6AddressRange range = getRange(cidr);
		com.googlecode.ipv6.IPv6Address first = com.googlecode.ipv6.IPv6Address.fromString(startIp);
		com.googlecode.ipv6.IPv6Address last = com.googlecode.ipv6.IPv6Address.fromString(endIp);
		if (range.contains(first) && range.contains(last)) {
			return true;
		}
		return false;
	}

	@Override
	public String getPools(String gateway, String cidr) {
		if (StringUtils.isEmpty(cidr)) {
			return null;
		}
		IPv6AddressRange range = getRange(cidr);
		BigInteger startIp = new BigInteger(ipv6ToBytes(range.getFirst().toLongString())).add(new BigInteger("1"));
		BigInteger endIp = new BigInteger(ipv6ToBytes(range.getLast().toLongString())).subtract(new BigInteger("1"));
		BigInteger gatewayIp = new BigInteger(ipv6ToBytes(gateway));
		List<String> pools = new ArrayList<>();
		if (startIp.equals(gatewayIp)) {
			pools.add(integerToIPv6(startIp.add(new BigInteger("1"))) + "-" + integerToIPv6(endIp));
		} else if (endIp.equals(gatewayIp)) {
			pools.add(integerToIPv6(startIp) + "-" + integerToIPv6(endIp.subtract(new BigInteger("1"))));
		} else if (gatewayIp.compareTo(startIp) > 0 && gatewayIp.compareTo(endIp) < 0) {
			pools.add(integerToIPv6(startIp) + "-" + integerToIPv6(gatewayIp.subtract(new BigInteger("1"))));
			pools.add(integerToIPv6(gatewayIp.add(new BigInteger("1"))) + "-" + integerToIPv6(endIp));
		} else {
			pools.add(integerToIPv6(startIp) + "-" + integerToIPv6(endIp));
		}
		return JSONObject.toJSONString(pools);
	}

	@Override
	public String getMask(String prefix) {
		if (StringUtils.isEmpty(prefix)) {
			return null;
		}
		int index = prefix.indexOf("/");
		if (index > 0) {
			prefix = prefix.substring(index + 1);
		}
		int maskNum = Integer.parseInt(prefix);
		String mask = IPv6Binary.substring(0, maskNum) + IPv6Binary.substring(maskNum).replace("1", "0");
		return compress(binaryToIPv6(mask));
	}

	@Override
	public int getMaskLength(String mask) {
		int index = getBinaryString(mask).indexOf("0");
		return index < 0 ? 128 : index;
	}

	@Override
	public BigInteger count(String cidr) {
		int index = cidr.indexOf("/");
		String gateway = cidr.substring(0, index);
		String range = getPools(gateway, cidr);
		BigInteger integer = new BigInteger("0");
		if (!StringUtils.isEmpty(range)) {
			List<String> ipPools = JSONArray.parseArray(range, String.class);
			for (String ipPool : ipPools) {
				String[] ipList = ipPool.split("-");
				BigInteger ipstart = new BigInteger(ipv6ToBytes(ipList[0]));// 获取起始IP的开始位置
				BigInteger ipend = new BigInteger(ipv6ToBytes(ipList[1]));// 获取终止IP的结束位置
				BigInteger count = ipend.subtract(ipstart).add(new BigInteger("1"));
				integer = integer.add(count);
			}
		}
		return integer;
	}

	public static void main(String[] args) {
		BigInteger count = new IPv6Address().count("ffe0:ff::01/125");
		System.out.println(count.add(new BigInteger("2")));
	}

	/**
	 * ipv6转为byte格式
	 * 
	 * @param address
	 * @return
	 */
	public byte[] ipv6ToBytes(String address) {
		byte[] bytes = new byte[17];
		bytes[0] = 0;
		int index = 16;
		boolean hasIpv4 = false;
		if (address.startsWith(":")) {
			address = address.substring(1);
		}
		String[] segments = address.split(":");
		for (int i = segments.length - 1; i > -1; i--) {
			if (segments[i].contains(".")) {// 当ipv6段最后是ipv4
				byte[] values = ipv4ToBytes(segments[i]);
				bytes[index--] = values[3];
				bytes[index--] = values[2];
				bytes[index--] = values[1];
				bytes[index--] = values[0];
				hasIpv4 = true;
			} else if ("".equals(segments[i])) { // ipv6段为空时，补全0
				int zeros = 8 - (segments.length - 1 + (hasIpv4 ? 1 : 0));
				while (zeros > 0) {
					bytes[index--] = 0;
					bytes[index--] = 0;
					zeros--;
				}
			} else {
				int value = Integer.parseInt(segments[i], 16);
				bytes[index--] = (byte) value;
				bytes[index--] = (byte) (value >> 8);
			}
		}
		return bytes;
	}

	/**
	 * ipv4转为byte格式
	 * 
	 * @param address
	 * @return
	 */
	private byte[] ipv4ToBytes(String address) {
		byte[] bytes = new byte[4];
		// 先找到IP地址字符串中.的位置
		int position1 = address.indexOf(".");
		int position2 = address.indexOf(".", position1 + 1);
		int position3 = address.indexOf(".", position2 + 1);
		// 将每个段转换为byte类型
		bytes[0] = (byte) Integer.parseInt(address.substring(0, position1));
		bytes[1] = (byte) Integer.parseInt(address.substring(position1 + 1, position2));
		bytes[2] = (byte) Integer.parseInt(address.substring(position2 + 1, position3));
		bytes[3] = (byte) Integer.parseInt(address.substring(position3 + 1));
		return bytes;
	}

	/**
	 * Big整型转为IPv6
	 * 
	 * @param integer
	 * @return
	 */
	private String integerToIPv6(BigInteger integer) {
		BigInteger ff = BigInteger.valueOf(0xffff);
		String ipv6 = "";
		for (int i = 0; i < 8; i++) {
			ipv6 = integer.and(ff).toString(16) + ":" + ipv6;
			integer = integer.shiftRight(16);
		}
		ipv6 = ipv6.substring(0, ipv6.length() - 1);
		return ipv6.replaceFirst("(^|:)(0+(:|$)){2,8}", "::");
	}

	/**
	 * 通过cidr得到IPv6范围
	 * 
	 * @param cidr
	 * @return
	 */
	private IPv6AddressRange getRange(String cidr) {
		int index = cidr.indexOf("/");
		String ip = cidr.substring(0, index);
		int prefix = Integer.parseInt(cidr.substring(index + 1));
		com.googlecode.ipv6.IPv6Address address = com.googlecode.ipv6.IPv6Address.fromString(ip);
		IPv6NetworkMask mask = IPv6NetworkMask.fromPrefixLength(Integer.parseInt(cidr.substring(index + 1)));
		IPv6Network iPv6Network = IPv6Network.fromAddressAndMask(address, mask);
		String network = iPv6Network.toLongString();
		String startIp = network.substring(0, network.indexOf("/"));
		ip = hexToIPv6(getHexString(ip));
		String binaryIp = getBinaryString(ip);
		String binaryEndIp = binaryIp.substring(0, prefix) + IPv6Binary.substring(prefix);
		String endIp = binaryToIPv6(binaryEndIp);
		return IPv6AddressRange.fromFirstAndLast(com.googlecode.ipv6.IPv6Address.fromString(startIp),
				com.googlecode.ipv6.IPv6Address.fromString(endIp));
	}

	/**
	 * 二进制转为IPv6
	 * 
	 * @param binary
	 * @return
	 */
	private String binaryToIPv6(String binary) {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < binary.length(); i += 4) {
			int value = 0;
			for (int j = 0; j < 4; j++) {
				value += Integer.parseInt(binary.substring(i + j, i + j + 1)) << (4 - j - 1);
			}
			buffer.append(Integer.toHexString(value));
		}
		return hexToIPv6(buffer.toString());
	}

	/**
	 * 十六进制转为IPv6
	 * 
	 * @param hex
	 * @return
	 */
	private String hexToIPv6(String hex) {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < 8; i++) {
			buffer.append(hex.substring(4 * i, 4 * i + 4));
			if (i < 7) {
				buffer.append(":");
			}
		}
		return buffer.toString();
	}

	/**
	 * 获取IPv6二进制字符
	 * 
	 * @param address
	 * @return
	 */
	private String getBinaryString(String address) {
		address = getHexString(address);
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < address.length(); i++) {
			String binary = Integer.toBinaryString(Integer.parseInt(address.substring(i, i + 1), 16));
			while (binary.length() < 4) {
				binary = "0" + binary;
			}
			buffer.append(binary);
		}
		return buffer.toString();
	}

	/**
	 * ipv6解析转换十六进制
	 * 
	 * @param address
	 * @return
	 */
	public String getHexString(String address) {
		String key = "";
		boolean hasDot = address.contains(".");
		boolean hasDColon = address.contains("::");
		address = address.replace(".", "&");
		address = address.replace(":", "&");
		if (hasDColon) {
			if (hasDot) {
				String[] arr = address.split("&");
				String[] arrParams = new String[10];
				int indexFlag = 0;
				int pFlag = 0;
				if ("".equals(arr[0])) {
					for (int j = 0; j < (10 - arr.length + 2); j++) {
						arrParams[indexFlag++] = "0000";
					}
					for (int i = 2; i < arr.length; i++) {
						arrParams[indexFlag++] = arr[i];
					}
				} else {
					for (int i = 0; i < arr.length; i++) {
						if ("".equals(arr[i])) {
							for (int j = 0; j < (10 - arr.length + 1); j++) {
								arrParams[indexFlag++] = "0000";
							}
						} else {
							arrParams[indexFlag++] = arr[i];
						}
					}
				}
				// 2、ip(去除ipv4的部分)中采用4位十六进制数表示一组数，将不足4位的十六进制数补0
				for (int i = 0; i < 8 - 2; i++) {
					for (int k = 0; k < (4 - arrParams[i].length()); k++) {
						key += "0";
					}
					key += arrParams[i];
					pFlag++;
				}
				for (int j = arr.length - 4; j < arrParams.length; j++) {
					arrParams[pFlag] = Integer.toHexString(Integer.parseInt(arrParams[pFlag]));
					for (int k = 0; k < (2 - arrParams[pFlag].length()); k++) {
						key += "0";
					}
					key += arrParams[pFlag];
					pFlag++;
				}
			} else {
				boolean isZeroZero = false;
				if (address.endsWith("&&")) {
					address = address.replace("&&", "&-1&-1");
					isZeroZero = true;
				}
				String[] arr = address.split("&");
				for (int z = 0; z < arr.length; z++) {
					if ("-1".equals(arr[z])) {
						arr[z] = "";
					}
				}
				String[] arrParams = new String[8];
				int indexFlag = 0;
				if ("".equals(arr[0])) {
					for (int j = 0; j < (8 - (arr.length - 2)); j++) {
						arrParams[indexFlag++] = "0000";
					}
					for (int i = 2; i < arr.length; i++) {
						arrParams[indexFlag++] = arr[i];
					}
				} else {
					if (isZeroZero) {// 结尾为::的单独处理了
						for (int i = 0; i < arr.length - 1; i++) {
							if ("".equals(arr[i])) {
								for (int j = 0; j < (8 - arr.length + 2); j++) {
									arrParams[indexFlag++] = "0000";
								}
							} else {
								arrParams[indexFlag++] = arr[i];
							}
						}
					} else {
						for (int i = 0; i < arr.length; i++) {
							if ("".equals(arr[i])) {
								for (int j = 0; j < (8 - arr.length + 1); j++) {
									arrParams[indexFlag++] = "0000";
								}
							} else {
								arrParams[indexFlag++] = arr[i];
							}
						}
					}
				}
				for (int i = 0; i < 8; i++) {
					for (int k = 0; k < (4 - arrParams[i].length()); k++) {
						key += "0";
					}
					key += arrParams[i];
				}
			}
		} else {
			String[] arr = address.split("&");
			if (hasDot) {
				for (int i = 0; i < 8 - 2; i++) {
					for (int k = 0; k < (4 - arr[i].length()); k++) {
						key += "0";
					}
					key += arr[i];
				}

				for (int j = arr.length - 4; j < arr.length; j++) {
					arr[j] = Integer.toHexString(Integer.parseInt(arr[j]));
					for (int k = 0; k < (2 - arr[j].length()); k++) {
						key += "0";
					}
					key += arr[j];
				}
			} else {
				for (int i = 0; i < 8; i++) {
					for (int k = 0; k < (4 - arr[i].length()); k++) {
						key += "0";
					}
					key += arr[i];
				}
			}
		}
		return key;
	}

	/**
	 * ipv6压缩
	 * 
	 * @param address
	 * @return
	 */
	public String compress(String address) {
		address = address.replace(" ", "");
		BigInteger integer = new BigInteger(ipv6ToBytes(address));
		return integerToIPv6(integer);
	}

	@Override
	public BigInteger ipToInteger(String address) {
		return new BigInteger(ipv6ToBytes(address));
	}

	@Override
	public String integerToIp(BigInteger integer) {
		return integerToIPv6(integer);
	}

}
