package com.zepchat.core.transport.messaging;


import android.util.Log;
import com.zepchat.core.ZEPCore;
import com.zepchat.core.data.Message;
import com.zepchat.core.data.User;
import com.zepchat.core.transport.ZEPMessagingTransport;
import zutil.io.StringOutputStream;
import zutil.log.LogUtil;
import zutil.net.http.HttpHeaderParser;
import zutil.net.http.HttpPrintStream;
import zutil.net.threaded.ThreadedUDPNetwork;
import zutil.net.threaded.ThreadedUDPNetworkThread;
import zutil.parser.DataNode;
import zutil.parser.json.JSONParser;
import zutil.parser.json.JSONWriter;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by Ziver on 2014-11-06.
 */
public class JSONMessagingTransport extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread, ZEPMessagingTransport{
    private static final Logger logger = LogUtil.getLogger();
    public static final int PORT = 1901;

    private final HashMap<String, String> headers;
    private MessageListener msgListener;
    private HashMap<String, InetAddress> userAddresses;
    private User localUser;
    private ZEPCore core;

    public JSONMessagingTransport(ZEPCore core, User localUser) throws IOException{
        super( null, PORT );
        super.setThread( this );

        this.localUser = localUser;
        this.core = core;

        headers = new HashMap<String, String>();
        headers.put("Alias", localUser.getAlias());
        headers.put("PublicKey", localUser.getPublicKey());

        userAddresses = new HashMap<String, InetAddress>();
    }


    @Override
    public void setMessageListener(MessageListener msgListener) {
        this.msgListener = msgListener;
    }

    /**
     *
     * ********** Message ex:
     * {
     *      Message: {
     *          Source: "<SENDER_BASE64_PUBLIC_KEY>",
     *          Timestamp: <UNIX_TIMESTAMP>,
     *          Encryption: “none”|”rsa512”|”rsa1024”|”rsa2048”,
     *          Payload: "<PAYLOAD>",
     *          CRC: "<SIGNED_PAYLOAD_SHA1>"
     *      }
     * }
     */
    @Override
    public void sendMessage(User remoteUser, String message) {
        try {
            // Generate the JSON message
            DataNode msgNode = new DataNode(DataNode.DataType.Map);
            msgNode.set("Source", localUser.getPublicKey());
            msgNode.set("Timestamp", System.currentTimeMillis());
            msgNode.set("Encryption", "none");
            msgNode.set("Payload", message);
            msgNode.set("CRC", "0");
            DataNode root = new DataNode(DataNode.DataType.Map);
            root.set("Message", msgNode);
            String msg = JSONWriter.toString(root);

            logger.log(Level.FINEST, "Message: " + msg);
            byte[] data = msg.toString().getBytes();
            DatagramPacket packet = new DatagramPacket(
                    data, data.length,
                    userAddresses.get(remoteUser.getPublicKey()),
                    PORT);
            send(packet);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
        try {
            DataNode root = JSONParser.read(new String(packet.getData()));
            if(root != null) {
                if (root.get("Message") != null) {
                    DataNode msgNode = root.get("Message");
                    User user = core.getRemoteUser(msgNode.getString("Source"));
                    if(user == null){
                        user = new User(
                                "Unknown",
                                msgNode.getString("Source"));
                        userAddresses.put(user.getPublicKey(), packet.getAddress());
                    }
                    sendMessageAck(user, msgNode.getString("CRC"));
                    if(msgListener != null) {
                        Message msg = new Message(
                                user,
                                msgNode.getString("Payload"));
                        msgListener.newMessage(msg);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     *
     * ********** Message ex:
     * {
     *      Message-Ack: {
     *          Source: "<SENDER_BASE64_PUBLIC_KEY>",
     *          CRC: "<SIGNED_PAYLOAD_SHA1>"
     *      }
     * }
     */
    public void sendMessageAck(User remoteUser, String crc) {
        try {
            // Generate the JSON message
            DataNode ackNode = new DataNode(DataNode.DataType.Map);
            ackNode.set("Source", localUser.getPublicKey());
            ackNode.set("CRC", "0");
            DataNode root = new DataNode(DataNode.DataType.Map);
            root.set("Message", ackNode);
            String msg = JSONWriter.toString(root);

            logger.log(Level.FINEST, "Message-Ack: " + msg);
            byte[] data = msg.toString().getBytes();
            DatagramPacket packet = new DatagramPacket(
                    data, data.length,
                    userAddresses.get(remoteUser.getPublicKey()),
                    PORT);
            send(packet);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
