package de.dhbwstuttgart.server.packet;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.dhbwstuttgart.util.Logger;

/**
 * A wrapper for the packet to ensure correct serialization/deserialization and make it possible to detect the matching
 * packet type for deserialization.
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PacketContainer {

  // The jackson serializer / deserializer tool
  private static final ObjectMapper objectMapper = new ObjectMapper();

  /*
   * The available packet types. The one type that is represented in the JSON should always be the ONLY non-null value.
   * They have to be public (for the moment) to let jackson fill them in while deserializing
   */
  public ErrorPacket errorPacket = null;
  public MessagePacket messagePacket = null;
  public InvalidPacket invalidPacket = null;
  public UnifyRequestPacket unifyRequestPacket = null;
  public UnifyResultPacket unifyResultPacket = null;
  public DebugPacket debugPacket = null;
  public SetAutoclosePacket setAutoclosePacket = null;


  /**
   * Generate the JSON string for the given packet
   *
   * @param packet The packet to serialize
   * @return The JSON representation of the packet
   */
  public static String serialize(IPacket packet) throws JsonProcessingException {
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    PacketContainer container = new PacketContainer();

    if (packet instanceof ErrorPacket)
      container.errorPacket = (ErrorPacket) packet;
    else if (packet instanceof MessagePacket)
      container.messagePacket = (MessagePacket) packet;
    else if (packet instanceof UnifyRequestPacket)
      container.unifyRequestPacket = (UnifyRequestPacket) packet;
    else if (packet instanceof UnifyResultPacket)
      container.unifyResultPacket = (UnifyResultPacket) packet;
    else if (packet instanceof DebugPacket)
      container.debugPacket = (DebugPacket) packet;
    else if (packet instanceof SetAutoclosePacket)
      container.setAutoclosePacket = (SetAutoclosePacket) packet;
    // Add new packets here and in the deserialize method
    else
      throw new RuntimeException("Cannot map packet to any known packet class");

    return objectMapper.writeValueAsString(container);
  }

  /**
   * Use the JSON string to generate the matching packet object
   *
   * @param json The serialized representation of a packet container
   * @return The deserialized Packet object
   */
  public static IPacket deserialize(String json) {
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

    try {
      PacketContainer container = objectMapper.readValue(json, PacketContainer.class);

      if (container.errorPacket != null)
        return container.errorPacket;
      if (container.messagePacket != null)
        return container.messagePacket;
      if (container.invalidPacket != null)
        return container.invalidPacket;
      if (container.unifyRequestPacket != null)
        return container.unifyRequestPacket;
      if (container.unifyResultPacket != null)
        return container.unifyResultPacket;
      if (container.debugPacket != null)
        return container.debugPacket;
      if (container.setAutoclosePacket != null)
        return container.setAutoclosePacket;
      // Add new packets here and in the serialize method

      throw new RuntimeException("Cannot map received json to any known packet class");
    } catch (Exception e) {
      (new Logger()).exception(e);
      InvalidPacket packet = new InvalidPacket();
      packet.error = e.getMessage();
      return packet;
    }
  }


}
