/*
 * 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.channel.cc.commons.spring;

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

import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;

/**
 * Application exception to report {@link SpringApplication} errors with
 * appropriate exit code.
 */
@SuppressWarnings("serial")
public final class ApplicationException extends Exception
        implements ExitCodeGenerator {
    private static final int DEFAULT_EXIT_CODE = -1;

    private final int exitCode;

    /**
     * Constructor.
     * 
     * @param message
     *            the message
     */
    public ApplicationException(String message) {
        this(message, DEFAULT_EXIT_CODE);
    }

    /**
     * Constructor.
     * 
     * @param message
     *            the message
     * @param exitCode
     *            the exit code.
     */
    public ApplicationException(String message, int exitCode) {
        super(message);
        this.exitCode = exitCode;
    }

    /**
     * Constructor.
     * 
     * @param message
     *            the message
     * @param cause
     *            the cause
     */
    public ApplicationException(String message, Throwable cause) {
        this(message, cause, DEFAULT_EXIT_CODE);
    }

    /**
     * Constructor.
     * 
     * @param message
     *            the message
     * @param cause
     *            the cause
     * @param exitCode
     *            the exit code.
     */
    public ApplicationException(String message, Throwable cause,
            int exitCode) {
        super(message, cause);
        this.exitCode = exitCode;
    }

    /**
     * Constructor.
     * 
     * @param cause
     *            the cause
     */
    public ApplicationException(Throwable cause) {
        this(cause, DEFAULT_EXIT_CODE);
    }

    /**
     * Constructor.
     * 
     * @param cause
     *            the cause
     * @param exitCode
     *            the exit code.
     */
    public ApplicationException(Throwable cause, int exitCode) {
        super(cause);
        this.exitCode = exitCode;
    }

    private static void addRecursively(
            List<ApplicationException> exceptions,
            ApplicationException exception) {
        exceptions.add(exception);
        for (Throwable e : exception.getSuppressed()) {
            if (e instanceof ApplicationException) {
                addRecursively(exceptions, (ApplicationException) e);
            }
        }
    }

    @Override
    public int getExitCode() {
        return exitCode;
    }

    /**
     * Returns an array of {@link ApplicationException} which contains this one
     * with all the suppressed instances.
     * 
     * @return array of {@link ApplicationException}.
     */
    public ApplicationException[] toArray() {
        List<ApplicationException> exceptions = new ArrayList<>();
        addRecursively(exceptions, this);
        return exceptions
            .toArray(new ApplicationException[exceptions.size()]);
    }
}
