package com.bizzan.bitrade.system;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bizzan.bitrade.controller.LoginController;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang.StringUtils;

@Slf4j
public class IpSearch {

    private int[] prefStart = new int[256];
    private int[] prefEnd = new int[256];
    private long[] endArr;
    private String[] addrArr;

    private static IpSearch instance = null;
    private static String pathUrl=null;
    private IpSearch(String url) {
        pathUrl=url;
        if(null==pathUrl || pathUrl.trim().isEmpty()) {
            return;
        }
        Path path = Paths.get(pathUrl);
        if (!Files.exists(path)) {
            log.warn("IpSearch: 库文件不存在, path={}, IP归属查询将禁用", pathUrl);
            return;
        }
        byte[] data = null;
        try {
            data = Files.readAllBytes(path);
        } catch (IOException e) {
            log.warn("IpSearch: 读取库文件失败, path={}, error={}, IP归属查询将禁用", pathUrl, e.getMessage());
            return;
        }
        if(null==data) {
            return ;
        }
        // 至少需要 2052 字节的索引头
        if (data.length < 2052) {
            log.warn("IpSearch: 库文件过小或格式异常, path={}, size={}, IP归属查询将禁用", pathUrl, data.length);
            return;
        }
        for (int k = 0; k < 256; k++) {
            int i = k * 8 + 4;
            prefStart[k] = (int) BytesToLong(data[i], data[i + 1], data[i + 2], data[i + 3]);
            prefEnd[k] = (int) BytesToLong(data[i + 4], data[i + 5], data[i + 6], data[i + 7]);

        }

        int RecordSize = (int) BytesToLong(data[0], data[1], data[2], data[3]);
        if (RecordSize <= 0 || RecordSize > 10_000_000) {
            log.warn("IpSearch: 库文件RecordSize异常, path={}, RecordSize={}, IP归属查询将禁用", pathUrl, RecordSize);
            return;
        }
        // 校验数据长度是否足够: 2052 + RecordSize*8
        int requiredLen = 2052 + RecordSize * 8;
        if (data.length < requiredLen) {
            log.warn("IpSearch: 库文件长度不足, path={}, 需要={}, 实际={}, IP归属查询将禁用", pathUrl, requiredLen, data.length);
            return;
        }
        endArr = new long[RecordSize];
        addrArr = new String[RecordSize];
        try {
            for (int i = 0; i < RecordSize; i++) {
                int p = 2052 + (i * 8);
                long endipnum = BytesToLong(data[p], data[1 + p], data[2 + p], data[3 + p]);

                int offset = (int)BytesToLong3(data[4 + p], data[5 + p], data[6 + p]);
                int length = data[7 + p] & 0xff;

                endArr[i] = endipnum;

                addrArr[i] = new String(Arrays.copyOfRange(data,  offset, (offset + length)));
            }
        } catch (Exception e) {
            log.warn("IpSearch: 解析库文件失败, path={}, error={}, IP归属查询将禁用", pathUrl, e.getMessage());
            endArr = null;
            addrArr = null;
        }
    }

    public static synchronized IpSearch getInstance() {
        if (instance == null) {
            if(!StringUtils.isEmpty(pathUrl))
                instance = new IpSearch(pathUrl);
        }
        return instance;
    }
    public static synchronized IpSearch getInstance(String url) {
        log.info("url=={}",url);
        if (instance == null)
            instance = new IpSearch(url);
        log.info("instance1=={}",instance);
        if(null==instance.endArr||null==instance.addrArr)
            instance=null;
        log.info("instance2=={}",instance);
        return instance;
    }
    public static String  getArea(String ip) {
        String area ="CN";
        if(null==instance)
            return area;
        String result=instance.Get(ip);
        String[] split = result.split("\\|");
        if(8<split.length) {
            area=split[8];
        }
        return area;
    }
    public static String[]  getCountryAndProvince(String ip) {
        log.info("getCountryAndProvince=={}",ip);
        String country ="";
        String province = "";
        if(null==instance)
            return new String[] {country,province};
        log.info("instance=={}",instance);
        String result=instance.Get(ip);
        log.info("result=={}",result);
        String[] split = result.split("\\|");
        if(8<split.length) {
            country=split[1];
            province = split[2];
        }
        return new String[] {country,province};
    }

    public String Get(String ip) {
        try {
            String[] ips = ip.split("\\.");
            int pref = Integer.valueOf(ips[0]);
            long val = ipToLong(ip);
            log.info("val=={}",val);
            int low = prefStart[pref], high = prefEnd[pref];
            log.info("low=={},high={}",low,high);
            long cur = low == high ? low : BinarySearch(low, high, val);
            log.info("cur=={}",cur);
            return addrArr[(int) cur];
        } catch(Exception e) {
            log.error(e.getMessage());
        }
        return "";
    }

    private int BinarySearch(int low, int high, long k) {
        int M = 0;
        while (low <= high) {
            int mid = (low + high) / 2;

            long endipNum = endArr[mid];
            if (endipNum >= k) {
                M = mid;
                if (mid == 0) {
                    break;
                }
                high = mid - 1;
            } else
                low = mid + 1;
        }
        return M;
    }

    private long BytesToLong(byte a, byte b, byte c, byte d) {
        return (a & 0xFFL) | ((b << 8) & 0xFF00L) | ((c << 16) & 0xFF0000L) | ((d << 24) & 0xFF000000L);

    }

    private long BytesToLong3(byte a, byte b, byte c) {
        return (a & 0xFFL) | ((b << 8) & 0xFF00L) | ((c << 16) & 0xFF0000L);

    }

    private long ipToLong(String ip) {
        long result = 0;
        String[] d = ip.split("\\.");
        for (String b : d) {
            result <<= 8;
            result |= Long.parseLong(b) & 0xff;
        }
        return result;
    }
	
	/*private static String[] privinces={"北京市","天津市","河北省","山西省","内蒙古","辽宁省","吉林省","黑龙江省","上海市","江苏省","浙江省","安徽省","福建省","江西省","山东省","河南省","湖北省","湖南省","广东省","广西","海南省","重庆市","四川省","贵州省","云南省","西藏","陕西省","甘肃省","青海省","宁夏","新疆","台湾省","香港","澳门"};
	
	public static boolean isInChina(String ip) {
        try {
            URL url = new URL("http://opendata.baidu.com/api.php?query=" + ip+"&co=&resource_id=6006&t=1433920989928&ie=utf8&oe=utf-8&format=json");;
            URLConnection conn = url.openConnection();
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line = null;
            StringBuffer result = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
            reader.close();
            JSONObject jsStr = JSONObject.parseObject(result.toString());
            JSONArray jsData = (JSONArray) jsStr.get("data");
            JSONObject data= (JSONObject) jsData.get(0);//位置
            
            String address=(String) data.get("location");
            boolean inChina=false;
            for(String privince:privinces){
                if(address.startsWith(privince)){
                	inChina=true;
                    break;
                }
            }
            
            return inChina;
        } catch (IOException e) {
            return true;
        }
    }*/


}
