package org.kne.cloud.network.klalb;

import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jctools.counters.CountersFactory;
import org.jctools.counters.FixedSizeStripedLongCounter;
import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.cloud.network.ipv6.IPv6Address;

public class KLALBUtils {
	public static Inet6Address uuidToIP(UUID uuid) {
		ByteArrayOutputStream bos=new ByteArrayOutputStream(8);
		DataOutputStream dos=new DataOutputStream(bos);
		try {
			long l=uuid.getMostSignificantBits();
			l=(l&0x0000FFFFFFFFFFFFL)|0x2486000000000000L;
		dos.writeLong(l);
			dos.writeLong(uuid.getLeastSignificantBits());
		dos.close();} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		try {
			return (Inet6Address) Inet6Address.getByAddress(bos.toByteArray());
		} catch (UnknownHostException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return null;
	}
	

	public static IPv6Address randomKLALBIPv6Address() {
		SecureRandom sc = new SecureRandom();
		byte[] v = new byte[16];
		sc.nextBytes(v);
		v[0] = (byte) 0x24;
		v[1] = (byte) 0x86;
		v[2] = 0x00;
		v[3] = 0x01;
		return IPv6Address.valueOf(v);
	}
	public static void main(String[] args) {
		System.out.println(uuidToIP(new UUID(-1,-1)));
	}
	
	public static String convertUintNanoDefalut(long v) {
		if(v>=1000L*1000L*1000L*1000L*1000L) {
			return format( v/(1000.0*1000.0*1000.0*1000.0*1000.0))+"M";
		}else if(v>=1000L*1000L*1000L*1000L) {
			return format (v/(1000.0*1000.0*1000.0*1000.0))+"K";
		}else if(v>=1000L*1000L*1000L) {
			return format( v/(1000.0*1000.0*1000.0));
		}else if(v>=1000L*1000L) {
			return format( v/(1000.0*1000.0))+"m";
		}else if(v>=1000L) {
			return format( v/(1000.0))+"u";
		}else  {
			return Long.toString(v)+"n" ;
		}
	}

	public static String convertUintDefalut(long v) {
		if(v>=1000L*1000L*1000L*1000L*1000L) {
			return format( v/(1000.0*1000.0*1000.0*1000.0*1000.0))+"P";
		}else if(v>=1000L*1000L*1000L*1000L) {
			return format (v/(1000.0*1000.0*1000.0*1000.0))+"T";
		}else if(v>=1000L*1000L*1000L) {
			return format( v/(1000.0*1000.0*1000.0))+"G";
		}else if(v>=1000L*1000L) {
			return format( v/(1000.0*1000.0))+"M";
		}else if(v>=1000L) {
			return format( v/(1000.0))+"K";
		}else  {
			return Long.toString(v) ;
		}
	}
	
	
	public static String convertIBUint(long v) {
		return convertIUintDefalut(v)+"B";
		/*if (v >= 1024L * 1024 * 1024 * 1024 * 1024) {
			return String.format("%.2f", v / (1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)) + "PB";
		} else if (v >= 1024L * 1024 * 1024 * 1024) {
			return String.format("%.2f", v / (1024.0 * 1024.0 * 1024.0 * 1024.0)) + "TB";
		} else if (v >= 1024L * 1024 * 1024) {
			return String.format("%.2f", v / (1024.0 * 1024.0 * 1024.0)) + "GB";
		} else if (v >= 1024L * 1024) {
			return String.format("%.2f", v / (1024.0 * 1024.0)) + "MB";
		} else if (v >= 1024L) {
			return String.format("%.2f", v / (1024.0)) + "KB";
		} else {
			return v + "B";
		}*/
	}
	public static String convertIBUintSimp(long v) {
		if(v>=1024L*1024*1024*1024*1024) {
			return format( v/(1024.0*1024.0*1024.0*1024.0*1024.0))+"P";
		}else if(v>=1024L*1024*1024*1024) {
			return format (v/(1024.0*1024.0*1024.0*1024.0))+"T";
		}else if(v>=1024L*1024*1024) {
			return format( v/(1024.0*1024.0*1024.0))+"G";
		}else if(v>=1024L*1024) {
			return format( v/(1024.0*1024.0))+"M";
		}else if(v>=1024L) {
			return format( v/(1024.0))+"K";
		}else  {
			return v+"B";
		}
	}
	

	public static String convertIUintDefalut(long v) {
		if(v>=1024L*1024*1024*1024*1024) {
			return format( v/(1024.0*1024.0*1024.0*1024.0*1024.0))+"Pi";
		}else if(v>=1024L*1024*1024*1024) {
			return format (v/(1024.0*1024.0*1024.0*1024.0))+"Ti";
		}else if(v>=1024L*1024*1024) {
			return format( v/(1024.0*1024.0*1024.0))+"Gi";
		}else if(v>=1024L*1024) {
			return format( v/(1024.0*1024.0))+"Mi";
		}else if(v>=1024L) {
			return format( v/(1024.0))+"Ki";
		}else  {
			return Long.toString(v) ;
		}
	}
	
	private static String format( double d) {
	String fmt=String.format( "%.2f",d);
	if(fmt.length()>4) {
		fmt=fmt.substring(0, 4);
	}
	if(fmt.endsWith(".")) {
		fmt=fmt.substring(0, fmt.length()-1);
	}
		return fmt;
	}
	public static int binarySearchDATATPacketNumber(List<DATATPacket>lst,long number) {
		int low =0;
		int high=lst.size()-1;
		int middle=0;
		if(low>high||number<lst.get(low).getNumber()||number>lst.get(high).getNumber()) {
			return -1;
		}
		while(low<=high) {
			middle=(low+high)/2;
			long xn=lst.get(middle).getNumber();
			if(xn>number) {
				high=middle-1;
			}else if(xn<number) {
				low=middle+1;
			}else {
				return middle;
			}
		}
		return -1;
	}
	public static Color getColorByLoadPercentage(float f) {
		int r=0;
		int g=255;
		if(f<50f) {
			r+=f/50f*255f;
		}else {
			r=255;
			g-=(f-50f)/50f*255f;
		}
			if(r>255)
				r=255;
			if(r<0)
				r=0;
			if(g>255)
				g=255;
			if(g<0)
				g=0;
		Color col=new Color( r,g,0);
		return col;
	}
	public static Color getColorByLoadPercentagex(float f) {
		int r=0;
		int g=200;
		if(f<50f) {
			r+=f/50f*200f;
		}else {
			r=200;
			g-=(f-50f)/50f*200f;
		}
			if(r>200)
				r=200;
			if(r<0)
				r=0;
			if(g>200)
				g=200;
			if(g<0)
				g=0;
		Color col=new Color( r,g,0);
		return col;
	}
	
	public static KLALBPacketLink createKLALBPacketLink(MultiProtocolSocketAddress bindAddress, MultiProtocolSocketAddress targetAddress, boolean buffered)
			throws IOException {
		if (targetAddress.isStream()) {
			if (targetAddress.supportNIO()) {
				if (bindAddress != null) {
					return new StreamChannelKLALBPacketLink(targetAddress
							.connectSocketChannel(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort()),buffered);
				} else {
					return new StreamChannelKLALBPacketLink(targetAddress.connectSocketChannel(),buffered);
				}
			} else {
				if (bindAddress != null) {
					return new StreamKLALBPacketLink(
							targetAddress.connectSocket(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort()));
				} else {
					return new StreamKLALBPacketLink(targetAddress.connectSocket());
				}
			}
		} else {
			if (bindAddress != null) {
				return new SplitedDatagramKLALBPacketLink(
						targetAddress.connectDatagramSocket(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort()));
			} else {
				return new SplitedDatagramKLALBPacketLink(targetAddress.connectDatagramSocket());
			}
		}
	}
	public static KLALBPacketLink createKLALBPacketLink(MultiProtocolSocketAddress bindAddress,
                                                        MultiProtocolSocketAddress targetAddress, boolean buffered, int timeout) throws UnknownHostException, IOException {
		if (targetAddress.isStream()) {
			if (targetAddress.supportNIO()) {
				if (bindAddress != null) {
					if(buffered) {
						return new StreamChannelKLALBPacketLink(targetAddress
								.connectSocketChannel(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort(),timeout),buffered);
					}else {
					return new StreamChannelKLALBPacketLink(targetAddress
							.connectSocketChannel(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort(),timeout),buffered);
					}
				} else {
					if(buffered) {
						return new StreamChannelKLALBPacketLink(targetAddress.connectSocketChannel(timeout));
					}else {
					return new StreamChannelKLALBPacketLink(targetAddress.connectSocketChannel(timeout));
					}
				}
			} else {
				if (bindAddress != null) {
					return new StreamKLALBPacketLink(
							targetAddress.connectSocket(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort(),timeout));
				} else {
					return new StreamKLALBPacketLink(targetAddress.connectSocket(timeout));
				}
			}
		} else {
			if (bindAddress != null) {
				return new SplitedDatagramKLALBPacketLink(
						targetAddress.connectDatagramSocket(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort()));
			} else {
				return new SplitedDatagramKLALBPacketLink(targetAddress.connectDatagramSocket());
			}
		}
	}
	public static final String IPv6Reg = "([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}$"
            + "|^:((:[\\da-fA-F]{1,4}){1,6}|:)$"
            + "|^[\\da-fA-F]{1,4}:((:[\\da-fA-F]{1,4}){1,5}|:)$"
            + "|^([\\da-fA-F]{1,4}:){2}((:[\\da-fA-F]{1,4}){1,4}|:)$"
            + "|^([\\da-fA-F]{1,4}:){3}((:[\\da-fA-F]{1,4}){1,3}|:)$"
            + "|^([\\da-fA-F]{1,4}:){4}((:[\\da-fA-F]{1,4}){1,2}|:)$"
            + "|^([\\da-fA-F]{1,4}:){5}:([\\da-fA-F]{1,4})?$"
            + "|^([\\da-fA-F]{1,4}:){6}:"; // IPv6地址的格式
 
/**
 * 将一个IPv6地址转为全写格式，全写中的前导0省略
 * 例：将1ade:03da:0::转为1ade:3da:0:0:0:0:0:0
 *
 * @param IPv6Str
 * @return fullIPv6
 */
public static String parseFullIPv6(String IPv6Str) {
    // 判断IPv6地址的格式是否正确
    if (!IPv6Str.matches(IPv6Reg)) {
        return "";
    }
 
    String[] arr = new String[]{"0", "0", "0", "0", "0", "0", "0", "0"};
 
    // 将IPv6地址用::分开
    // 如果IPv6地址为::，tempArr.length==0
    // 如果不包含::或以::结尾，tempArr.length==1
    // 如果以::开头或::在中间，tempArr.length==2
    String[] tempArr = IPv6Str.split("::");
 
    // tempArr[0]用:分开，填充到arr前半部分
    if (tempArr.length > 0) {
        // new String[0]为空数组，因为"".split(":")为{""}，如果tempArr[0]==""，此时数组包含一个元素
        String[] tempArr0 = tempArr[0].isEmpty() ? new String[0] : tempArr[0].split(":");
        for (int i = 0; i < tempArr0.length; i++) {
            // 如果是纯数字，用parseInt去除前导0，如果包含字母，用正则去除前导0
            arr[i] = tempArr0[i].matches("\\d+")
                    ? (Integer.parseInt(tempArr0[i]) + "")
                    : tempArr0[i].replaceAll("^(0+)", "");
        }
    }
 
    // tempArr[1]用:分开，填充到arr后半部分
    if (tempArr.length > 1) {
        String[] tempArr1 = tempArr[1].isEmpty() ? new String[0] : tempArr[1].split(":");
        for (int i = 0; i < tempArr1.length; i++) {
            arr[i + arr.length - tempArr1.length] = tempArr1[i].matches("\\d+")
                    ? (Integer.parseInt(tempArr1[i]) + "")
                    : tempArr1[i].replaceAll("^(0+)", "");
        }
    }
 
    return join(arr, ":");
}
 
private static String join(String[] arr, String string) {
	StringBuilder sb=new StringBuilder();
	for (int i = 0; i < arr.length; i++) {
		sb.append(arr[i]);
		if(i+1<arr.length) {
			sb.append(string);
		}
	}
	return sb.toString();
}
/**
 * 将一个IPv6地址转为简写格式
 * 例：将1ade:03da:0::转为1ade:3da::
 *
 * @param IPv6Str
 * @return AbbrIPv6
 */
public static String parseAbbrIPv6(String IPv6Str) {
    // 将一个IPv6地址转为全写格式，全写中的前导0省略
    String fullIPv6 = parseFullIPv6(IPv6Str);
    if (fullIPv6.isEmpty()) { // 如果IPv6地址的格式不正确
        return "";
    }
 
    // 将1ade:3da:0:0:0:0:0:99转为{"-","-","0","0","0","0","0","-"}
    String[] arr = fullIPv6.split(":");
    for (int i = 0, len = arr.length; i < len; i++) {
        if (!"0".equals(arr[i])) {
            arr[i] = "-";
        }
    }
 
    // 找到最长的连续的0
    Pattern pattern = Pattern.compile("0{2,}");
    Matcher matcher = pattern.matcher(join(arr, ""));
    String maxStr = "";
    int start = -1;
    int end = -1;
    while (matcher.find()) {
        if (maxStr.length() < matcher.group().length()) {
            maxStr = matcher.group();
            start = matcher.start();
            end = matcher.end();
        }
    }
 
    // 合并
    arr = fullIPv6.split(":");
    if (maxStr.length() > 0) {
        for (int i = start; i < end; i++) {
            arr[i] = ":";
        }
    }
    return join(arr, ":").replaceAll(":{2,}", "::");
}

	private static class UUIDGenarator{

		private final long id;
		private long counter=0;
		private long randUUIDh;
		private long randUUIDl;
		private static SecureRandom sr=new SecureRandom();
		public UUIDGenarator(long threadId) {
			this.id=threadId;
			randUUIDh=sr.nextLong();
			randUUIDl=sr.nextLong();
		}
		public UUID genarateUUID() {
			long h=randUUIDh^id;
			long l=randUUIDl^counter;
			
			UUID ret=new UUID(h, l);
			counter++;
			return ret;
		}
		
	}
	private static ThreadLocal<UUIDGenarator> gens=ThreadLocal.withInitial(()->{
		return new UUIDGenarator(Thread.currentThread().threadId());
	});
	public static UUID createGlobalUUID() {
		//return UUID.randomUUID();
		return gens.get().genarateUUID();
	}


	public static FixedSizeStripedLongCounter createCounter() {
		return CountersFactory.createFixedSizeStripedCounter(Runtime.getRuntime().availableProcessors());
	}
}
