content: package net.gcdc;
  
  import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.PrintStream;
  import java.net.InetSocketAddress;
  import java.net.SocketAddress;
  import java.net.SocketException;
  
  
  import net.gcdc.geonetworking.Address;
  import net.gcdc.geonetworking.BtpPacket;
  import net.gcdc.geonetworking.BtpSocket;
  import net.gcdc.geonetworking.LinkLayer;
  import net.gcdc.geonetworking.LinkLayerUdpToEthernet;
  import net.gcdc.geonetworking.LongPositionVector;
  import net.gcdc.geonetworking.Optional;
  import net.gcdc.geonetworking.Position;
  import net.gcdc.geonetworking.PositionProvider;
  import net.gcdc.geonetworking.StationConfig;
  import net.gcdc.geonetworking.gpsdclient.GpsdClient;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.threeten.bp.Instant;
  
  public final class BtpStdinClient {
  
      private final static String usage =
              "Usage: java -cp gn.jar StdinClient --local-port <local-port> --remote-address <udp-to-ethernet-remote-host-and-port> <--has-ethernet-header | --no-ethernet-header> <--position <lat>,<lon> | --gpsd-server <host>:<port>> --btp-destination-port <port>" + "\n" +
      "BTP ports: 2001 (CAM), 2002 (DENM), 2003 (MAP), 2004 (SPAT).";
      
      private BtpStdinClient(){}
  
      private   static  final Logger LOGGER  = LoggerFactory.getLogger(BtpUdpClient.class);
  
      public static void main(String[] args) throws IOException {
          if (args.length < 7) {
              System.err.println(usage);
              System.exit(1);
          }
  
          int localPort = 0;
          InetSocketAddress remoteAddress = null;
          boolean hasEthernetHeader = false;
          PositionProvider positionProvider = null;
          short btpDestinationPort = (short) 2001;  // CAM
  
          for (int arg = 0; arg < args.length; arg++) {
              if (args[arg].startsWith("--local-port")) {
                  arg++;
                  localPort = Integer.parseInt(args[arg]);
              } else if (args[arg].startsWith("--remote-address")) {
                  arg++;
                  String[] hostPort = args[arg].split(":");
                  if (hostPort.length != 2) {
                      System.err.println("Bad utoepy host:port.\n" + usage); System.exit(1);
                  }
                  remoteAddress = new InetSocketAddress(hostPort[0], Integer.parseInt(hostPort[1]));
              } else if (args[arg].startsWith("--has-ethernet-header")) {
                  hasEthernetHeader = true;
              } else if (args[arg].startsWith("--no-ethernet-header")) {
                  hasEthernetHeader = false;
              } else if (args[arg].startsWith("--position")) {
                  arg++;
                  String[] latLon = args[arg].split(",");
                  if (latLon.length != 2) { System.err.println("Bad lat,lon.\n" + usage); System.exit(1); }
                  final double lat = Double.parseDouble(latLon[0]);
                  final double lon = Double.parseDouble(latLon[1]);
                  final boolean isPositionConfident = true;  // Let's say we know it.
                  positionProvider = new PositionProvider() {
                      final Optional<Address> emptyAddress = Optional.empty();
                      @Override public LongPositionVector getLatestPosition() {
                          return new LongPositionVector(emptyAddress, Instant.now(),
                                  new Position(lat, lon), isPositionConfident, 0, 0);
                      }
                  };
              } else if (args[arg].startsWith("--gpsd-server")) {
                  arg++;
                  String[] hostPort = args[arg].split(":");
                  if (hostPort.length != 2) {
                      System.err.println("Bad gpsd host:port.\n" + usage); System.exit(1);
                  }
                  positionProvider = new GpsdClient(
                          new InetSocketAddress(hostPort[0], Integer.parseInt(hostPort[1])));
              } else if (args[arg].startsWith("--btp-destination-port")) {
                  arg++;
                  btpDestinationPort = Short.parseShort(args[arg]);
              } else {
                  throw new IllegalArgumentException("Unrecognized argument: " + args[arg]);
              }
          }
  
          runSenderAndReceiver(localPort, remoteAddress, hasEthernetHeader, System.in, System.out, positionProvider, btpDestinationPort);
      }
  
      public static void runSenderAndReceiver(
              final int localPort,
              final SocketAddress remoteAddress,
              final boolean hasEthernetHeader,
              final InputStream in,
              final PrintStream out,
              final PositionProvider positionProvider,
              final short btpDestinationPort
              ) throws SocketException {
  
          LinkLayer linkLayer = new LinkLayerUdpToEthernet(localPort, remoteAddress, hasEthernetHeader);
  
          StationConfig config = new StationConfig();
          final BtpSocket socket = BtpSocket.on(config, linkLayer, positionProvider);
  
          final Runnable sender = new Runnable() {
              @Override public void run() {
                  try (InputStreamReader isr = new InputStreamReader(in);
                          BufferedReader br = new BufferedReader(isr)){
                      String dataToSend = br.readLine();  // First line.
                      while(dataToSend != null) {
                          socket.send(BtpPacket.singleHop(dataToSend.getBytes(), btpDestinationPort));
                          dataToSend = br.readLine();  // Consecutive lines.
                      }
                  } catch (IOException e) {
                      LOGGER.info("socket sen error ", e.getMessage(), e);
                  }
              }
          };
  
          Runnable receiver = new Runnable() {
              @Override public void run() {
                  while(true) {
                      try {
                          BtpPacket packet = socket.receive();
                          out.println(new String(packet.payload()));
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
          };
  
          new Thread(sender).start();
          new Thread(receiver).start();
      }
  }
  