package com.gdh.parser;

import java.math.BigInteger;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;

import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import com.gdh.main.Constants;
import com.gdh.main.ExchangeState;
import com.gdh.main.Group;
import com.gdh.main.Node;

/**
 * 
 * JsonMessageParser is a class for parsing json messages
 * 
 * Parses two types of messages:
 * 
 * Messages relaying the public parameters of the group, e.g. large prime (N) and cyclic generator (g)
 * 
 * and messages relaying the partial keys computed during the key exchange.
 *
 * @author Max Amelchenko
 */
public class JsonMessageParser implements MessageParser {

    private final Map<Integer, Group> groupMappings;
    private final Map<Integer, ExchangeState> stateMappings;

    public JsonMessageParser(Map<Integer, Group> groupMappings, Map<Integer, ExchangeState> stateMappings) {
        this.groupMappings = groupMappings;
        this.stateMappings = stateMappings;
    }

    /**
     * @param msg
     *            the information to be parsed
     * @return the group id of the group contained in the message or error code
     */
    @Override
    public int parse(String msg) {
        if (msg.contains(Constants.ROUND))
            return extractRoundInfo(msg);
        return extractGroupInfo(msg);
    }

    /**
     * 
     * @param msg
     *            the group details in json format to be parsed
     * @return the group id of the group contained in the message
     *         -2 if this message has already been received
     */
    private int extractGroupInfo(String msg) {
        TreeSet<Node> set = new TreeSet<>();
        Group group = null;
        JsonObject obj = new JsonObject(msg);
        String prime = obj.getString(Constants.PRIME);
        String generator = obj.getString(Constants.GENERATOR);
        JsonArray members = obj.getJsonArray(Constants.MEMBERS);
        Iterator<?> iter = members.iterator();
        while (iter.hasNext()) {
            JsonObject member = (JsonObject) iter.next();
            String ip = member.getString(Constants.IP);
            String port = member.getString(Constants.PORT);
            Node n = new Node(ip, port);
            set.add(n);
        }
        group = new Group(generator, prime, set);
        if (stateMappings.containsKey(group.getGroupId()) && !stateMappings.get(group.getGroupId()).isDone())
            return -2; // message received twice
        group.setGenerator(new BigInteger(generator));
        group.setPrime(new BigInteger(prime));
        groupMappings.put(group.getGroupId(), group);
        stateMappings.put(group.getGroupId(), new ExchangeState(group.getGroupId(), group.getGenerator()));
        return group.getGroupId();
    }

    /**
     * Get the info about the current round of the key exchange. These include
     * the id of the group exchanging keys and the partial key computed by this
     * Node's counterpart. This partial key will be used by this Node for
     * further computation.
     * 
     * @param msg
     *            the round details in json format to be parsed
     * @return the group id of the group contained in the message 
     *         -1 if the state does not exist yet, which means the group info was not received yet 
     *         -2 if this message has already been received
     */
    private int extractRoundInfo(String msg) {
        JsonObject obj = new JsonObject(msg);
        String groupId = obj.getString(Constants.GROUPID);
        int ret = Integer.parseInt(groupId);
        String partial_key = obj.getString(Constants.PARTIAL_KEY);
        ExchangeState state = stateMappings.get(ret);
        String round = obj.getString(Constants.ROUND);
        if (state == null) // State does not exist
            return -1;
        if (state.getRound() == Integer.parseInt(round) - 1) // message received twice
            return -2;
        state.setPartial_key(new BigInteger(partial_key));
        return ret;
    }
}