package com.nms.server.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.snmp4j.CommunityTarget;
import org.snmp4j.DefaultTimeoutModel;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.UserTarget;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.AuthMD5;
import org.snmp4j.security.AuthSHA;
import org.snmp4j.security.PrivAES128;
import org.snmp4j.security.PrivAES192;
import org.snmp4j.security.PrivAES256;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.DefaultPDUFactory;
import org.snmp4j.util.TreeEvent;
import org.snmp4j.util.TreeUtils;

import com.nms.server.common.Common;
import com.nms.server.common.Constants;

/**
 * 最近项目上用到了SNMP操作，在网上找了找有关java操作SNMP 的例子发现原创的非常少，要么就是不完整。
 * 看了一下SNMP4J的文档自己写了一个工具类，
 * @author Administrator
 *
 */
public class SNMP4JUtils {
	private static Logger logger = Logger.getLogger(SNMP4JUtils.class);
    public static final String ROOT="WALK";
    private static final Integer C_P_RETRIES=0;
    private static final Integer C_P_TIME_OUT=7000;
    private static final String C_P_COMMUNITY="public";
    private static final  int C_P_VERSION = SnmpConstants.version2c;
    private static final Snmp snmp = createSnmp();
    

    
    /**
     * 
     * @param datas
     * @param oids
     * @param snmp
     * @param target
     * @throws Exception
     */
    public static void snmpGet(Map<String,String> datas,String[] oids,Target target) throws Exception{
       if(target.getVersion() == SnmpConstants.version3){
    	   snmpGetV3(datas,oids,target,Constants.SNMP_V3_SECURITY_NAME);
       }else{
    	   snmpGetV2(datas,oids,snmp,target);
       }
    }
    /**
     * 
     * @param datas
     * @param oids
     * @param snmp
     * @param target
     * @throws Exception
     */
    public static void snmpGetV2(Map<String,String> datas,String[] oids,Snmp snmp,Target target) throws Exception{
       
        PDU request = createPDU(PDU.GET);
        for(String oid :oids ){
            if((oid.toString()).endsWith(".0"))
                request.add(new VariableBinding(new OID(oid)));
            else{
//            	throw new Exception(oid+" 为非基本节点");
                throw new Exception(oid+" is not basic node");
            }
        }
        ResponseEvent respEvent = snmp.send(request, target);
        PDU response = respEvent.getResponse();
//        if(isNullPDU(response,"SNMP4J 响应结果为空")){
        if(isNullPDU(response,"The result of the SNMP4J response is empty")){
        	return;
        }
        if(response.getErrorIndex()==PDU.noError&&response.getErrorStatus()==PDU.noError){
            Vector<VariableBinding> vector = (Vector<VariableBinding>) response.getVariableBindings();
            for(VariableBinding vb : vector){
                String key = vb.getOid().toString();
                datas.put(key,vb.getVariable().toString());   
                //pl(key+" 88 "+vb.getVariable().toString());
            }
        }else{
//        	throw new Exception("错误信息:"+response.getErrorStatusText());
            throw new Exception("Error information:"+response.getErrorStatusText());
        }
    }
    
    public static void snmpGetV3(Map<String,String> datas,String[] oids,Target target,String contextName) throws Exception{
        
    	ScopedPDU request = new ScopedPDU();
    	 for(String oid :oids){
            if((oid.toString()).endsWith(".0"))
                request.add(new VariableBinding(new OID(oid)));
            else{
                throw new Exception(oid+" is not basic node");
            }
        }
//		request.setContextName(new OctetString(contextName));
		request.setType(PDU.GET);
    	 
        ResponseEvent respEvent = snmp.send(request, target);
        System.out.println(snmp.getUSM().getUserTable().getUser(new OctetString(contextName)));
        System.out.println(respEvent.getResponse());
        PDU response = respEvent.getResponse();
//        if(isNullPDU(response,"SNMP4J 响应结果为空")){
        if(isNullPDU(response,"The result of the SNMP4J response is empty")){
        	return;
        }
        
        if(response.getErrorIndex()==PDU.noError&&response.getErrorStatus()==PDU.noError){
            Vector<VariableBinding> vector = (Vector<VariableBinding>) response.getVariableBindings();
            for(VariableBinding vb : vector){
                String key = vb.getOid().toString();
                datas.put(key,vb.getVariable().toString());   
            }
        }else{
//        	throw new Exception("错误信息:"+response.getErrorStatusText());
            throw new Exception("Error information:"+response.getErrorStatusText());
        }
    }

    
   private static Map<String,String> resoveVersion2c(PDU response) throws Exception{
	   Map<String,String> datas = new HashMap<String, String>();
	   if (response.getErrorIndex()==PDU.noError&&response.getErrorStatus()==PDU.noError) {  
		   Object obj = response.getVariableBindings().firstElement();  
		   VariableBinding variable = (VariableBinding) obj; 
		   String key = variable.getOid().toString();
		   System.out.println("key:"+key+":"+variable.getVariable().toString());
		   datas.put(key,variable.getVariable().toString());   
	   } else {  
//		   throw new Exception("错误信息:"+response.getErrorStatusText());
		   throw new Exception("Error information:"+response.getErrorStatusText());
	   }
	   return datas;
   }
   
	/** 
	 * 将十进六制转换成为中文 
	 */
	private static String hexString = "0123456789ABCDEF";

	public static String toStringHex(String bytes) {

		ByteArrayOutputStream baos = new ByteArrayOutputStream(
				bytes.length() / 2);
		//将每2位16进制整数组装成一个字节 
		for (int i = 0; i < bytes.length(); i += 2) {
			baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString
					.indexOf(bytes.charAt(i + 1))));
		}
		return new String(baos.toByteArray());
	}
	
   private static Map<String,String> resoveVersion3(PDU response) throws Exception{
	   
	   Map<String,String> datas = new HashMap<String, String>();
		for (int i = 0; i < response.size(); i++) {
			VariableBinding vb = response.get(i);
			String[] str = vb.getVariable().toString().toUpperCase().split(":");
			String strOut = "";
			int intLength = str.length;
			logger.debug("长度==> " + intLength);
			for (int j = 0; j < intLength; j++) {
				strOut += str[j];
			}
//			System.out.println("snmpWalkKey:"+vb.getOid()+" : "+vb.getVariable().toString()+" --中文： ");
			datas.put(vb.getOid().toString(), vb.getVariable().toString());
			if (str.length != 1) {
				logger.debug("==第284行=vb.getVariable()=" + toStringHex(strOut));//显示中文 
			}
		}
	   return datas;
   }
    
    public static CommunityTarget createCommunityTarget(String host,String port,String community,int retries,int timeout,int version){
        CommunityTarget target = new CommunityTarget();
        target.setAddress(new UdpAddress(host+"/"+port));//设置Snmp Agent的访问地址和端口
        target.setCommunity(new OctetString(StringUtils.isEmpty(community)?C_P_COMMUNITY:community));//设置共同体,默认是"public"
        target.setRetries(retries==0?C_P_RETRIES:retries);//设置重试次数
        target.setTimeout(timeout==0?C_P_TIME_OUT:timeout);//设置超时毫秒数
        target.setVersion(version==0?C_P_VERSION : version);//设置snmp的版本号
        return target;
    }
    
    public static UserTarget createUserTarget(String host,String port,int retries,int timeout){
        UserTarget target = new UserTarget();
        target.setAddress(new UdpAddress(host+"/"+port));//设置Snmp Agent的访问地址和端口
        target.setRetries(1);//设置重试次数
        target.setRetries(retries==0?C_P_RETRIES : retries);//设置重试次数
        target.setTimeout(timeout==0?C_P_TIME_OUT : timeout);//设置超时毫秒数
        target.setVersion(SnmpConstants.version3);//设置snmp的版本号
        switch (Constants.SNMP_V3_SECURITY_LEVEL) {
		case 0:
			 target.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
			break;
		case 1:
			 target.setSecurityLevel(SecurityLevel.AUTH_NOPRIV);
			break;
		case 2:
			 target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
			break;
		default:
			 target.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
			break;
		}
        target.setSecurityName(new OctetString(Constants.SNMP_V3_SECURITY_NAME));
        return target;
    }
    
    public static Snmp createSnmp() {
		
    	try {
    		if(snmp == null){
    			String ip = Common.getLocalIp();
    			if(StringUtils.isEmpty(ip)){
    				return null;
    			}

			   TransportMapping transport = new DefaultUdpTransportMapping();
			   Snmp snmp = new Snmp(transport);
    			snmp.getMessageDispatcher().addMessageProcessingModel(new MPv1());
    			snmp.getMessageDispatcher().addMessageProcessingModel(new MPv2c());
    			snmp.getMessageDispatcher().addMessageProcessingModel(new MPv3());
    			snmp.setTimeoutModel(new DefaultTimeoutModel());
    			USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
    			SecurityModels.getInstance().addSecurityModel(usm);
    			snmp.listen();
    			
    	        OID authProtocol = getAuthProtocol(Constants.SNMP_V3_AUTH_PROTOCOL);	//认证协议
    	        OID privProtocol = getPrivProtocol(Constants.SNMP_V3_PRIV_PROTOCOL);	//加密协议
    	        OctetString securityName = new OctetString(Constants.SNMP_V3_SECURITY_NAME);	//认证用户
    	        OctetString authPassphrase = new OctetString(Constants.SNMP_V3_AUTH_PASSPHRASE);	//认证密码明文
    	        OctetString privPassphrase = new OctetString(Constants.SNMP_V3_PRIV_PASSPHRASE);	//加密密码明文
    	    	snmp.getUSM().addUser(securityName, new UsmUser(securityName, authProtocol, authPassphrase,
                          privProtocol, privPassphrase));
    			logger.info("SNMP 启动成功");
    			return snmp;
    		}else{
    			 return snmp;
    		}
		} catch (IOException e) {
			logger.error("SNMP4J ERROR", e);
		} catch (Exception e) {
			logger.error("SNMP4J ERROR", e);
		}
		 return snmp;
    }
    
    private static OID getAuthProtocol(String auth){
    	if (auth.equals("MD5")) {
  		 	return  AuthMD5.ID;
        } else if (auth.equals("SHA")) {
        	return  AuthSHA.ID;
        }else{
        	return null;
        }
    }
    
    private static OID getPrivProtocol(String priv){
        if (priv.equals("DES")) {
      	  return PrivDES.ID;
        } else if ((priv.equals("AES128"))
                || (priv.equals("AES"))) {
      	  return PrivAES128.ID;
        } else if (priv.equals("AES192")) {
      	  return PrivAES192.ID;
        } else if (priv.equals("AES256")) {
      	  return PrivAES256.ID;
        }else{
        	return null;
        }
    }

    private static PDU createPDU(int type){
        PDU pdu= new PDU();
        pdu.setType(type);
        return pdu;
    }
    
    /**
     * PDU为空判断
     * @param pdu 需判断的PDU
     * @param messages PDU为空是输出的提示信息
     * @return
     * @throws Exception 
     */
    public static boolean isNullPDU(PDU pdu, String messages) throws Exception{
    	boolean isNull = true;
    	if(pdu!=null){
    		isNull = false;
    	}else{
    		isNull = true;
    		//pdu为空的处理可以添加到这里
//    		logger.debug(messages);
//    		throw new Exception("错误信息:"+messages);
    		throw new Exception("Error information:"+messages);
    	}
    	return isNull;
    }
    public static String checkTypeName = null;
    public static void snmpTree(List<Map<String, String>> datasList,List<String> oidModel, String oid,String host,String port,String community,int retries,int timeout,int version,String checktypename) throws Exception{
    	checkTypeName = checktypename;
    	snmpTree(datasList,oidModel,  oid, host, port, community, retries, timeout, version);
    }
    public static void snmpTree(List<Map<String, String>> datasList,List<String> oidModel, String oid,String host,String port,String community,int retries,int timeout,int version) throws Exception{
        Target target = null;
		if (version == SnmpConstants.version3) {
			target = createUserTarget(host,port,retries,timeout);
		}else{
			target = createCommunityTarget(host,port,community,retries,timeout, version);
		}
		treeUtilNew(datasList,oidModel,snmp,target,oid);
    }
    
    /**
     * 
     * TODO 
     * @author hyx Mar 20, 2013
     * @version 1.0 
     */
    public static void snmpIfXTabTree(Map<String,Map<String, String>> datasMap,List<String> oidModel, String oid,String host,String port,String community,int retries,int timeout,int version) throws Exception{
    	Target target = null;
    	if (version == SnmpConstants.version3) {
    		target = createUserTarget(host,port,retries,timeout);
    	}else{
    		target = createCommunityTarget(host,port,community,retries,timeout, version);
    	}
    	ifXTabTreeUtilNew(datasMap,oidModel,snmp,target,oid);
    }
    public static void subTreeUtil( List<Map<String, String>> datasList,List<String> oidModel,Snmp snmp,Target target,String rootOid)throws Exception{
       
        PDU request = createPDU(PDU.GETBULK);
        ResponseEvent respEvent = snmp.send(request, target);
        PDU response = respEvent.getResponse();
//        if(isNullPDU(response,"SNMP4J 响应结果为空")){
        if(isNullPDU(response,"The result of the SNMP4J response is empty")){
        	return;
        }
        if(response.getErrorIndex()==PDU.noError&&response.getErrorStatus()==PDU.noError){
            Vector<VariableBinding> vector = (Vector<VariableBinding>) response.getVariableBindings();
            for(VariableBinding vb : vector){
                String key = vb.getOid().toString();
            }
        }else{
//        	throw new Exception("错误信息:"+response.getErrorStatusText());
            throw new Exception("Error information:"+response.getErrorStatusText());
        }
    }
    
    public static void treeUtilNew( List<Map<String, String>> datasList,List<String> oidModel,Snmp snmp,Target target,String rootOid)throws Exception{
    	TreeUtils treeUtils = null;
    	List list = null;
    	//如果没有指定oid则取系统基本信息
    	if(StringUtils.isBlank(rootOid)){
    		return ;//rootOid = "1.3.6.1.2.1.1";
    	}
    	treeUtils = new TreeUtils(snmp ,new DefaultPDUFactory());
    	list = treeUtils.getSubtree(target, new OID(rootOid));
    	//获取到的所有实例信息，每个keyvalue中包含一个oid的所有实例信息的map
 		Map<String, Map<String, String>> mapInstance = new LinkedHashMap<String, Map<String, String>>();
        for (int j = 0; j < list.size(); j++) {
 			TreeEvent treeEvent = (TreeEvent)list.get(j);
 			if (treeEvent.isError())
 				throw new Exception((new StringBuilder("i18n_server.SNMP4JUtils.Exception3_n81i：")).append(treeEvent.getErrorMessage()).toString());
 			if (treeEvent.getException() != null)
 				throw new Exception((new StringBuilder("i18n_server.SNMP4JUtils.Exception4_n81i：")).append(treeEvent.getErrorMessage()).append(" ").append(treeEvent.getException().getMessage()).toString());
 			if (treeEvent.getStatus() != 0)
 				throw new Exception((new StringBuilder("i18n_server.SNMP4JUtils.Exception3_n81i：")).append(treeEvent.getErrorMessage()).toString());
 			if (treeEvent.getVariableBindings() != null ){
 				VariableBinding vbArr[] = treeEvent.getVariableBindings();
 				if(vbArr != null){
 					for(int i = 0,m= vbArr.length;i<m;i++){
	 					VariableBinding vb = vbArr[i];
	 					OID oid = vb.getOid();
	 					String index = oid.last()+"";//索引
	 					String oidStr = oid.toString();
	 					oidStr = oidStr.substring(0, oidStr.lastIndexOf("."));
	 					if(oidModel.contains(oidStr)){
	 						Map<String, String> map = mapInstance.get(index);
	 						if(map == null){
	 							map = new HashMap<String, String>();
	 							mapInstance.put(index, map);
	 						}
	 						map.put(oidStr, vb.getVariable().toString());
	 					}
	 				}
 				}
 			}
 		}
        if(mapInstance.size() > 0){
        	Set<Entry<String,Map<String,String>>> entrySet = mapInstance.entrySet();
        	for(Entry<String,Map<String,String>> entry : entrySet){
        		Map<String, String> value = entry.getValue();
        		if(value != null && value.size() > 0){
        			datasList.add(value);
        		}
        		
        	}
        }
        logger.debug("datasList -> " + datasList.size());
    }
    
    /**
     * 
     * TODO 
     * @author hyx Mar 20, 2013
     * @version 1.0 
     */
    public static void ifXTabTreeUtilNew( Map<String,Map<String, String>> datasMap,List<String> oidModel,Snmp snmp,Target target,String rootOid)throws Exception{
    	TreeUtils treeUtils = null;
    	List list = null;
    	//如果没有指定oid则取系统基本信息
    	if(StringUtils.isBlank(rootOid)){
    		return ;//rootOid = "1.3.6.1.2.1.1";
    	}
    	treeUtils = new TreeUtils(snmp ,new DefaultPDUFactory());
    	list = treeUtils.getSubtree(target, new OID(rootOid));
    	Vector<TreeEvent> vector2 = new Vector<TreeEvent>();
    	//获取信息过程是否异常
    	for (int j = 0; j < list.size(); j++) {
    		TreeEvent treeEvent = (TreeEvent)list.get(j);
    		if (treeEvent.isError())
    			throw new Exception((new StringBuilder("i18n_server.SNMP4JUtils.Exception3_n81i：")).append(treeEvent.getErrorMessage()).toString());
    		if (treeEvent.getException() != null)
    			throw new Exception((new StringBuilder("i18n_server.SNMP4JUtils.Exception4_n81i：")).append(treeEvent.getErrorMessage()).append(" ").append(treeEvent.getException().getMessage()).toString());
    		if (treeEvent.getStatus() != 0)
    			throw new Exception((new StringBuilder("i18n_server.SNMP4JUtils.Exception3_n81i：")).append(treeEvent.getErrorMessage()).toString());
    		if (treeEvent.getVariableBindings() != null ){
    			VariableBinding vbArr[] = treeEvent.getVariableBindings();
 				if(vbArr != null){
 					for(int i = 0,m= vbArr.length;i<m;i++){
	 					VariableBinding vb = vbArr[i];
	 					OID oid = vb.getOid();
	 					String index = oid.last()+"";//索引
	 					String oidStr = oid.toString();
	 					oidStr = oidStr.substring(0, oidStr.lastIndexOf("."));
	 					if(oidModel.contains(oidStr)){
	 						Map<String, String> map = datasMap.get(index);
	 						if(map == null){
	 							map = new HashMap<String, String>();
	 							datasMap.put(index, map);
	 						}
	 						map.put(oidStr, vb.getVariable().toString());
	 					}
	 				}
 				}
    		}
    	}
    	//删除空数据
    	Iterator<Entry<String, Map<String, String>>> it = datasMap.entrySet().iterator();
    	while(it.hasNext()){
    		Entry<String, Map<String, String>> next = it.next();
    		Map<String, String> value = next.getValue();
    		if(value == null || value.size() <1){
    			it.remove();
    		}
    	}
    }

	private static void pl(Object object) {
		System.out.println(object==null?null:object.toString());
	}
	
    public static void main(String [] args) throws Exception{
    	
    	// SNMP4J 》》》》1.3.6.1.2.1.1 10.0.6.230 161 public 3 0 1
    	//SNMP4J 》》》》 1.3.6.1.4.1.1000.1.2.1 10.0.6.230 161 public 3 0 1>
    	List<Map<String,String>> datasList = new ArrayList<Map<String,String>>();
    	List<String> oidModel = new ArrayList<String>();
    	// [1.3.6.1.2.1.1.1, 1.3.6.1.2.1.1.2, 1.3.6.1.2.1.1.3, 1.3.6.1.2.1.1.4, 1.3.6.1.2.1.1.5, 1.3.6.1.2.1.1.6, 1.3.6.1.2.1.1.7]
    	/*oidModel.add("1.3.6.1.2.1.1.1");
    	oidModel.add("1.3.6.1.2.1.1.2");
    	oidModel.add("1.3.6.1.2.1.1.3");
    	oidModel.add("1.3.6.1.2.1.1.4");
    	oidModel.add("1.3.6.1.2.1.1.5");
    	oidModel.add("1.3.6.1.2.1.1.6");
    	oidModel.add("1.3.6.1.2.1.1.7");*/
    	// [1.3.6.1.2.1.31.1.1.1.1, 1.3.6.1.2.1.31.1.1.1.2, 1.3.6.1.2.1.31.1.1.1.3, 1.3.6.1.2.1.31.1.1.1.4, 1.3.6.1.2.1.31.1.1.1.5, 1.3.6.1.2.1.31.1.1.1.6, 1.3.6.1.2.1.31.1.1.1.7, 1.3.6.1.2.1.31.1.1.1.8, 1.3.6.1.2.1.31.1.1.1.9, 1.3.6.1.2.1.31.1.1.1.10, 1.3.6.1.2.1.31.1.1.1.11, 1.3.6.1.2.1.31.1.1.1.12, 1.3.6.1.2.1.31.1.1.1.13, 1.3.6.1.2.1.31.1.1.1.14, 1.3.6.1.2.1.31.1.1.1.15, 1.3.6.1.2.1.31.1.1.1.17, 1.3.6.1.2.1.31.1.1.1.16, 1.3.6.1.2.1.31.1.1.1.19, 1.3.6.1.2.1.31.1.1.1.18]
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.1");
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.2");
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.3");
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.4");
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.5");
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.6");
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.7");
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.8");
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.9");
    	oidModel.add("1.3.6.1.4.1.1000.4.3.1.10");
    	//String oid = "1.3.6.1.2.1.1";
    	String oid = "1.3.6.1.4.1.1000.4.3.1";
    	String host = "10.0.6.230";
    	String port = "161";
    	String community = "public";
    	int retries = 3;
    	int timeout = 0;
    	int version = SnmpConstants.version2c;
    	String checktypename = "checktype100017";
    	snmpTree(datasList, oidModel, oid, host, port, community, retries, timeout, version, checktypename);
    	
    	System.out.println(datasList.size());
    	System.out.println(Arrays.toString(datasList.toArray()));
    }

}