// 
// Decompiled by Procyon v0.6.0
// 

package io.sentry;

import io.sentry.exception.ExceptionMechanismException;
import org.jetbrains.annotations.TestOnly;
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicInteger;
import io.sentry.protocol.SentryStackFrame;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Deque;
import io.sentry.protocol.SentryStackTrace;
import java.util.ArrayList;
import io.sentry.protocol.SentryException;
import java.util.List;
import io.sentry.protocol.Mechanism;
import io.sentry.protocol.SentryThread;
import io.sentry.util.Objects;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public final class SentryExceptionFactory
{
    @NotNull
    private final SentryStackTraceFactory sentryStackTraceFactory;
    
    public SentryExceptionFactory(@NotNull final SentryStackTraceFactory sentryStackTraceFactory) {
        this.sentryStackTraceFactory = Objects.requireNonNull(sentryStackTraceFactory, "The SentryStackTraceFactory is required.");
    }
    
    @NotNull
    public List<SentryException> getSentryExceptionsFromThread(@NotNull final SentryThread thread, @NotNull final Mechanism mechanism, @NotNull final Throwable throwable) {
        final SentryStackTrace threadStacktrace = thread.getStacktrace();
        if (threadStacktrace == null) {
            return new ArrayList<SentryException>(0);
        }
        final List<SentryException> exceptions = new ArrayList<SentryException>(1);
        exceptions.add(this.getSentryException(throwable, mechanism, thread.getId(), threadStacktrace.getFrames(), true));
        return exceptions;
    }
    
    @NotNull
    public List<SentryException> getSentryExceptions(@NotNull final Throwable throwable) {
        return this.getSentryExceptions(this.extractExceptionQueue(throwable));
    }
    
    @NotNull
    private List<SentryException> getSentryExceptions(@NotNull final Deque<SentryException> exceptions) {
        return new ArrayList<SentryException>(exceptions);
    }
    
    @NotNull
    private SentryException getSentryException(@NotNull final Throwable throwable, @Nullable final Mechanism exceptionMechanism, @Nullable final Long threadId, @Nullable final List<SentryStackFrame> frames, final boolean snapshot) {
        final Package exceptionPackage = throwable.getClass().getPackage();
        final String fullClassName = throwable.getClass().getName();
        final SentryException exception = new SentryException();
        final String exceptionMessage = throwable.getMessage();
        final String exceptionClassName = (exceptionPackage != null) ? fullClassName.replace(exceptionPackage.getName() + ".", "") : fullClassName;
        final String exceptionPackageName = (exceptionPackage != null) ? exceptionPackage.getName() : null;
        if (frames != null && !frames.isEmpty()) {
            final SentryStackTrace sentryStackTrace = new SentryStackTrace(frames);
            if (snapshot) {
                sentryStackTrace.setSnapshot(true);
            }
            exception.setStacktrace(sentryStackTrace);
        }
        exception.setThreadId(threadId);
        exception.setType(exceptionClassName);
        exception.setMechanism(exceptionMechanism);
        exception.setModule(exceptionPackageName);
        exception.setValue(exceptionMessage);
        return exception;
    }
    
    @TestOnly
    @NotNull
    Deque<SentryException> extractExceptionQueue(@NotNull final Throwable throwable) {
        return this.extractExceptionQueueInternal(throwable, new AtomicInteger(-1), new HashSet<Throwable>(), new ArrayDeque<SentryException>(), null);
    }
    
    Deque<SentryException> extractExceptionQueueInternal(@NotNull final Throwable throwable, @NotNull final AtomicInteger exceptionId, @NotNull final HashSet<Throwable> circularityDetector, @NotNull final Deque<SentryException> exceptions, @Nullable String mechanismTypeOverride) {
        Throwable currentThrowable = throwable;
        int parentId = exceptionId.get();
        while (currentThrowable != null && circularityDetector.add(currentThrowable)) {
            boolean snapshot = false;
            final String mechanismType = (mechanismTypeOverride == null) ? "chained" : mechanismTypeOverride;
            Mechanism exceptionMechanism;
            Thread thread;
            if (currentThrowable instanceof ExceptionMechanismException) {
                final ExceptionMechanismException exceptionMechanismThrowable = (ExceptionMechanismException)currentThrowable;
                exceptionMechanism = exceptionMechanismThrowable.getExceptionMechanism();
                currentThrowable = exceptionMechanismThrowable.getThrowable();
                thread = exceptionMechanismThrowable.getThread();
                snapshot = exceptionMechanismThrowable.isSnapshot();
            }
            else {
                exceptionMechanism = new Mechanism();
                thread = Thread.currentThread();
            }
            final boolean includeSentryFrames = Boolean.FALSE.equals(exceptionMechanism.isHandled());
            final List<SentryStackFrame> frames = this.sentryStackTraceFactory.getStackFrames(currentThrowable.getStackTrace(), includeSentryFrames);
            final SentryException exception = this.getSentryException(currentThrowable, exceptionMechanism, thread.getId(), frames, snapshot);
            exceptions.addFirst(exception);
            if (exceptionMechanism.getType() == null) {
                exceptionMechanism.setType(mechanismType);
            }
            if (exceptionId.get() >= 0) {
                exceptionMechanism.setParentId(parentId);
            }
            final int currentExceptionId = exceptionId.incrementAndGet();
            exceptionMechanism.setExceptionId(currentExceptionId);
            final Throwable[] suppressed = currentThrowable.getSuppressed();
            if (suppressed != null && suppressed.length > 0) {
                for (final Throwable suppressedThrowable : suppressed) {
                    this.extractExceptionQueueInternal(suppressedThrowable, exceptionId, circularityDetector, exceptions, "suppressed");
                }
            }
            currentThrowable = currentThrowable.getCause();
            parentId = currentExceptionId;
            mechanismTypeOverride = null;
        }
        return exceptions;
    }
}
