/*
 * 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.products.ov.mixnet.commons.beans;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.builder.HashCodeBuilder;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.scytl.cryptolib.api.exceptions.CryptoLibException;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.bean.ElGamalComputationsValues;
import com.scytl.cryptolib.elgamal.cryptoapi.Ciphertext;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;

/**
 * A bean to encapsulate a ElGamal encrypted ballot information.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public final class ElGamalEncryptedBallot implements Ciphertext {

    private ZpGroupElement gamma;

    private List<ZpGroupElement> phis;

    private List<ZpGroupElement> elements;

    private boolean isPoisonPill;

    public ElGamalEncryptedBallot(final List<ZpGroupElement> elements) {

        validateInputs(elements);

        this.gamma = elements.get(0);
        this.phis = new ArrayList<>();
        for (int i = 1; i < elements.size(); i++) {
        	this.phis.add(elements.get(i));
        }
        this.elements = new ArrayList<>(elements);
    }

    private ElGamalEncryptedBallot(final boolean isPoisonPill) {
        this.isPoisonPill = isPoisonPill;
    }

    @Override
    public ZpGroupElement getGamma() {
        return gamma;
    }
    
    @Override
    public List<ZpGroupElement> getPhis() {
        return phis;
    }

    @Override
    public int hashCode() {

        HashCodeBuilder builder = new HashCodeBuilder();

        builder.append(gamma);
        phis.forEach(builder::append);

        return builder.toHashCode();
    }

    @Override
    public boolean equals(final Object obj) {

        if (obj instanceof ElGamalEncryptedBallot) {
            final ElGamalEncryptedBallot other = (ElGamalEncryptedBallot) obj;

            return (gamma.equals(other.getGamma())) && (phis.equals(other.getPhis()));
        } else {
            return false;
        }
    }

    private static void validateInputs(final List<ZpGroupElement> elements) {

        if ((elements == null) || elements.isEmpty()) {
            throw new IllegalArgumentException(
                "The list of group elements must be initialized and must contain at least 1 elements.");
        }
    }

    public ElGamalComputationsValues getValues() {
        ZpGroupElement gammaValue = getGamma();
        List<ZpGroupElement> phisValue = getPhis();
        try {
            return new ElGamalComputationsValues(gammaValue, phisValue);
        } catch (GeneralCryptoLibException e) {
            throw new CryptoLibException("Exception while trying to obtain ElGamal values: " + e.getMessage(), e);
        }
    }

    @Override
    public String toString() {
        return "ElGamalEncryptedBallot [_gamma=" + gamma + ", _phis=" + phis + "]";
    }

    @Override
    public List<ZpGroupElement> getElements() {
        return elements;
    }

    public static ElGamalEncryptedBallot create(final List<ZpGroupElement> elements) {
        return new ElGamalEncryptedBallot(elements);
    }

    public static ElGamalEncryptedBallot poisonPill() {
        return new ElGamalEncryptedBallot(true);
    }

    public boolean isPoisonPill() {
        return isPoisonPill;
    }
}
