package io.scalecube.services.api;


import static java.util.Objects.requireNonNull;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public final class ServiceMessage {

  /**
   * This header is supposed to be used by application in case if same data type can be reused for several messages so
   * it will allow to qualify the specific message type.
   */
  public static final String HEADER_QUALIFIER = "q";

  /**
   * This is a system header which used by transport for serialization and deserialization purpose. It is not supposed
   * to be used by application directly and it is subject to changes in future releases.
   */
  public static final String HEADER_DATA_TYPE = "_type";

  private Map<String, String> headers = Collections.emptyMap();
  private Object data;

  /**
   * Instantiates empty message for deserialization purpose.
   */
  ServiceMessage() {}

  private ServiceMessage(Builder builder) {
    this.data = builder.data();
    this.headers = builder.headers();
  }

  /**
   * Instantiates a new message with the given data and without headers.
   * 
   * @param data the data to build a message from
   * @return the built message
   */
  public static ServiceMessage fromData(Object data) {
    return withData(data).build();
  }

  /**
   * Instantiates a new message builder with the given data and without headers.
   * 
   * @param data the initial data for the builder
   * @return a builder with initial data
   */
  public static Builder withData(Object data) {
    return builder().data(data);
  }


  /**
   * Instantiates a new message with the given headers and with empty data.
   * 
   * @param headers an initial headers to build a message from
   * @return the built message
   */
  public static ServiceMessage fromHeaders(Map<String, String> headers) {
    return withHeaders(headers).build();
  }

  /**
   * Instantiates a new message builder with the given headers and with empty data.
   * 
   * @param headers the initial headers for the builder
   * @return a builder with initial headers
   */
  public static Builder withHeaders(Map<String, String> headers) {
    return builder().headers(headers);
  }

  /**
   * Instantiates a new message with the given qualifier header and with empty data.
   * 
   * @param qualifier the qualifier to build a message from
   * @return the built message
   */
  public static ServiceMessage fromQualifier(String qualifier) {
    return withQualifier(qualifier).build();
  }

  /**
   * Instantiates a new message builder with the given qualifier header and with empty data.
   * 
   * @param qualifier the initial qualifier for the builder
   * @return a builder with initial qualifier
   */
  public static Builder withQualifier(String qualifier) {
    return builder().qualifier(qualifier);
  }

  /**
   * Instantiates new message with the same data and headers as at given message.
   * 
   * @param message the message to be copied
   * @return a new message, with the same data and headers
   */
  public static Builder from(ServiceMessage message) {
    return with(message);
  }

  /**
   * Instantiates new message builder with the same data and headers as at given message.
   * 
   * @param message the message to instantiate the new builder from
   * @return a builder with initial data and headers from the message
   */
  public static Builder with(ServiceMessage message) {
    return withData(message.data).headers(message.headers);
  }

  /**
   * Instantiates new empty message builder.
   *
   * @return new builder
   */
  public static Builder builder() {
    return Builder.getInstance();
  }

  /**
   * Sets data for deserialization purpose.
   * 
   * @param data data to set
   */
  void setData(Object data) {
    this.data = data;
  }

  /**
   * Sets headers for deserialization purpose.
   * 
   * @param headers headers to set
   */
  void setHeaders(Map<String, String> headers) {
    requireNonNull(headers != null);
    this.headers = Collections.unmodifiableMap(headers);
  }

  /**
   * Returns the message headers.
   *
   * @return message headers
   */
  public Map<String, String> headers() {
    return headers;
  }

  /**
   * Returns header value by given header name.
   * 
   * @param name header name
   * @return the message header by given header name
   */
  public String header(String name) {
    return headers.get(name);
  }

  /**
   * Returns message qualifier.
   * 
   * @return qualifier string
   */
  public String qualifier() {
    return header(HEADER_QUALIFIER);
  }

  /**
   * Return the message data, which can be byte array, string or any type.
   *
   * @param <T> data type
   * @return payload of the message or null if message is without any payload
   */
  public <T> T data() {
    // noinspection unchecked
    return (T) data;
  }

  @Override
  public String toString() {
    return "ServiceMessage {headers: " + headers + ", data: " + data + '}';
  }

  public static class Builder {

    private Map<String, String> headers = new HashMap<>();
    private Object data;

    private Builder() {}

    static Builder getInstance() {
      return new Builder();
    }

    private Object data() {
      return this.data;
    }

    public Builder data(Object data) {
      this.data = data;
      return this;
    }

    private Map<String, String> headers() {
      return this.headers;
    }

    public Builder headers(Map<String, String> headers) {
      this.headers.putAll(headers);
      return this;
    }

    public Builder header(String key, String value) {
      headers.put(key, value);
      return this;
    }

    public Builder qualifier(String qualifier) {
      return header(HEADER_QUALIFIER, qualifier);
    }

    public ServiceMessage build() {
      return new ServiceMessage(this);
    }

    public Builder qualifier(String serviceName, String methodName) {
      return this.qualifier(Qualifier.fromString(serviceName + "/" + methodName).asString());
    }
  }
}

@@ -1046,2405 +1046,317 @@
 tes 
-a new message with the given data and without headers.%0A   * %0A   * @param data the data to build a message from%0A   * @return the built message%0A   */%0A  public static ServiceMessage fromData(Object data) %7B%0A    return withData(data).build();%0A  %7D%0A%0A  /**%0A   * Instantiates a new message builder with the given data and without headers.%0A   * %0A   * @param data the initial data for the builder%0A   * @return a builder with initial data%0A   */%0A  public static Builder withData(Object data) %7B%0A    return builder().data(data);%0A  %7D%0A%0A%0A  /**%0A   * Instantiates a new message with the given headers and with empty data.%0A   * %0A   * @param headers an initial headers to build a message from%0A   * @return the built message%0A   */%0A  public static ServiceMessage fromHeaders(Map%3CString, String%3E headers) %7B%0A    return withHeaders(headers).build();%0A  %7D%0A%0A  /**%0A   * Instantiates a new message builder with the given headers and with empty data.%0A   * %0A   * @param headers the initial headers for the builder%0A   * @return a builder with initial headers%0A   */%0A  public static Builder withHeaders(Map%3CString, String%3E headers) %7B%0A    return builder().headers(headers);%0A  %7D%0A%0A  /**%0A   * Instantiates a new message with the given qualifier header and with empty data.%0A   * %0A   * @param qualifier the qualifier to build a message from%0A   * @return the built message%0A   */%0A  public static ServiceMessage fromQualifier(String qualifier) %7B%0A    return withQualifier(qualifier).build();%0A  %7D%0A%0A  /**%0A   * Instantiates a new message builder with the given qualifier header and with empty data.%0A   * %0A   * @param qualifier the initial qualifier for the builder%0A   * @return a builder with initial qualifier%0A   */%0A  public static Builder withQualifier(String qualifier) %7B%0A    return builder().qualifier(qualifier);%0A  %7D%0A%0A  /**%0A   * Instantiates new message with the same data and headers as at given message.%0A   * %0A   * @param message the message to be copied%0A   * @return a new message, with the same data and headers%0A   */%0A  public static Builder from(ServiceMessage message) %7B%0A    return with(message);%0A  %7D%0A%0A  /**%0A   * Instantiates new message builder with the same data and headers as at given message.%0A   * %0A   * @param message the message to instantiate the new builder from%0A   * @return a builder with initial data and headers from the message%0A   */%0A  public static Builder with(ServiceMessage message) %7B%0A    return withData(message.data)
+new message with the same data and headers as at given message.%0A   * %0A   * @param message the message to be copied%0A   * @return a new message, with the same data and headers%0A   */%0A  public static Builder from(ServiceMessage message) %7B%0A    return ServiceMessage.builder()%0A        .data(message.data())%0A        
 .hea
@@ -1371,24 +1371,26 @@
 sage.headers
+()
 );%0A  %7D%0A%0A  /*

remove this confusing from and with stuff