package com.nms.server.thread.monitor;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.Socket;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.snmp4j.Target;
import org.snmp4j.mp.SnmpConstants;

import com.nms.server.bean.AlarmInfo;
import com.nms.server.bean.SetInfo;
import com.nms.server.common.Common;
import com.nms.server.common.Constants;
import com.nms.server.util.CSVUtils;
import com.nms.server.util.DateUtil;
import com.nms.server.util.ExceptionPrintUtils;
import com.nms.server.util.SNMP4JUtils;

/**
 *  监测数据
 *  监测NMSClient端是否执行执行线程
 *	@author ZhangGang
 *
 */
public class NMSClientForSNMPThread implements  Runnable{
	private Logger logger = Logger.getLogger(NMSClientForSNMPThread.class);
	private SimpleDateFormat format = new SimpleDateFormat(Constants.COMMON_DATE_FORMAT);
	private String name;
	private String ip;
	private int port = Constants.SNMP_CLIENT_PORT;
	private String checkTypeName = null;
	private Long startTime = null;
	private Long sitInfoId = null;
	private long outTime = 2;
	private long maxTimes = 3;
	private long gap = 0 ;
	protected Socket socket = null;		//Socket
	private String processIden = null;
	private CountDownLatch downLatch = null;
	/**
	 * 构造方法
	 * @param name 线程名称
	 * @param ip 被监测端IP	
	 * @param port socket通信端口
	 * @param info
	 */
	public NMSClientForSNMPThread(String name,String ip,SetInfo info,Long startTime,CountDownLatch downLatch) {
		this.name = name;
		this.ip = ip;
		this.sitInfoId = info.getId();
		this.checkTypeName = info.getCheckTypeName();
		this.startTime = startTime;
		this.outTime = info.getCheckOutTime();
		this.maxTimes = info.getCheckMaxTimes();
		this.processIden = info.getProcessIden()==null ? "" : info.getProcessIden();
		this.downLatch = downLatch;
	}
	
	public NMSClientForSNMPThread(String name,String ip,SetInfo info,Long startTime) {
		this.name = name;
		this.ip = ip;
		this.sitInfoId = info.getId();
		this.checkTypeName = info.getCheckTypeName();
		this.startTime = startTime;
		this.outTime = info.getCheckOutTime();
		this.maxTimes = info.getCheckMaxTimes();
		this.processIden = info.getProcessIden()==null ? "" : info.getProcessIden();
		this.downLatch = downLatch;
	}
	@Override
	public void run() {
		
		Thread.currentThread().setName(name+" "+ip);
		
		try {

			Calendar checkTime =  Calendar.getInstance();
			Calendar nextTime =  Calendar.getInstance();
			nextTime.add(Calendar.MINUTE, Long.valueOf(gap).intValue());
			
			List<String> datas = new ArrayList<String>();		//拼写数据格式 已安全进入公共数据解析入口
			//--初始部分数据整理
			datas.add(Common.getIpSeqIdMap().get(ip)+"");//IP，
			datas.add(sitInfoId+"");//监测设置ID，
			datas.add(checkTypeName);//监测类别，
			datas.add(processIden);//进程名称（检查类别设置名称），
			
			String stateInfo ="";
			int state = -1;
			String[] datas2 = new String[6];
			int a = 0;
			String msgArr = "";
			boolean isSuccess = false;
			datas2[3] = Calendar.getInstance().getTimeInMillis()+"";
			isSuccess = this.sockeHandshake(msgArr);
			datas2[4] = Calendar.getInstance().getTimeInMillis()+"";
			datas2[0] = "snmp:"+"1.3.6.1.2.1.1.5.0";
			datas2[1] = msgArr;
//			stateInfo += "开始"+format.format(new Date(Long.parseLong(datas2[3])))+ " SNMP握手";
//			stateInfo += "Start"+format.format(new Date(Long.parseLong(datas2[3])))+ " SNMP Shakehand";
			stateInfo += "i18n_server.NMSClientForSNMPThread.begin_n81i"+format.format(new Date(Long.parseLong(datas2[3])))+ " i18n_server.NMSClientForSNMPThread.snmpShake_n81i";
			datas.add(startTime+"");//监测服务启动时间，
			if(isSuccess){			//成功
//				stateInfo += format.format(new Date(Long.parseLong(datas2[4]))) + " 成功 ";
				stateInfo += format.format(new Date(Long.parseLong(datas2[4]))) + " i18n_server.NMSClientForSNMPThread.success_n81i ";
				datas2[2] = "0";
			}else{		//失败
//				stateInfo += format.format(new Date(Long.parseLong(datas2[4]))) + " 失败 ";
				stateInfo += format.format(new Date(Long.parseLong(datas2[4]))) + " i18n_server.NMSClientForSNMPThread.fail_n81i ";
				datas2[2] = "1";
				String [] pingData = pingHandshake();
				
				if ((StringUtils.isEmpty(pingData[1]) || "0".equals(pingData[1])) ) {
//					stateInfo = "网络异常";
					stateInfo = "i18n_server.NMSClientForSNMPThread.netErr_n81i";
				} else {
//					stateInfo = "请检查目标主机NMSClient是否启动";
				}
			}
			datas2[5] = DateUtil.getSecondsFromBeinToEnd(new Date(Long
					.parseLong(datas2[3].toString())).getTime(), new Date(Long
					.parseLong(datas2[4].toString())).getTime())
					+ "";
			state = 0;
	
				
			datas.add("0");//检测时延（秒）
			datas.add(checkTime.getTimeInMillis()+"");//本次检测时间，
			
			if(state!=-1){
				datas.add(a+"");// 尝试次数，+"/"+maxTimes
				datas.add(nextTime.getTimeInMillis()+"");// 下次计划监测时间，
				String [] pArray = getAlarmState(Common.getAlarmInfoMap().get(sitInfoId.longValue()+""), datas2);
				if(isSuccess){
					datas.add("1"); // 执行状态
					datas.add(""); // 告警列序号，
					datas.add(""); // 告警级别，
					datas.add(""); // 告警值，
				}else {
					datas.add("0"); // 执行状态
					datas.add(pArray==null?"":pArray[0]); // 告警列序号，
					datas.add(pArray==null?"":pArray[1]); // 告警级别，
					datas.add(pArray==null?"":pArray[2]); // 告警值，
				}
				datas.add(stateInfo); // 状态信息（描述信息），
				datas.add(stateInfo); // 性能数据，
				datas.addAll(Arrays.asList(datas2));//监测具体数据信息（每个字段一列）
			} else {
				datas.add(a+"");// 尝试次数，+"/"+maxTimes
				datas.add(nextTime.getTimeInMillis()+"");// 下次计划监测时间，
				datas.add("-1"); // 执行状态
				datas.add("3"); // 告警列序号，(丢包数 列 序号 为3)
				datas.add("1"); // 告警级别，
				datas.add("1"); // 告警值，
				datas.add(stateInfo); // 状态信息（描述信息），
				datas.add(stateInfo); // 性能数据，
			}
			datas.add(checkTime.getTimeInMillis()+"");//本次检测时间，
	
			//- 存入缓存
			try {
				logger.debug("data："+Arrays.toString(datas.toArray()));
				Common.addDeteData(CSVUtils.csvBytesPrinter(datas, Constants.COMMON_TEXT_CODING));
				logger.debug("监测数据以 CSV格式 保存完成");
			} catch (Exception e) {
				logger.error("Monitoring data to save exceptions in CSV format", e);
			}
		} catch (Exception ex) {
			logger.error(ExceptionPrintUtils.printExceptionStack(ex));
			String[] alarm = Common.alarmExceptionInfo(sitInfoId, ip, Constants.DETEC_NMSC_STR, processIden, startTime, 1,Constants.DETECTION_INFO_ALARM_WRONG, ex.getMessage());
			Common.addAlarmData(alarm);
		}finally{
			if(downLatch != null){
				downLatch.countDown();
			}
		}
	}
	
	private void datasHeader(List<String> pubDatas){
		
		logger.debug("监测数据头拼写开始");
		
		// --初始部分数据整理
		pubDatas.add(Common.getIpSeqIdMap().get(ip)+"");// uuid，
		pubDatas.add(sitInfoId + "");// 监测设置ID，
		pubDatas.add(checkTypeName);// 监测类别，
		pubDatas.add("");// 进程名称（检查类别设置名称），
		pubDatas.add(startTime + "");// 监测服务启动时间，
		pubDatas.add("0");// 检测时延（秒）

	}
	
	private boolean sockeHandshake(String value){
		//- 通过 CloumnsMap 获取 oids
		String [] oidArray = new String []{"1.3.6.1.2.1.1.5.0"};
		//- 监测数据集合
		List<String> pubDatas = new ArrayList<String>(); // 拼写数据格式
	
		//- DetecDatas 拼写 Header
		datasHeader(pubDatas);
	
		int version = Common.getSnmpVerByUUID(Common.getIpSeqIdMap().get(ip));
//		int version = SnmpConstants.version2c;
		//-- 执行和解析 即获取详细数据
		int state = -1;

		Map<String, String> snmpDatas = new HashMap<String, String>() ;
		StringBuffer stateInfo = new StringBuffer();
		try {
			Target target= null;
			if(version == SnmpConstants.version3){
				target = SNMP4JUtils.createUserTarget(ip, port+"", (int)maxTimes, (int)outTime*1000);
			}else{
				target = SNMP4JUtils.createCommunityTarget(ip, port+"", Constants.SNMP_COMMUNITY, (int)maxTimes, (int)outTime*1000, SnmpConstants.version2c);
			}
			SNMP4JUtils.snmpGet(snmpDatas, oidArray, target);
			
			state = 1;
		} catch (Exception e) {
			state = -1;
			stateInfo.append(e.getMessage());
			logger.error((version == SnmpConstants.version3?"version3":"version2c")+" GET失败", e);
		}
	
		//- DetecDatas 拼写 Body
		if(state==1 && snmpDatas.size()>0 && StringUtils.isNotBlank(snmpDatas.get(oidArray[0]))){
			value = snmpDatas.get(oidArray[0]);
			logger.debug((version == SnmpConstants.version3?"version3":"version2c")+" GET："+oidArray[0]+" "+snmpDatas.get(oidArray[0]));
			return true;
		}else{
			return false;
		}
	}
	
	private String[] pingHandshake(){
		
		String command = ""; // 命令语句
		int minTime = Integer.MAX_VALUE, maxTime = 0, totalTime = 0, curTime; // 最短时间，最长时间，总时间，时间变量
		int snum = 0, fnum = 0; // 发包成功和失败数
		
		// 判断系统类型 win or Linux
		String system = (String) (System.getProperty("os.name")).toLowerCase();
		
		if (system.indexOf("win") != -1) {
			command += "ping -n 4 -w 4000 " + ip;
		} else if (system.indexOf("linux") != -1) {
			command += "ping -c 4 -w 4000 " + ip;
		} else {
			command += "ping " + ip;
		}
		
		String stateInfo = "";
		String[] datas2 = new String[7];
		Process process = null;
		BufferedReader in = null; // 读取 Ping命令返回的信息
		StringBuffer buffer = new StringBuffer();
		try {
			process = Runtime.getRuntime().exec(command);
			in = new BufferedReader(new InputStreamReader(process.getInputStream()));
			String line = null;
			long count = maxTimes + 10;
			int index;
			// 最多多读10行
			while ((line = in.readLine()) != null && count != 0) {
				if ("".equals(line)) {
					continue;
				} // 空串跳过
				buffer.append(line + "\n");
				line = line.toLowerCase();
				logger.debug("line:"+line);
				if (line.indexOf("ttl") > 0) { // 获得成功响应的数据
					count--; // 计数器自减1
					// 截取time 值
					if ((index = line.lastIndexOf("ms")) != -1) {
						byte[] buf = line.getBytes();
						int start = 0, end = buf.length, i, j;
						// 下标末点
						for (i = index; i >= 0; i--) {
							if (Character.isDigit((char) buf[i])) {
								end = i;
								break;
							}
						}
						if (i == 0)
							continue;
						// 下标起点
						for (j = end; j >= 0; j--) {
							if (!Character.isDigit((char) buf[j])) {
								start = j + 1;
								break;
							}
						}
						// 时间值截取
						curTime = Integer.parseInt(new String(buf, start,
								end + 1 - start));
						snum++; // 成功接收次数加1
						totalTime += curTime; // 计算总时间
						
						if (curTime < minTime) {
							minTime = curTime;
						} // 最小时间
						
						if (curTime > maxTime) {
							maxTime = curTime;
						} // 最大时间
					}
				} else if (line.split(" ").length < 4) {
					count--; // 计数器自减1
					fnum++; // 失败接收次数加1
				} else {
					stateInfo += line + "\n";
				}
			}
			
			if (totalTime == 0) {
				minTime = 0;
			}
			
			// 已发送包数
			datas2[0] = "" + (snum + fnum);
			datas2[1] = "" + snum;
			datas2[2] = "" + fnum;
			
			BigDecimal fnum0 = new BigDecimal(fnum);
			BigDecimal tnum0 = new BigDecimal(snum + fnum);
			DecimalFormat df = new DecimalFormat("####0.00");
			datas2[3] = ""
					+ ((new BigDecimal(df.format(fnum0.divide(tnum0))))
							.multiply(new BigDecimal(100)));
			datas2[4] = "" + minTime;
			datas2[5] = "" + maxTime;
			datas2[6] = "" + (snum == 0 ? 0 : (totalTime / snum));
		} catch (Exception e) {
			logger.error(ExceptionPrintUtils.printExceptionStack(e));
		}finally{
			if(in!=null){try {
				in.close();
			} catch (IOException e) {
				logger.error("",e);
			}}
			if(process!= null)process.destroy();
			process = null;
		}
		
		/*if(state==-1){
			isSuccess = false;
		}else{
			isSuccess = true;
		}*/
		return datas2;
	}
	
	//获得当前的报警设置信息，与相应字段值比较
	private String[] getAlarmState(List<AlarmInfo> alarmInfos, String[] sysData){
		String[] strs = null;
		if(alarmInfos!=null){
			strs = new String[]{"", "", ""};
			for(AlarmInfo alarm : alarmInfos){
				if(sysData.length>=alarm.getShowNum()){
					Double data = Math.abs(Double.parseDouble(sysData[alarm.getShowNum()-1]));
					if("equal".equals(alarm.getPoliceSysmbols())){
						if((data+"").equals(alarm.getPoliceValue())){
							strs[0] += alarm.getShowNum() + "|";// 告警序列号
							strs[1] += alarm.getPoliceLevel() + "|"; //告警级别
							strs[2] += alarm.getPoliceValue() + "|"; //告警值
						}
					}
					else if("include".equals(alarm.getPoliceSysmbols())){
						if((data+"").indexOf(alarm.getPoliceValue())!=-1){
							strs[0] += alarm.getShowNum() + "|";// 告警序列号
							strs[1] += alarm.getPoliceLevel() + "|"; //告警级别
							strs[2] += alarm.getPoliceValue() + "|"; //告警值
						}
					}
					else if("exclude".equals(alarm.getPoliceSysmbols())){
						if((data+"").indexOf(alarm.getPoliceValue())==-1){
							strs[0] += alarm.getShowNum() + "|";// 告警序列号
							strs[1] += alarm.getPoliceLevel() + "|"; //告警级别
							strs[2] += alarm.getPoliceValue() + "|"; //告警值
						}
					}
					else{
						double result = data - Double.parseDouble(alarm.getPoliceValue());
						if ((">".equals(alarm.getPoliceSysmbols()) && result > 0)
								|| ("<".equals(alarm.getPoliceSysmbols()) && result < 0)
								|| ("=".equals(alarm.getPoliceSysmbols()) && result == 0)
								|| (">=".equals(alarm.getPoliceSysmbols()) && result >= 0)
								|| ("<=".equals(alarm.getPoliceSysmbols()) && result <=0) ) {
							strs[0] += alarm.getShowNum() + "|";// 告警序列号
							strs[1] += alarm.getPoliceLevel() + "|"; //告警级别
							strs[2] += alarm.getPoliceValue() + "|"; //告警值
						}
					}
				}
			}
			for(int i=0; i<strs.length; i++){
				if(strs[i].length()>0){
					strs[i] = strs[i].substring(0, strs[i].length()-1);
				}
			}
		}
		
		if(strs!=null && "".equals(strs[0])){
			strs = null;
		}
		
		return strs;
	}

	public NMSClientForSNMPThread(String name,Long sitInfoId,String socketIp,long gap,long outTime,long maxTimes) {
		this.name = name;
		this.ip = socketIp;
		this.sitInfoId = sitInfoId;
		this.outTime = outTime;
		this.maxTimes = maxTimes;
		this.gap = gap;	
	}
	
	/*public static void main(String [] args) {
		Thread thread = new Thread(new NMSClientForSNMPThread("NMSC Thread",10l,"10.0.6.254",2,100,3));
//		String name,Long sitInfoId,String socketIp,int socketPort,long gap,long outTime,long maxTimes
		thread.start();
	}*/
	
}
