package com.netease.LDNetDiagnoService;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.util.Log;

/**
 * 通过ping模拟traceroute过程
 * 
 * @author panghui
 * 
 */
public class LDNetTraceRoute {
  private final String LOG_TAG = "LDNetTraceRoute";
  private static LDNetTraceRoute instance;

  private LDNetTraceRoute() {
  }

  public static LDNetTraceRoute getInstance() {
    if (instance == null) {
      instance = new LDNetTraceRoute();
    }
    return instance;
  }

  LDNetTraceRouteListener listener;
  public boolean isCTrace = true;

  public void initListenter(LDNetTraceRouteListener listener) {
    this.listener = listener;
  }

  /**
   * 监控NetPing的日志输出到Service
   * 
   * @author panghui
   * 
   */
  public interface LDNetTraceRouteListener {
    public void OnNetTraceUpdated(String log);

    public void OnNetTraceFinished();
  }

  /**
   * 执行指定host的traceroute
   * 
   * @param host
   * @return
   */
  public void startTraceRoute(String host) {
    if (isCTrace && loaded) {
      try {
        startJNICTraceRoute(host);
      } catch (UnsatisfiedLinkError e) {
        e.printStackTrace();
        // 如果c调用失败改调JAVA代码
        Log.i("LDNetTraceRoute", "调用java模拟traceRoute");
        TraceTask trace = new TraceTask(host, 1);
        execTrace(trace);
      }
    } else {
      TraceTask trace = new TraceTask(host, 1);
      execTrace(trace);
    }
  }

  public void resetInstance() {
    if (instance != null) {
      instance = null;
    }
  }

  /**
   * 调用jni c函数执行traceroute过程
   */
  public native void startJNICTraceRoute(String traceCommand);

  static boolean loaded;
  static {
    try {
      System.loadLibrary("tracepath");
      loaded = true;
    } catch (UnsatisfiedLinkError e) {
      e.printStackTrace();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 供jni c函数回调
   * 
   * @param log
   */
  public void printTraceInfo(String log) {
    // Log.i(LOG_TAG, log);
    listener.OnNetTraceUpdated(log.toString());
  }

  private static final String MATCH_TRACE_IP = "(?<=From )(?:[0-9]{1,3}\\.){3}[0-9]{1,3}";
  private static final String MATCH_PING_IP = "(?<=from ).*(?=: icmp_seq=1 ttl=)";
  private static final String MATCH_PING_TIME = "(?<=time=).*?ms";

  /**
   * 执行ping命令，返回ping命令的全部控制台输出
   * 
   * @param ping
   * @return
   */
  private String execPing(PingTask ping) {
    Process process = null;
    String str = "";
    BufferedReader reader = null;
    try {
      process = Runtime.getRuntime().exec("ping -c 1 " + ping.getHost());
      reader = new BufferedReader(new InputStreamReader(
          process.getInputStream()));
      String line = null;
      while ((line = reader.readLine()) != null) {
        str += line;
      }
      reader.close();
      process.waitFor();

    } catch (IOException e) {
      e.printStackTrace();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      try {
        if (reader != null) {
          reader.close();
        }
        process.destroy();
      } catch (Exception e) {
      }
    }
    return str;
  }

  /**
   * 通过ping命令模拟执行traceroute的过程
   * 
   * @param trace
   * @return
   */
  private void execTrace(TraceTask trace) {
    Pattern patternTrace = Pattern.compile(MATCH_TRACE_IP);
    Pattern patternIp = Pattern.compile(MATCH_PING_IP);
    Pattern patternTime = Pattern.compile(MATCH_PING_TIME);

    Process process = null;
    BufferedReader reader = null;
    boolean finish = false;
    try {
      // 通过ping的跳数控制，取得相应跳输的ip地址，然后再次执行ping命令读取时间
      while (!finish && trace.getHop() < 30) {
        // 先发出ping命令获得某个跳数的ip地址
        String str = "";
        // -c 1 同时发送消息次数 －t是指跳数
        String command = "ping -c 1 -t " + trace.getHop() + " "
            + trace.getHost();

        process = Runtime.getRuntime().exec(command);
        reader = new BufferedReader(new InputStreamReader(
            process.getInputStream()));
        String line = null;
        while ((line = reader.readLine()) != null) {
          str += line;
        }
        reader.close();
        process.waitFor();

        Matcher m = patternTrace.matcher(str);

        // 如果成功获得trace:IP，则再次发送ping命令获取ping的时间
        StringBuilder log = new StringBuilder(256);
        if (m.find()) {
          String pingIp = m.group();
          PingTask pingTask = new PingTask(pingIp);

          String status = execPing(pingTask);
          if (status.length() == 0) {
            log.append("unknown host or network error\n");
            finish = true;
          } else {
            Matcher matcherTime = patternTime.matcher(status);
            if (matcherTime.find()) {
              String time = matcherTime.group();
              log.append(trace.getHop());
              log.append("\t\t");
              log.append(pingIp);
              log.append("\t\t");
              log.append(time);
              log.append("\t");
            } else {
              log.append(trace.getHop());
              log.append("\t\t");
              log.append(pingIp);
              log.append("\t\t timeout \t");
            }
            listener.OnNetTraceUpdated(log.toString());
            trace.setHop(trace.getHop() + 1);
          }

        }

        // 否则：what
        else {
          Matcher matchPingIp = patternIp.matcher(str);
          if (matchPingIp.find()) {
            String pingIp = matchPingIp.group();
            Matcher matcherTime = patternTime.matcher(str);
            if (matcherTime.find()) {
              String time = matcherTime.group();
              log.append(trace.getHop());
              log.append("\t\t");
              log.append(pingIp);
              log.append("\t\t");
              log.append(time);
              log.append("\t");
              listener.OnNetTraceUpdated(log.toString());
            }
            finish = true;
          } else {
            if (str.length() == 0) {
              log.append("unknown host or network error\t");
              finish = true;
            } else {
              log.append(trace.getHop());
              log.append("\t\t timeout \t");
              trace.setHop(trace.getHop() + 1);
            }
            listener.OnNetTraceUpdated(log.toString());
          }
        }// else no match traceIPPattern
      }// while
    } catch (IOException e) {
      e.printStackTrace();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      try {
        if (reader != null) {
          reader.close();
        }
        process.destroy();
      } catch (Exception e) {
      }
    }

    listener.OnNetTraceFinished();
  }

  /**
   * Ping任务
   * 
   * @author panghui
   * 
   */
  private class PingTask {

    private String host;
    private static final String MATCH_PING_HOST_IP = "(?<=\\().*?(?=\\))";

    public String getHost() {
      return host;
    }

    public PingTask(String host) {
      super();
      this.host = host;
      Pattern p = Pattern.compile(MATCH_PING_HOST_IP);
      Matcher m = p.matcher(host);
      if (m.find()) {
        this.host = m.group();
      }

    }
  }

  /**
   * 生成trace任务
   * 
   * @author panghui
   * 
   */
  private class TraceTask {
    private final String host;
    private int hop;

    public TraceTask(String host, int hop) {
      super();
      this.host = host;
      this.hop = hop;
    }

    public String getHost() {
      return host;
    }

    public int getHop() {
      return hop;
    }

    public void setHop(int hop) {
      this.hop = hop;
    }
  }
}
