/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.cryptolib.elgamal.bean;

import static com.scytl.cryptolib.commons.utils.validations.Validate.inRange;
import static com.scytl.cryptolib.commons.utils.validations.Validate.notNull;

import java.math.BigInteger;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.commons.utils.serialization.AbstractJsonSerializable;
import com.scytl.cryptolib.elgamal.cryptoapi.EncryptionParameters;
import com.scytl.cryptolib.mathematical.groups.MathematicalGroup;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;

/**
 * Represents ElGamal Encryption parameters.
 */
@JsonRootName("encryptionParams")
@JsonIgnoreProperties({"group" })
public final class ElGamalEncryptionParameters
        extends AbstractJsonSerializable implements EncryptionParameters {
    private final BigInteger _p;

    private final BigInteger _q;

    private final BigInteger _g;

    private final ZpSubgroup _group;

    /**
     * Constructs an ElGamalEncryptionParameters, setting the received
     * parameters.
     * <p>
     * Note: the received parameters should meet the requirements below, if
     * these requirements are not met then the behavior of this class is not
     * guaranteed:
     * <ul>
     * <li>The p and q parameters are primes, and can be used as the valid p
     * (modulus) and q (order) parameters of a mathematical group.</li>
     * <li>The g parameter is group elements of the group defined by p and
     * q.</li>
     * </ul>
     * 
     * @param p
     *            the p (modulus) parameter.
     * @param q
     *            the q (order) parameter.
     * @param g
     *            the g (generator) parameter.
     * @throws GeneralCryptoLibException
     *             if any of the arguments are null.
     */
    @JsonCreator
    public ElGamalEncryptionParameters(@JsonProperty("p") BigInteger p,
            @JsonProperty("q") BigInteger q,
            @JsonProperty("g") BigInteger g)
            throws GeneralCryptoLibException {
        notNull(p, "Zp subgroup p parameter");
        notNull(q, "Zp subgroup q parameter");
        notNull(g, "Zp subgroup generator");
        BigInteger pMinusOne = p.subtract(BigInteger.ONE);
        inRange(q, BigInteger.ONE, pMinusOne, "Zp subgroup q parameter",
            "", "Zp subgroup p parameter minus 1");
        inRange(g, BigInteger.valueOf(2), pMinusOne,
            "Zp subgroup generator", "",
            "Zp subgroup p parameter minus 1");
        _p = p;
        _q = q;
        _g = g;
        _group = new ZpSubgroup(_g, _p, _q);

    }

    /**
     * Deserializes the instance from a string in JSON format.
     * 
     * @param json
     *            the JSON
     * @return the instance
     * @throws GeneralCryptoLibException
     *             failed to deserialize the instance.
     */
    public static ElGamalEncryptionParameters fromJson(String json)
            throws GeneralCryptoLibException {
        return fromJson(json, ElGamalEncryptionParameters.class);
    }

    /**
     * Extracts {@link java.math.BigInteger} value of modulus (parameter p).
     * 
     * @return modulus (parameter p).
     */
    @JsonProperty("p")
    public BigInteger getP() {
        return _p;
    }

    /**
     * Extracts {@link java.math.BigInteger} value of order (parameter q).
     * 
     * @return order (parameter q).
     */
    @JsonProperty("q")
    public BigInteger getQ() {
        return _q;
    }

    /**
     * Extracts {@link java.math.BigInteger} value of {@link ZpGroupElement}
     * generator (parameter g).
     * 
     * @return generator (parameter g).
     */
    @JsonProperty("g")
    public BigInteger getG() {
        return _g;
    }

    @Override
    public MathematicalGroup<?> getGroup() {
        return _group;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((_g == null) ? 0 : _g.hashCode());
        result = prime * result + ((_p == null) ? 0 : _p.hashCode());
        result = prime * result + ((_q == null) ? 0 : _q.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        ElGamalEncryptionParameters other =
            (ElGamalEncryptionParameters) obj;
        if (_g == null) {
            if (other._g != null) {
                return false;
            }
        } else if (!_g.equals(other._g)) {
            return false;
        }
        if (_p == null) {
            if (other._p != null) {
                return false;
            }
        } else if (!_p.equals(other._p)) {
            return false;
        }
        if (_q == null) {
            if (other._q != null) {
                return false;
            }
        } else if (!_q.equals(other._q)) {
            return false;
        }
        return true;
    }
}
