/*
 * 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.tallying.api;

import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.ov.tallying.api.dto.output.TallyingOutput;
import com.scytl.products.ov.tallying.command.TallyingHolder;
import com.scytl.products.ov.tallying.commons.HashService;
import com.scytl.products.ov.tallying.commons.beans.ErrorCodes;
import com.scytl.products.ov.tallying.commons.beans.TalliedInvalidVotes;
import com.scytl.products.ov.tallying.commons.beans.TallyingError;
import com.scytl.products.ov.tallying.commons.exception.TallyingException;
import com.scytl.products.ov.tallying.commons.io.writer.SignResultWriter;
import com.scytl.products.ov.tallying.commons.io.writer.TalliedInvalidVotesWriter;
import com.scytl.products.ov.tallying.commons.io.writer.TalliedSuccessfulVotesWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The class in charge of the tallying process.
 */
public class TallyingServiceImpl implements TallyingService {

    private static final Logger LOG = LoggerFactory.getLogger("std");

    private final SignResultWriter signResultWriter;

    private final TallyingVotesService tallyingVotesService;

    private final HashService hashService;

    private final SecureLoggingWriter loggingWriter;

    
    public TallyingServiceImpl(final SignResultWriter signResultWriter, final HashService hashService,
                               final SecureLoggingWriter loggingWriter) {
        this.signResultWriter = signResultWriter;
        this.hashService = hashService;
        this.tallyingVotesService = new TallyingVotesService(loggingWriter);
        this.loggingWriter = loggingWriter;
    }

    @Override
    public void tallyValidVotes(final TallyingHolder holder, final TallyingOutput output) throws TallyingException {

        LOG.info("Starting to tally correct results, BallotBoxId " + holder.getBallotBoxId() + "...");

        tallyingVotesService.tally(holder, output);

        storeTally(holder, output);

        LOG.info("Tallying correct results finished, BallotBoxId " + holder.getBallotBoxId() + ".");

    }

    @Override
    public void tallyInvalidVotes(final TallyingHolder holder, final TallyingOutput output) throws TallyingException {

        LOG.info("Starting to tally invalid votes, BallotBoxId " + holder.getBallotBoxId() + "...");

        int numberOfFailedVotes = holder.getFailedValues().size();
        int numberOfAuditableVotes = holder.getAuditableValues().size();

        TalliedInvalidVotes talliedInvalidVotes = new TalliedInvalidVotes(numberOfAuditableVotes, numberOfFailedVotes);
        output.setTalliedInvalidVotes(talliedInvalidVotes);

        storeTalliedInvalidVotes(holder, output);
        LOG.info("Tallying invalid votes finished, BallotBoxId " + holder.getBallotBoxId() + ".");
    }

    private void storeTally(final TallyingHolder holder, final TallyingOutput tallyingOutput)
            throws TallyingException {
        try {
            TalliedSuccessfulVotesWriter talliedVotesWriter = new TalliedSuccessfulVotesWriter(signResultWriter,
                    hashService, loggingWriter);
            talliedVotesWriter.writeToFiles(tallyingOutput.getContestVotes(), holder);

        } catch (TallyingException e) {
            tallyingOutput.getErrors()
                .add(new TallyingError("Cannot store tally result: " + e.getMessage(), ErrorCodes.UNKNOWN_ERROR));
            throw e;
        }
    }

    private void storeTalliedInvalidVotes(final TallyingHolder holder, final TallyingOutput output)
        throws TallyingException {
        try {
            TalliedInvalidVotesWriter writer = new TalliedInvalidVotesWriter(signResultWriter, hashService,
                    loggingWriter);
            writer.writeToFiles(output.getTalliedInvalidVotes(), holder);

        } catch (TallyingException e) {
            output.getErrors()
                .add(new TallyingError("Cannot store tally result: " + e.getMessage(), ErrorCodes.UNKNOWN_ERROR));
            throw e;
        }
    }
}
