/*
 * Copyright (c) eHealth
 */
package be.fgov.ehealth.etee.crypto.examples;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import be.fgov.ehealth.etee.crypto.decrypt.DataUnsealer;
import be.fgov.ehealth.etee.crypto.decrypt.UnsealedData;
import be.fgov.ehealth.etee.crypto.status.CryptoResult;
import be.fgov.ehealth.etee.crypto.status.NotificationError;
import be.fgov.ehealth.etee.crypto.status.NotificationWarning;
import be.fgov.ehealth.etee.crypto.utils.IoUtils;


/**
 * An example that illustrates the actions at Bob's (recipient) side in order to read and verify a large sealed data message from Alice
 * (sender).
 *
 * @author jeh
 */
public class UnsealBigMessage extends AbstractExample {

    private static final int ONE_SECOND = 1000;

    private static final Logger LOGGER = LoggerFactory.getLogger(UnsealBigMessage.class);

    @Test
    public final void unsealBigMessage() throws IOException {
        // 0. During initialisation, Bob creates his DataUnsealer that he can use to
        // decrypt and verify incoming sealed data.
        DataUnsealer bobsDataUnsealer = initUnsealing();

        // 1. Now Bob receives a large sealed message,
        // therefore the DataUnsealer streaming implementation should be used.
        InputStream sealedData = openSealedDataStream();

        // 2. Get an OutputStream where the unsealed data will be written to.
        OutputStream unsealedData = openUnsealedDataStream();

        // 3. Unseal the received message. If no decryption failure has occurred during unsealing,
        // the unsealed data is already written to the given output stream when the call to this method returns.
        long start = System.currentTimeMillis();
        CryptoResult<UnsealedData> result = bobsDataUnsealer.unseal(sealedData, unsealedData);
        long end = System.currentTimeMillis();
        long duration = (end - start) / ONE_SECOND;
        LOGGER.info("Time elapsed [s]: " + String.valueOf(duration));

        // 4. Process the result of the unseal operation
        if (result.getFatal() == null) { // 4.A. The decryption operation succeeded
            if (!result.hasErrors()) { // 4.A.A. There are no errors or failures
                // 4.A.A.1. Get the author
                LOGGER.info("From author: " + result.getData().getAuthenticationCert().getSubjectDN());

            } else { // 4.A.B. The data authenticity is not OK
                // 4.A.B.1. Get the DataAuthenticationErrors or DataAuthenticationFailures
                // and do your specific security failure or error processing
                // BEFORE reading the unsealed data (otherwise you will have an RuntimeException
                for (NotificationError error : result.getErrors()) {
                    // e.g.
                    LOGGER.error("Error: " + error);
                }
                for (NotificationWarning failure : result.getWarnings()) {
                    // e.g.
                    LOGGER.error("Failure: " + failure);
                }
            }
        } else { // 4.B the decryption failed, there is no decrypted data
            LOGGER.info("The message could not be unsealed, because:" + result.getFatal());
        }

    }

    private static OutputStream openUnsealedDataStream() {
        return IoUtils.openFileOutputStream("target/big_unsealed_message_from_alice_to_bob.msg");
    }

    /**
     * Method to abstract how Bob received the sealed data from Alice comes. (This is application specific.)
     *
     * @return the sealed data from Alice.
     */
    private static InputStream openSealedDataStream() {
        return Thread.currentThread().getContextClassLoader().getResourceAsStream("test_examples/big_sealed_message_from_alice_for_bob.msg");
    }
}
