/*
 * 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.validation.decompress;

import com.scytl.products.ov.commons.beans.Ballot;
import com.scytl.products.ov.validation.commons.beans.BigIntegerFactor;
import com.scytl.products.ov.validation.commons.beans.Factorization;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * A class in charge of "decompress" numbers. As a inverse process of "compress", it consists on,
 * given an input number, return a list of prime numbers that the product of them are the input number.
 * For example:
 * for a compressedElement = 189, it returns factorization with factors {3^1,7^1,9^1} and remainder 1.
 * Where in 3^1: 3 is a base and 1 is an exponent (number of times 3 was found in 189).
 */
public class GroupElementDecompressor {

  public Factorization decompress(BigInteger compressedElement, Ballot ballot) {

    return decompress(compressedElement, obtainOptionRepresentations(ballot));
  }

  public Factorization decompress(BigInteger compressedValue,
      List<BigInteger> ballotOptionRepresentations) {

    List<BigIntegerFactor> factors = new ArrayList<>();
    BigInteger remainder = compressedValue;

    for (int i = 0; i < ballotOptionRepresentations.size() && !remainder.equals(BigInteger.ONE);
        i++) {

      BigInteger optionRepresentation = ballotOptionRepresentations.get(i);

      int exponent = 0;
      while (remainder.mod(optionRepresentation).compareTo(BigInteger.ZERO) == 0) {
        exponent++;
        remainder = remainder.divide(optionRepresentation);
      }
      if (exponent > 0) {
        factors.add(new BigIntegerFactor(optionRepresentation, exponent));
      }
    }

    return new Factorization(compressedValue, factors, remainder);
  }

  private List<BigInteger> obtainOptionRepresentations(Ballot ballot) {

    List<BigInteger> optionRepresentations = new ArrayList<>();

    ballot.getContests()
        .forEach(contest -> contest.getOptions().forEach(option -> optionRepresentations.add(
            new BigInteger(option.getRepresentation()))));

    return optionRepresentations;
  }
}
