package com.aphyr.riemann.client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Arrays;

import com.aphyr.riemann.Proto.Event;
import com.aphyr.riemann.Proto.Query;
import com.aphyr.riemann.Proto.Msg;

public abstract class AbstractRiemannClient {

  public static final int DEFAULT_PORT = 5555;

  protected final InetSocketAddress server;

  public AbstractRiemannClient(final InetSocketAddress server) {
    this.server = server;
  }

  public AbstractRiemannClient(final int port) throws UnknownHostException {
    this.server = new InetSocketAddress(InetAddress.getLocalHost(), port);
  }

  public AbstractRiemannClient() throws UnknownHostException {
    this(new InetSocketAddress(InetAddress.getLocalHost(), DEFAULT_PORT));
  }
 
  public EventDSL event() {
    return new EventDSL(this);
  }

  // Sends events and checks the server's response. Will throw IOException for
  // network failures, ServerError for error responses from Riemann. Returns
  // true if events acknowledged.
  public Boolean sendEventsWithAck(final Event... events) throws IOException, ServerError {
    validate(
        sendRecvMessage(
          Msg.newBuilder()
            .addAllEvents(Arrays.asList(events))
            .build()
        )
    );
    return true;
  }

  // Sends events in fire-and-forget fashion. Doesn't check server response,
  // swallows all exceptions silently. No guarantees on delivery.
  public void sendEvents(final Event... events) {
    try {
      sendMaybeRecvMessage(
         Msg.newBuilder()
          .addAllEvents(Arrays.asList(events))
          .build()
      );
    } catch (IOException e) {
      // Fuck it.
    }
  }

  public List<Event> query(String q) throws IOException, ServerError {
    Msg m = sendRecvMessage(Msg.newBuilder()
        .setQuery(
          Query.newBuilder().setString(q).build())
        .build());

    validate(m);

    return m.getEventsList();
  }

  public abstract void sendMessage(Msg message) throws IOException;

  public abstract Msg recvMessage() throws IOException;

  public abstract Msg sendRecvMessage(Msg message) throws IOException;

  public abstract Msg sendMaybeRecvMessage(Msg message) throws IOException;

  public abstract boolean isConnected();

  public abstract void connect() throws IOException;

  public abstract void disconnect() throws IOException;

  // Asserts that the message is OK; if not, throws a ServerError.
  public Msg validate(Msg message) throws IOException, ServerError {
    if (message.hasOk() && message.getOk() == false) {
      throw(new ServerError(message.getError()));
    }
    return message;
  } 
}

@@ -1765,24 +1765,731 @@
 %0A    %7D%0A  %7D%0A%0A
+  // Send an Exception event, with state %22error%22 and tagged%0A  // %22exception%22. The event will also be tagged with the exception class name.%0A  // Description includes the exception class and stack trace.%0A  public void sendException(String service, Throwable t) %7B%0A      final StringBuilder desc = new StringBuilder();%0A      desc.append(t.toString());%0A      desc.append(%22%5Cn%5Cn%22);%0A      for (StackTraceElement e : t.getStackTrace()) %7B%0A          desc.append(e);%0A          desc.append(%22%5Cn%22);%0A      %7D%0A%0A      event().service(service)%0A              .state(%22error%22)%0A              .tag(%22exception%22)%0A              .tag(t.getClass().getSimpleName())%0A              .description(desc.toString())%0A              .send();%0A  %7D%0A%0A
   public Lis

Add AbstractRiemannClient.sendException