package com.nms.server.util.socket.limiter;

import org.apache.log4j.Logger;

import com.nms.server.common.Constants;

/**
 *  共用网络通讯限速器
 *  实现对上行和下行通讯发送频率限制，达到通讯限速目的
 *  设计：实现通讯线程IO数量同步计数linkNumber；
 * 		总网络带宽/当前IO流数量，计算单IO流通讯带宽限制；
 * 		通过带宽限制，计算每次已发送数据量的限定时间；
 * 		用线程睡眠限定时间，降低发送频率，实现通讯限速
 *  网络带宽单位：Mbps（每秒兆字节）单位换算8Kbps = 8Kb/s=1KBps=1KB/s
 *  @date Nov 13, 2012 10:37:23 AM
 *	@author ZhangGang
 *
 */
public class BandwidthLimiter {
	/* Log4j */
	private final static Logger logger = Logger.getLogger(BandwidthLimiter.class);
	
	/*全局空锁*/
	private final static byte[] lock = new byte[0];
	
	/* KB 单位*/
//	private final static Long KB = 1024l;
	
	/* 最小包长度 chunk length in bytes */
//	private final static Long CHUNK_LENGTH = 1024l;
	
	/*总链路数*/
	private static int ioNumber = 0;

	/* 缺省 无限速 byte/s */
	private static int maxRate = Constants.MAX_NETWORK_BANDWIDTH==null?0:Constants.MAX_NETWORK_BANDWIDTH*1024*1024/8;
	
	/**
	 * Next 1 byte should do bandwidth limit.
	 */
	public static void limitNextBytes() {
		limitNextBytes(1);
	}

	/**
	 * 等待限定时间执行方法
	 * 发送 len长度数据限定时间
	 * @param len 数据长度 （未涉及到TCP通讯实际发送长度，不确定实际通讯中TCP头数量）
	 */
	public static void limitNextBytes(int len) {
		if(ioNumber<=0){ logger.warn("LinkNumber cannot be calculated for 0. Please execute the BandwidthLimiter. addLink () method first"); return;}
		if(len<=0){ logger.warn("Sent bytes of length 0 cannot be calculated"); return;}
		if(maxRate == 0){return;}
		/* 计算当前 链路数 因为 同一链路内，输入和输出不能同时工作 所以 在计算单链路的通讯带宽需要 IO数减半（余1判断为容错处理） */
		int linkNumber = ioNumber/2 + ioNumber%2;
		
		
		/* 计算当前 发送 len bytes 的数据 时间周期 纳秒数 */
		long timeCostPerChunk =  1000000000l * len / (maxRate / linkNumber) ;		// 发送长度 / (单条通信线路长度) *1000000000l纳秒
		try {
//			logger.debug("本次接收字节 "+len+" 等待间隔："+timeCostPerChunk+" ns");
			Thread.sleep(timeCostPerChunk / 1000000,(int) (timeCostPerChunk % 1000000));		//Thread.sleep(毫秒,纳秒);
		} catch (InterruptedException e) {
			//KB??是不是应该是字节B
			logger.error("Communication speed limiter execution thread wait failure .Parameter[Total Bandwidth："+maxRate+"KB/s linkNumber："+linkNumber+" Data Length："+len+"Byte Limit Time："+timeCostPerChunk+"nm ]", e);
		}
	}
	
	/**
	 * 通讯中输入输出流计数器，IO流数加1
	 * @time Nov 15, 2012-10:14:56 AM
	 */
	public static void addLink() {
		synchronized (lock) {
			++ioNumber;
		}
	}

	/**
	 * 通讯中输入输出流计数器，IO流数减1
	 * @time Nov 15, 2012-10:15:37 AM
	 */
	public static void removeLink() {
		synchronized (lock) {
			--ioNumber;
		}
	}
}
