package com.bocloud.cmp.provider.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;

public class IPv4Address extends IPAddress {
    public static final String IPV4_REGEX = "((\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})";
    public static final String DEFAULT_NETMASK = "255.255.255.255";

    @Override
    public boolean validate(String address) {
        if (StringUtils.isEmpty(address)) {
            return false;
        }
        return Pattern.matches(IPV4_REGEX, address.trim());
    }

    @Override
    public String getDefaultGeteway(String cidr) {
        if (cidr == null) {
            return "";
        }
        int index = cidr.indexOf("/");
        // ip地址
        String ip = cidr.substring(0, index);
        // 子网掩码
        String maskip = getMask(cidr);
        long[] ipList = new long[4];
        long[] maskList = new long[4];
        long[] temp = new long[4];
        ipList = split(ip);
        maskList = split(maskip);
        // 将两个二进制数逻辑与（AND）运算后得出的结果即为网络部分
        for (int i = 0; i < 4; i++) {
            temp[i] = ipList[i] & maskList[i];
        }
        return String.valueOf(temp[0]) + "." + String.valueOf(temp[1]) + "." + String.valueOf(temp[2]) + "." + "1";
    }

    /**
     * IP拆到数组里
     *
     * @param address
     * @return
     */
    private long[] split(String address) {
        long[] ip = new long[4];
        if (null == address) {
            return ip;
        }
        // 先找到IP地址字符串中.的位置
        int position1 = address.indexOf(".");
        int position2 = address.indexOf(".", position1 + 1);
        int position3 = address.indexOf(".", position2 + 1);
        // 将每个.之间的字符串转换成整型
        ip[0] = Long.parseLong(address.substring(0, position1));
        ip[1] = Long.parseLong(address.substring(position1 + 1, position2));
        ip[2] = Long.parseLong(address.substring(position2 + 1, position3));
        ip[3] = Long.parseLong(address.substring(position3 + 1));
        return ip;
    }

    private long ipToLong(String address) {
        long[] ip = split(address);
        return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
    }

    /**
     * 十进制的ip格式 转为32位2进制
     *
     * @param ipString
     * @return
     */
    private String ipToBinary(String ipString) {
        long[] ip = split(ipString);
        return (binary2EigthLength(Long.toBinaryString(ip[0]).toString()))
            + (binary2EigthLength(Long.toBinaryString(ip[1]).toString()))
            + (binary2EigthLength(Long.toBinaryString(ip[2]).toString()))
            + (binary2EigthLength(Long.toBinaryString(ip[3]).toString()));
    }

    /**
     * 二进制补满8位
     *
     * @param value
     * @return
     */
    private String binary2EigthLength(String value) {
        if (value == null) {
            return "00000000";
        }
        if (value.length() == 8) {
            return value;
        }
        int length = value.length();
        String zero = "";
        for (int i = 0; i < 8 - length; i++) {
            zero += "0";
        }
        return zero + value;
    }

    /**
     * 32的二进制字符串 转为十进制的ip格式
     *
     * @param ipString
     * @return
     */
    private String binaryToIp(String ipString) {
        return Integer.parseInt(ipString.substring(0, 8), 2) + "." + Integer.parseInt(ipString.substring(8, 16), 2)
            + "." + Integer.parseInt(ipString.substring(16, 24), 2) + "."
            + Integer.parseInt(ipString.substring(24), 2);
    }

    /**
     * @param maskLength
     * @return
     */
    private long getMaskLong(int maskLength) {
        int subLength = (32 - maskLength);
        long r = 0;
        for (int i = 0; i < subLength; i++) {
            r += (1 << i);
        }
        return ~r;
    }

    /**
     * 将十进制整数形式转换成127.0.0.1形式的ip地址
     *
     * @param longIpOrMask
     * @return
     */
    public String longToIp(long value) {
        StringBuffer sb = new StringBuffer("");
        // 直接右移24位
        sb.append(String.valueOf((value >>> 24)));
        sb.append(".");
        // 将高8位置0，然后右移16位
        sb.append(String.valueOf((value & 0x00FFFFFF) >>> 16));
        sb.append(".");
        // 将高16位置0，然后右移8位
        sb.append(String.valueOf((value & 0x0000FFFF) >>> 8));
        sb.append(".");
        // 将高24位置0
        sb.append(String.valueOf((value & 0x000000FF)));
        return sb.toString();
    }

    @Override
    public boolean isInRange(String address, String cidr) {
        if (StringUtils.isEmpty(address) || StringUtils.isEmpty(cidr) || !validate(address)) {
            return false;
        }
        String[] ips = address.split("\\.");
        int ipAddr = (Integer.parseInt(ips[0]) << 24) | (Integer.parseInt(ips[1]) << 16)
            | (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]);
        int type = Integer.parseInt(cidr.replaceAll(".*/", ""));
        int mask = 0xFFFFFFFF << (32 - type);
        String cidrIp = cidr.replaceAll("/.*", "");
        String[] cidrIps = cidrIp.split("\\.");
        int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24) | (Integer.parseInt(cidrIps[1]) << 16)
            | (Integer.parseInt(cidrIps[2]) << 8) | Integer.parseInt(cidrIps[3]);
        return (ipAddr & mask) == (cidrIpAddr & mask);
    }

    @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;
        }
        long value = ipToLong(address);
        for (String pool : pools) {
            String[] startEndIp = pool.split("-");
            if (!validate(startEndIp[0])) {
                return false;
            }
            if (!validate(startEndIp[1])) {
            	return false;
            }
            long ipstart = ipToLong(startEndIp[0]);// 获取起始IP的开始位置
            long ipend = ipToLong(startEndIp[1]);// 获取终止IP的结束位置
            if(value == ipstart || value == ipend) {
            	return true;
            }
            if (value > ipstart && value < ipend) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean checkIpMask(String startIp, String endIp, String cidr) {
        if (!validate(startIp)) {
            return false;
        }
        if (!validate(endIp)) {
            return false;
        }
        long startIpValue = ipToLong(startIp);
        long endIpValue = ipToLong(endIp);
        if (startIpValue > endIpValue) {
            return false;
        }
        long maskValue;
        long ipStartValue, ipEndValue;
        int index = cidr.indexOf("/");
        if (index == -1) {
            maskValue = ipToLong(cidr);
        } else {
            String maskStr = cidr.substring(index + 1);
            if (StringUtils.isEmpty(maskStr)) {
                return false;
            }
            // 通过掩码截取，后面可变的部分全部变为0 即为最小值
            String min = ipToBinary(cidr.substring(0, index)).substring(0, Integer.parseInt(maskStr))
                + ipToBinary(cidr.substring(0, index)).substring(Integer.parseInt(maskStr)).replace("1", "0");
            ipStartValue = ipToLong(binaryToIp(min));
            // 通过掩码截取，后面可变的部分全部变为1 即为最大值
            String max = ipToBinary(cidr.substring(0, index)).substring(0, Integer.parseInt(maskStr))
                + ipToBinary(cidr.substring(0, index)).substring(Integer.parseInt(maskStr)).replace("0", "1");
            ipEndValue = ipToLong(binaryToIp(max));
            if (startIpValue < ipStartValue || endIpValue > ipEndValue) {
                return false;
            }
            maskValue = getMaskLong(Integer.parseInt(maskStr));
        }
        return (maskValue & startIpValue) == (maskValue & endIpValue);
    }

    @Override
    public String getPools(String gateway, String cidr) {
        long ipStartValue, ipEndValue, ipGateway;
        int index = cidr.indexOf("/");
        String maskStr = cidr.substring(index + 1);
        // 通过掩码截取，后面可变的部分全部变为0 即为最小值 排除网络号+1
        String min = ipToBinary(cidr.substring(0, index)).substring(0, Integer.parseInt(maskStr))
            + ipToBinary(cidr.substring(0, index)).substring(Integer.parseInt(maskStr)).replace("1", "0");
        ipStartValue = ipToLong(binaryToIp(min)) + 1L;
        // 通过掩码截取，后面可变的部分全部变为1 即为最大值 排除广播号-1
        String max = ipToBinary(cidr.substring(0, index)).substring(0, Integer.parseInt(maskStr))
            + ipToBinary(cidr.substring(0, index)).substring(Integer.parseInt(maskStr)).replace("0", "1");
        ipEndValue = ipToLong(binaryToIp(max)) - 1L;
        // fix bug： PBSM-2938
        // 不确认什么原因，最后一个ip的值反而比第一个值小，为解决这个问题，把前后值做一个交换。
        if (ipEndValue < ipStartValue) {
            Long temp = ipStartValue;
            ipStartValue = ipEndValue;
            ipEndValue = temp;
        }
        List<String> ipPools = new ArrayList<>();
        ipGateway = ipToLong(gateway);
        // 最小值等于网关的话 跳过网关
        if (ipStartValue == ipGateway) {
            ipPools.add(longToIp(ipStartValue + 1L) + "-" + longToIp(ipEndValue));
        } else if (ipEndValue == ipGateway) {
            // 网关等于最大值的话 就-1
            ipPools.add(longToIp(ipStartValue) + "-" + longToIp(ipEndValue - 1L));
        } else if (ipGateway > ipStartValue && ipGateway < ipEndValue) {
            // 网关在中间的话 就分成两段
            ipPools.add(longToIp(ipStartValue) + "-" + longToIp(ipGateway - 1L));
            ipPools.add(longToIp(ipGateway + 1) + "-" + longToIp(ipEndValue));
        } else {
            ipPools.add(longToIp(ipStartValue) + "-" + longToIp(ipEndValue));
        }
        return JSONObject.toJSONString(ipPools);
    }

    @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 part = Integer.parseInt(prefix) / 8;
        int remainder = Integer.parseInt(prefix) % 8;
        int sum = 0;
        for (int i = 8; i > 8 - remainder; i--) {
            sum = sum + (int) Math.pow(2, i - 1d);
        }
        String mask = null;
        if (part == 0) {
            mask = sum + ".0.0.0";
        } else if (part == 1) {
            mask = "255." + sum + ".0.0";
        } else if (part == 2) {
            mask = "255.255." + sum + ".0";
        } else if (part == 3) {
            mask = "255.255.255." + sum;
        } else if (part == 4) {
            mask = DEFAULT_NETMASK;
        }
        return mask;
    }

    @Override
    public int getMaskLength(String mask) {
        int r = 0;
        long value = ipToLong(mask);
        while ((value & 1) == 0) {
            value = value >> 1;
            r++;
        }
        return 32 - r;
    }

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

    @Override
    public BigInteger ipToInteger(String address) {
        return new BigInteger(ipToLong(address) + "");
    }

    @Override
    public String integerToIp(BigInteger integer) {
        return longToIp(integer.longValue());
    }
}
