package mirror;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Paths;
import java.util.concurrent.CountDownLatch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.rvesse.airline.Cli;
import com.github.rvesse.airline.annotations.Command;
import com.github.rvesse.airline.annotations.Option;
import com.github.rvesse.airline.builder.CliBuilder;
import com.github.rvesse.airline.help.Help;

import io.grpc.Channel;
import io.grpc.internal.ServerImpl;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;
import io.grpc.netty.NettyServerBuilder;
import mirror.MirrorGrpc.MirrorStub;
import mirror.tasks.ThreadBasedTaskFactory;

public class Mirror {

  private static final Logger log = LoggerFactory.getLogger(Mirror.class);

  static {
    LoggingConfig.init();
  }

  public static void main(String[] args) throws Exception {
    CliBuilder<Runnable> b = Cli.<Runnable> builder("mirror").withDescription("two-way, real-time sync of files across machines");
    b.withCommand(MirrorClientArgs.class);
    b.withCommand(MirrorServerArgs.class);
    b.withDefaultCommand(Help.class);
    b.build().parse(args).run();
  }

  @Command(name = "server", description = "starts a server for the remote client to connect to")
  public static class MirrorServerArgs implements Runnable {
    @Option(name = { "-p", "--post" }, description = "port to listen on", arity = 1)
    public int port;

    @Override
    public void run() {
      ServerImpl rpc = NettyServerBuilder.forPort(port).maxMessageSize(1073741824).addService(new MirrorServer()).build();
      try {
        rpc.start();
        log.info("Listening on " + port);
        rpc.awaitTermination();
      } catch (IOException e) {
        e.printStackTrace();
        System.exit(-1);
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
    }
  }

  @Command(name = "client", description = "two-way real-time sync")
  public static class MirrorClientArgs implements Runnable {
    @Option(name = { "-h", "--host" }, description = "host name of remote server to connect to", arity = 1)
    public String host;

    @Option(name = { "-p", "--post" }, description = "port remote server to connect to", arity = 1)
    public int port;

    @Option(name = { "-l", "--local-root" }, description = "path on the local side to sync, e.g. ~/code", arity = 1)
    public String localRoot;

    @Option(name = { "-r", "--remote-root" }, description = "path on the remote side to sync, e.g. ~/code", arity = 1)
    public String remoteRoot;

    @Override
    public void run() {
      try {
        Channel c = NettyChannelBuilder.forAddress(host, port).negotiationType(NegotiationType.PLAINTEXT).maxMessageSize(1073741824).build();
        MirrorStub stub = MirrorGrpc.newStub(c).withCompression("gzip");
        MirrorClient client = new MirrorClient(//
          Paths.get(localRoot),
          Paths.get(remoteRoot),
          new ThreadBasedTaskFactory(),
          new ConnectionDetector.Impl(),
          FileSystems.getDefault());
        client.startSession(stub);
        // dumb way of waiting until they hit control-c
        CountDownLatch cl = new CountDownLatch(1);
        cl.await();
      } catch (InterruptedException ie) {
        Thread.currentThread().interrupt();
      }
    }
  }

}

@@ -806,16 +806,64 @@
 .class);
+%0A  private static final int defaultPort = 49172;
 %0A%0A  stat
@@ -1480,28 +1480,42 @@
 isten on
-%22
 , 
-arity = 1
+default: %22 + defaultPort
 )%0A    pu
@@ -1519,32 +1519,46 @@
  public int port
+ = defaultPort
 ;%0A%0A    @Override
@@ -2222,35 +2222,24 @@
  connect to%22
-, arity = 1
 )%0A    public
@@ -2339,28 +2339,42 @@
 nnect to
-%22
 , 
-arity = 1
+default: %22 + defaultPort
 )%0A    pu
@@ -2386,16 +2386,30 @@
 int port
+ = defaultPort
 ;%0A%0A    @
@@ -2503,35 +2503,24 @@
 e.g. ~/code%22
-, arity = 1
 )%0A    public
@@ -2649,19 +2649,8 @@
 ode%22
-, arity = 1
 )%0A  

Use a default port of 49172.