package com.mobify.astro.messaging;

import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Iterator;

public class Message {
    private static final String TAG = Message.class.getName();

    protected static final class KeyNames {
        protected static final String PAYLOAD = "payload";
    }

    private JSONObject payload;
    private HashMap<String, String> headers = new HashMap<String, String>();

    //region Constructors

    public Message() {}
    public Message(JSONObject payload) {
        setPayload(payload);
    }

    //endregion

    //region Properties

    /**
     *
     * @param payload a JSONObject to set as the value of the "payload" key of this Message's JSON
     */
    public void setPayload(JSONObject payload) {
        this.payload = payload;
    }

    public JSONObject getPayload() {
        return this.payload;
    }

    public void setHeader(String name, String value) {
        // TODO: validate serializability here, rather than failing later
        headers.put(name, value);
    }

    public String getHeader(String name) {
        return headers.get(name);
    }

    /**
     * Checks to see if the message has the requested header
     * @param name String name of the header in question
     * @return true if the message has the header, false otherwise
     */
    public boolean hasHeader(String name) {
        return headers.containsKey(name);
    }

    //endregion

    @Override
    public String toString() {
        try {
            return toJSON().toString();
        } catch (Exceptions.HeaderSerializationException e) {
            // re-throw as an unchecked exception
            Exceptions.SerializationException f =  new Exceptions.SerializationException(this, e);
            Log.e(TAG, f.getMessage(), f);
            throw f;
        }
    }

    public JSONObject toJSON() throws Exceptions.HeaderSerializationException {
        JSONObject message = new JSONObject();

        for (String name : headers.keySet()) {
            String value = headers.get(name);
            try {
                message.put(name, value);
            } catch (JSONException e) {
                throw new Exceptions.HeaderSerializationException(this, e);
            }
        }

        try {
            message.put(KeyNames.PAYLOAD, payload);
        } catch (JSONException e) {
            // log and die violently, this should never happen
            Log.e(TAG, "Could not set payload on JSON: " + e);
            throw new Exceptions.PayloadSerializationException(this, payload, e);
        }
        return message;
    }

    /**
     * Escapes single and double quotes.
     * @return a string representation of the message with quotes escaped
     */
    public String toEscapedJSONString() {
        String messageString = toString();
        messageString = messageString.replaceAll("\\\"", "\\\\\"");
        messageString = messageString.replaceAll("'", "\\\\\\'");
        return messageString;
    }

    /**
     * Sets the payload on the message using the given serialized JSON object
     *
     * @param serializedMessage JSON serialized message with the desired payload
     */
    public void setPayloadFromJSON(JSONObject serializedMessage) {
        JSONObject payload = null;
        try {
            payload = serializedMessage.getJSONObject(Message.KeyNames.PAYLOAD);
        }  catch(JSONException ex) {
            Log.e(TAG, "Could not get payload from JSON: " + ex);
        }
        setPayload(payload);
    }

    /**
     * Takes the headers from the given serialized message and sets them on this message
     *
     * @param serializedMessage JSON serialized message with the desired headers
     */
    public void setHeadersFromJSON(JSONObject serializedMessage) {
        Iterator<String> iterator = serializedMessage.keys();
        while (iterator.hasNext()) {
            String name = iterator.next();
            if (name.equals(Message.KeyNames.PAYLOAD)) {
                continue;
            }

            String value = null;
            try {
                value = serializedMessage.getString(name);
            } catch(JSONException ex) {
                Log.e(TAG, String.format("Could not get header '%s' from JSON: %s", name, ex));
            }
            setHeader(name, value);
        }
    }
}
