/*
   * Creates an endpoint with the wildcard adress (::0) and an ephemeral port.
   * The new endpoint gets a client message deliverer and is started.
   * To listen on specific interfaces or ports, set the default endpoint manually.
   * To distinguish different interfaces, one endpoint per interface must be added.
   */
  private synchronized void createDefaultEndpoint() {
    if (default_endpoint != null) return;

    default_endpoint = new CoAPEndpoint();

    try {
      default_endpoint.start();
      LOGGER.log(Level.INFO, "Created implicit default endpoint " + default_endpoint.getAddress());
    } catch (IOException e) {
      LOGGER.log(Level.SEVERE, "Could not create default endpoint", e);
    }
  }
  /**
   * Configures a new default secure endpoint. Any old default endpoint is destroyed.
   *
   * @param endpoint the new default endpoint
   */
  public void setDefaultSecureEndpoint(Endpoint endpoint) {

    if (this.default_secure_endpoint != null) {
      this.default_secure_endpoint.destroy();
    }

    this.default_secure_endpoint = endpoint;

    if (!this.default_secure_endpoint.isStarted()) {
      try {
        default_secure_endpoint.start();
        LOGGER.log(
            Level.INFO, "Started new default secure endpoint " + default_endpoint.getAddress());
      } catch (IOException e) {
        LOGGER.log(Level.SEVERE, "Could not start new default secure endpoint", e);
      }
    }
  }
  /**
   * Configures a new default endpoint. Any old default endpoint is destroyed.
   *
   * @param endpoint the new default endpoint
   */
  public void setDefaultEndpoint(Endpoint endpoint) {

    if (this.default_endpoint != null) {
      this.default_endpoint.destroy();
    }

    LOGGER.config(endpoint.getAddress() + " becomes default endpoint");

    this.default_endpoint = endpoint;

    if (!this.default_endpoint.isStarted()) {
      try {
        default_endpoint.start();
      } catch (IOException e) {
        LOGGER.log(Level.SEVERE, "Could not start new default endpoint", e);
      }
    }
  }
    @Override
    public void run() {
      Logger.debug("ArpReader started ...");

      mNetBiosMap.clear();
      mStopped = false;
      String iface = "";
      ArrayList<Target> foundTargets = new ArrayList<Target>();

      try {
        iface = System.getNetwork().getInterface().getDisplayName();
      } catch (Exception e) {
        System.errorLogging(e);
      }

      while (!mStopped) {
        try {
          BufferedReader reader = new BufferedReader(new FileReader(ARP_TABLE_FILE));
          String line = null, name = null;
          Matcher matcher = null;
          Endpoint endpoint = null;
          Target target = null;
          Network network = System.getNetwork();

          foundTargets.clear();

          while ((line = reader.readLine()) != null) {
            if ((matcher = ARP_TABLE_PARSER.matcher(line)) != null && matcher.find()) {
              String address = matcher.group(1),
                  // hwtype  = matcher.group( 2 ),
                  flags = matcher.group(3),
                  hwaddr = matcher.group(4),
                  // mask	   = matcher.group( 5 ),
                  device = matcher.group(6);

              if (device.equals(iface)
                  && !hwaddr.equals("00:00:00:00:00:00")
                  && flags.contains("2")) {
                endpoint = new Endpoint(address, hwaddr);
                target = new Target(endpoint);
                foundTargets.add(target);
                // rescanning the gateway could cause an issue when the gateway itself has multiple
                // interfaces ( LAN, WAN ... )
                if (!endpoint.getAddress().equals(network.getGatewayAddress())
                    && !endpoint.getAddress().equals(network.getLocalAddress())) {
                  synchronized (mNetBiosMap) {
                    name = mNetBiosMap.get(address);
                  }

                  if (name == null) {
                    try {
                      mExecutor.execute(new NBResolver(address));
                    } catch (RejectedExecutionException e) {
                      // ignore since this is happening because the executor was shut down.
                    } catch (OutOfMemoryError m) {
                      // wait until the thread queue gets freed
                      break;
                    }

                    if (!target.isRouter()) {
                      // attempt DNS resolution
                      name = endpoint.getAddress().getHostName();

                      if (!name.equals(address)) {
                        Logger.debug(address + " was DNS resolved to " + name);

                        synchronized (mNetBiosMap) {
                          mNetBiosMap.put(address, name);
                        }
                      } else name = null;
                    }
                  }

                  if (!System.hasTarget(target)) sendNewEndpointNotification(endpoint, name);
                  else if (name != null) {
                    target = System.getTargetByAddress(address);
                    if (target != null && !target.hasAlias()) {
                      target.setAlias(name);
                      sendEndpointUpdateNotification();
                    }
                  }
                }
              }
            }
          }

          reader.close();

          boolean update = false;
          boolean found;
          int i;
          Target[] currentTargets =
              System.getTargets().toArray(new Target[System.getTargets().size()]);

          for (Target t : currentTargets) {

            endpoint = t.getEndpoint();

            if (endpoint == null) continue;
            for (found = false, i = 0; i < foundTargets.size() && !found; i++) {
              if (endpoint.equals(foundTargets.get(i).getEndpoint())) found = true;
            }

            if (t.isConnected() != found
                && !t.isRouter()
                && !t.getAddress().equals(network.getLocalAddress())) {
              t.setConneced(found);
              update = true;
            }
          }

          if (update) sendEndpointUpdateNotification();
          Thread.sleep(500);
        } catch (Exception e) {
          System.errorLogging(e);
          String msg = e.getMessage();
          if (msg != null && msg.contains("EMFILE")) {
            try {
              Shell.exec(
                  "lsof | grep " + android.os.Process.myPid(),
                  new Shell.OutputReceiver() {
                    @Override
                    public void onStart(String command) {}

                    @Override
                    public void onNewLine(String line) {
                      Logger.debug(line);
                    }

                    @Override
                    public void onEnd(int exitCode) {}
                  });
            } catch (Exception e1) {
              System.errorLogging(e1);
            }
          }
        }
      }
    }