package io.latent.storm.rabbitmq;

import backtype.storm.spout.Scheme;
import backtype.storm.tuple.Fields;

import java.util.*;

import backtype.storm.task.TopologyContext;
import java.io.Serializable;


public class RabbitMQMessageScheme implements MessageScheme {
  private final Scheme payloadScheme;
  private final List<String> fieldNames;


  public RabbitMQMessageScheme(Scheme payloadScheme, String envelopeFieldName, String propertiesFieldName) {
    this.payloadScheme = payloadScheme;

    List<String> payloadFieldNames = payloadScheme.getOutputFields().toList();

    this.fieldNames = new ArrayList<String>();
    fieldNames.addAll(payloadFieldNames);
    fieldNames.add(envelopeFieldName);
    fieldNames.add(propertiesFieldName);
  }

  @Override
  public void open(Map config, TopologyContext context) {
  }

  @Override
  public void close() {
  }

  @Override 
  public List<Object> deserialize(Message message) {
    Message.DeliveredMessage dm = (Message.DeliveredMessage)message;
    Envelope envelope = createEnvelope(dm);
    Properties properties = createProperties(dm);
    List<Object> payloadValues = deserialize(dm.getBody());

    List<Object> values = new ArrayList<Object>();
    values.addAll(payloadValues);
    values.add(envelope);
    values.add(properties);

    return values;
  }

  @Override
  public List<Object> deserialize(byte[] payload) {
    return payloadScheme.deserialize(payload);
  }

  @Override
  public Fields getOutputFields() {
    return new Fields(fieldNames);
  }

  private Envelope createEnvelope(Message.DeliveredMessage dm) {
    return new Envelope(dm.isRedelivery(), dm.getDeliveryTag(), dm.getExchange(), dm.getRoutingKey());
  }

  private Properties createProperties(Message.DeliveredMessage dm) {
    return new Properties(dm.getClassName(),
            dm.getClusterId(),
            dm.getContentEncoding(),
            dm.getContentType(),
            dm.getCorrelationId(),
            dm.getDeliveryMode(),
            dm.getExpiration(),
            serialiazableHeaders(dm.getHeaders()),
            dm.getMessageId(),
            dm.getPriority(),
            dm.getReplyTo(),
            dm.getTimestamp(),
            dm.getType(),
            dm.getUserId());
  }

  private Map<String, Object> serialiazableHeaders(Map<String, Object> headers) {
    if (headers == null) {
      return new HashMap<String, Object>();
    }
    Map<String, Object> serializableHeaders = new HashMap<String, Object>(headers.size());
    for (Map.Entry<String, Object> entry : headers.entrySet()) {
      if (entry.getValue() instanceof Number ||
          entry.getValue() instanceof Boolean ||
          entry.getValue() instanceof Character ||
          entry.getValue() instanceof String ||
          entry.getValue() instanceof Date) {
        serializableHeaders.put(entry.getKey(), entry.getValue());
      }
    }
    return serializableHeaders;
  }

  public static class Envelope implements Serializable {
    private final boolean isRedelivery;
    private final long deliveryTag;
    private final String exchange;
    private final String routingKey;

    Envelope(boolean isRedelivery, long deliveryTag, String exchange, String routingKey) {
      this.isRedelivery = isRedelivery;
      this.deliveryTag = deliveryTag;
      this.exchange = exchange;
      this.routingKey = routingKey;
    }

    public boolean isRedelivery() { return isRedelivery; }
    public long getDeliveryTag() { return deliveryTag; }
    public String getExchange() { return exchange; }
    public String getRoutingKey() { return routingKey; }
  }

  public static class Properties implements Serializable {
    private final String className;
    private final String clusterId;
    private final String contentEncoding;
    private final String contentType;
    private final String correlationId;
    private final Integer deliveryMode;
    private final String expiration;
    private final Map<String, Object> headers;
    private final String messageId;
    private final Integer priority;
    private final String replyTo;
    private final Date timestamp;
    private final String type;
    private final String userId;


    public Properties(String className,
                      String clusterId,
                      String contentEncoding,
                      String contentType,
                      String correlationId,
                      Integer deliveryMode,
                      String expiration,
                      Map<String, Object> headers,
                      String messageId,
                      Integer priority,
                      String replyTo,
                      Date timestamp,
                      String type,
                      String userId) {
      this.className = className;
      this.clusterId = clusterId;
      this.contentEncoding = contentEncoding;
      this.contentType = contentType;
      this.correlationId = correlationId;
      this.deliveryMode = deliveryMode;
      this.expiration = expiration;
      this.headers = headers;
      this.messageId = messageId;
      this.priority = priority;
      this.replyTo = replyTo;
      this.timestamp = timestamp;
      this.type = type;
      this.userId = userId;
    }

    public String getClassName() { return className; }
    public String getClusterId() { return clusterId; }
    public String getContentEncoding() { return contentEncoding; }
    public String getContentType() { return contentType; }
    public String getCorrelationId() { return correlationId; }
    public Integer getDeliveryMode() { return deliveryMode; }
    public String getExpiration() { return expiration; }
    public Map<String, Object> getHeaders() { return headers; }
    public String getMessageId() { return messageId; }
    public Integer getPriority() { return priority; }
    public String getReplyTo() { return replyTo; }
    public Date getTimestamp() { return timestamp; }
    public String getType() { return type; }
    public String getUserId() { return userId; }
  }
}


@@ -166,16 +166,56 @@
 ontext;%0A
+import com.rabbitmq.client.LongString;%0A%0A
 import j
@@ -2899,24 +2899,160 @@
 etValue());%0A
+      %7Delse if(entry.getValue() instanceof LongString)%7B%0A          serializableHeaders.put(entry.getKey(), entry.getValue().toString());%0A
       %7D%0A    
@@ -3296,24 +3296,31 @@
 ngKey;%0A%0A    
+public 
 Envelope(boo

Add support LongString