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

package io.sentry;

import java.util.Iterator;
import java.nio.ByteOrder;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.LinkedHashMap;
import io.sentry.clientreport.ClientReport;
import io.sentry.protocol.profiling.SentryProfile;
import java.io.File;
import io.sentry.vendor.Base64;
import io.sentry.exception.SentryEnvelopeException;
import io.sentry.util.FileUtils;
import io.sentry.util.JsonSerializationUtils;
import io.sentry.protocol.SentryTransaction;
import java.io.Reader;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Writer;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import io.sentry.util.Objects;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.Callable;
import java.nio.charset.Charset;
import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public final class SentryEnvelopeItem
{
    private static final long MAX_PROFILE_CHUNK_SIZE = 52428800L;
    private static final Charset UTF_8;
    private final SentryEnvelopeItemHeader header;
    @Nullable
    private final Callable<byte[]> dataFactory;
    @Nullable
    private byte[] data;
    
    SentryEnvelopeItem(@NotNull final SentryEnvelopeItemHeader header, final byte[] data) {
        this.header = Objects.requireNonNull(header, "SentryEnvelopeItemHeader is required.");
        this.data = data;
        this.dataFactory = null;
    }
    
    SentryEnvelopeItem(@NotNull final SentryEnvelopeItemHeader header, @Nullable final Callable<byte[]> dataFactory) {
        this.header = Objects.requireNonNull(header, "SentryEnvelopeItemHeader is required.");
        this.dataFactory = Objects.requireNonNull(dataFactory, "DataFactory is required.");
        this.data = null;
    }
    
    @NotNull
    public byte[] getData() throws Exception {
        if (this.data == null && this.dataFactory != null) {
            this.data = this.dataFactory.call();
        }
        return this.data;
    }
    
    @NotNull
    public SentryEnvelopeItemHeader getHeader() {
        return this.header;
    }
    
    @NotNull
    public static SentryEnvelopeItem fromSession(@NotNull final ISerializer serializer, @NotNull final Session session) throws IOException {
        Objects.requireNonNull(serializer, "ISerializer is required.");
        Objects.requireNonNull(session, "Session is required.");
        final CachedItem cachedItem = new CachedItem(() -> {
            try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
                new BufferedWriter(new OutputStreamWriter(stream, SentryEnvelopeItem.UTF_8));
                final BufferedWriter bufferedWriter;
                try (final Writer writer = bufferedWriter) {
                    serializer.serialize(session, writer);
                    stream.toByteArray();
                    return;
                }
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.Session, () -> cachedItem.getBytes().length, "application/json", null);
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    @Nullable
    public SentryEvent getEvent(@NotNull final ISerializer serializer) throws Exception {
        if (this.header == null || this.header.getType() != SentryItemType.Event) {
            return null;
        }
        try (final Reader eventReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(this.getData()), SentryEnvelopeItem.UTF_8))) {
            return serializer.deserialize(eventReader, SentryEvent.class);
        }
    }
    
    @NotNull
    public static SentryEnvelopeItem fromEvent(@NotNull final ISerializer serializer, @NotNull final SentryBaseEvent event) {
        Objects.requireNonNull(serializer, "ISerializer is required.");
        Objects.requireNonNull(event, "SentryEvent is required.");
        final CachedItem cachedItem = new CachedItem(() -> {
            try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
                new BufferedWriter(new OutputStreamWriter(stream, SentryEnvelopeItem.UTF_8));
                final BufferedWriter bufferedWriter;
                try (final Writer writer = bufferedWriter) {
                    serializer.serialize(event, writer);
                    stream.toByteArray();
                    return;
                }
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.resolve(event), () -> cachedItem.getBytes().length, "application/json", null);
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    @Nullable
    public SentryTransaction getTransaction(@NotNull final ISerializer serializer) throws Exception {
        if (this.header == null || this.header.getType() != SentryItemType.Transaction) {
            return null;
        }
        try (final Reader eventReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(this.getData()), SentryEnvelopeItem.UTF_8))) {
            return serializer.deserialize(eventReader, SentryTransaction.class);
        }
    }
    
    @Nullable
    public SentryLogEvents getLogs(@NotNull final ISerializer serializer) throws Exception {
        if (this.header == null || this.header.getType() != SentryItemType.Log) {
            return null;
        }
        try (final Reader eventReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(this.getData()), SentryEnvelopeItem.UTF_8))) {
            return serializer.deserialize(eventReader, SentryLogEvents.class);
        }
    }
    
    public static SentryEnvelopeItem fromUserFeedback(@NotNull final ISerializer serializer, @NotNull final UserFeedback userFeedback) {
        Objects.requireNonNull(serializer, "ISerializer is required.");
        Objects.requireNonNull(userFeedback, "UserFeedback is required.");
        final CachedItem cachedItem = new CachedItem(() -> {
            try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
                new BufferedWriter(new OutputStreamWriter(stream, SentryEnvelopeItem.UTF_8));
                final BufferedWriter bufferedWriter;
                try (final Writer writer = bufferedWriter) {
                    serializer.serialize(userFeedback, writer);
                    stream.toByteArray();
                    return;
                }
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.UserFeedback, () -> cachedItem.getBytes().length, "application/json", null);
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    public static SentryEnvelopeItem fromCheckIn(@NotNull final ISerializer serializer, @NotNull final CheckIn checkIn) {
        Objects.requireNonNull(serializer, "ISerializer is required.");
        Objects.requireNonNull(checkIn, "CheckIn is required.");
        final CachedItem cachedItem = new CachedItem(() -> {
            try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
                new BufferedWriter(new OutputStreamWriter(stream, SentryEnvelopeItem.UTF_8));
                final BufferedWriter bufferedWriter;
                try (final Writer writer = bufferedWriter) {
                    serializer.serialize(checkIn, writer);
                    stream.toByteArray();
                    return;
                }
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.CheckIn, () -> cachedItem.getBytes().length, "application/json", null);
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    public static SentryEnvelopeItem fromAttachment(@NotNull final ISerializer serializer, @NotNull final ILogger logger, @NotNull final Attachment attachment, final long maxAttachmentSize) {
        final CachedItem cachedItem = new CachedItem(() -> {
            if (attachment.getBytes() != null) {
                final byte[] data = attachment.getBytes();
                ensureAttachmentSizeLimit(data.length, maxAttachmentSize, attachment.getFilename());
                return data;
            }
            else {
                if (attachment.getSerializable() != null) {
                    final JsonSerializable serializable = attachment.getSerializable();
                    final byte[] data2 = JsonSerializationUtils.bytesFrom(serializer, logger, serializable);
                    if (data2 != null) {
                        ensureAttachmentSizeLimit(data2.length, maxAttachmentSize, attachment.getFilename());
                        return data2;
                    }
                }
                else if (attachment.getPathname() != null) {
                    return FileUtils.readBytesFromFile(attachment.getPathname(), maxAttachmentSize);
                }
                else if (attachment.getByteProvider() != null) {
                    final byte[] data3 = attachment.getByteProvider().call();
                    if (data3 != null) {
                        ensureAttachmentSizeLimit(data3.length, maxAttachmentSize, attachment.getFilename());
                        return data3;
                    }
                }
                new SentryEnvelopeException(String.format("Couldn't attach the attachment %s.\nPlease check that either bytes, serializable, path or provider is set.", attachment.getFilename()));
                throw;
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.Attachment, () -> cachedItem.getBytes().length, attachment.getContentType(), attachment.getFilename(), attachment.getAttachmentType());
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    private static void ensureAttachmentSizeLimit(final long size, final long maxAttachmentSize, @NotNull final String filename) throws SentryEnvelopeException {
        if (size > maxAttachmentSize) {
            throw new SentryEnvelopeException(String.format("Dropping attachment with filename '%s', because the size of the passed bytes with %d bytes is bigger than the maximum allowed attachment size of %d bytes.", filename, size, maxAttachmentSize));
        }
    }
    
    @NotNull
    public static SentryEnvelopeItem fromProfileChunk(@NotNull final ProfileChunk profileChunk, @NotNull final ISerializer serializer) throws SentryEnvelopeException {
        return fromProfileChunk(profileChunk, serializer, NoOpProfileConverter.getInstance());
    }
    
    @NotNull
    public static SentryEnvelopeItem fromProfileChunk(@NotNull final ProfileChunk profileChunk, @NotNull final ISerializer serializer, @NotNull final IProfileConverter profileConverter) throws SentryEnvelopeException {
        final File traceFile = profileChunk.getTraceFile();
        final CachedItem cachedItem = new CachedItem(() -> {
            if (!traceFile.exists()) {
                new SentryEnvelopeException(String.format("Dropping profile chunk, because the file '%s' doesn't exists", traceFile.getName()));
                throw;
            }
            else {
                Label_0147_3: {
                    if ("java".equals(profileChunk.getPlatform())) {
                        if (!NoOpProfileConverter.getInstance().equals(profileConverter)) {
                            try {
                                final SentryProfile profile = profileConverter.convertFromFile(traceFile.getAbsolutePath());
                                profileChunk.setSentryProfile(profile);
                                break Label_0147_3;
                            }
                            catch (final Exception e) {
                                throw new SentryEnvelopeException("Profile conversion failed", e);
                            }
                        }
                        throw new SentryEnvelopeException("No ProfileConverter available, dropping chunk.");
                    }
                    else {
                        final byte[] traceFileBytes = FileUtils.readBytesFromFile(traceFile.getPath(), 52428800L);
                        final String base64Trace = Base64.encodeToString(traceFileBytes, 3);
                        if (base64Trace.isEmpty()) {
                            throw new SentryEnvelopeException("Profiling trace file is empty");
                        }
                        else {
                            profileChunk.setSampledProfile(base64Trace);
                        }
                    }
                    try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
                        new BufferedWriter(new OutputStreamWriter(stream, SentryEnvelopeItem.UTF_8));
                        final BufferedWriter bufferedWriter;
                        try (final Writer writer = bufferedWriter) {
                            serializer.serialize(profileChunk, writer);
                            stream.toByteArray();
                            return;
                        }
                    }
                    catch (final IOException e2) {
                        new SentryEnvelopeException(String.format("Failed to serialize profile chunk\n%s", e2.getMessage()));
                        throw;
                    }
                    finally {
                        traceFile.delete();
                    }
                }
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.ProfileChunk, () -> cachedItem.getBytes().length, "application-json", traceFile.getName(), null, profileChunk.getPlatform(), null);
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    @NotNull
    public static SentryEnvelopeItem fromProfilingTrace(@NotNull final ProfilingTraceData profilingTraceData, final long maxTraceFileSize, @NotNull final ISerializer serializer) throws SentryEnvelopeException {
        final File traceFile = profilingTraceData.getTraceFile();
        final CachedItem cachedItem = new CachedItem(() -> {
            if (!traceFile.exists()) {
                new SentryEnvelopeException(String.format("Dropping profiling trace data, because the file '%s' doesn't exists", traceFile.getName()));
                throw;
            }
            else {
                final byte[] traceFileBytes = FileUtils.readBytesFromFile(traceFile.getPath(), maxTraceFileSize);
                final String base64Trace = Base64.encodeToString(traceFileBytes, 3);
                if (base64Trace.isEmpty()) {
                    throw new SentryEnvelopeException("Profiling trace file is empty");
                }
                else {
                    profilingTraceData.setSampledProfile(base64Trace);
                    profilingTraceData.readDeviceCpuFrequencies();
                    try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
                        new BufferedWriter(new OutputStreamWriter(stream, SentryEnvelopeItem.UTF_8));
                        final BufferedWriter bufferedWriter;
                        try (final Writer writer = bufferedWriter) {
                            serializer.serialize(profilingTraceData, writer);
                            stream.toByteArray();
                            return;
                        }
                    }
                    catch (final IOException e) {
                        new SentryEnvelopeException(String.format("Failed to serialize profiling trace data\n%s", e.getMessage()));
                        throw;
                    }
                    finally {
                        traceFile.delete();
                    }
                }
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.Profile, () -> cachedItem.getBytes().length, "application-json", traceFile.getName());
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    @NotNull
    public static SentryEnvelopeItem fromClientReport(@NotNull final ISerializer serializer, @NotNull final ClientReport clientReport) throws IOException {
        Objects.requireNonNull(serializer, "ISerializer is required.");
        Objects.requireNonNull(clientReport, "ClientReport is required.");
        final CachedItem cachedItem = new CachedItem(() -> {
            try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
                new BufferedWriter(new OutputStreamWriter(stream, SentryEnvelopeItem.UTF_8));
                final BufferedWriter bufferedWriter;
                try (final Writer writer = bufferedWriter) {
                    serializer.serialize(clientReport, writer);
                    stream.toByteArray();
                    return;
                }
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.resolve(clientReport), () -> cachedItem.getBytes().length, "application/json", null);
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    @Nullable
    public ClientReport getClientReport(@NotNull final ISerializer serializer) throws Exception {
        if (this.header == null || this.header.getType() != SentryItemType.ClientReport) {
            return null;
        }
        try (final Reader eventReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(this.getData()), SentryEnvelopeItem.UTF_8))) {
            return serializer.deserialize(eventReader, ClientReport.class);
        }
    }
    
    public static SentryEnvelopeItem fromReplay(@NotNull final ISerializer serializer, @NotNull final ILogger logger, @NotNull final SentryReplayEvent replayEvent, @Nullable final ReplayRecording replayRecording, final boolean cleanupReplayFolder) {
        final File replayVideo = replayEvent.getVideoFile();
        final CachedItem cachedItem = new CachedItem(() -> {
            try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
                new BufferedWriter(new OutputStreamWriter(stream, SentryEnvelopeItem.UTF_8));
                final BufferedWriter bufferedWriter;
                try (final Writer writer = bufferedWriter) {
                    final LinkedHashMap<String, byte[]> replayPayload = new LinkedHashMap<String, byte[]>();
                    serializer.serialize(replayEvent, writer);
                    replayPayload.put(SentryItemType.ReplayEvent.getItemType(), stream.toByteArray());
                    stream.reset();
                    if (replayRecording != null) {
                        serializer.serialize(replayRecording, writer);
                        replayPayload.put(SentryItemType.ReplayRecording.getItemType(), stream.toByteArray());
                        stream.reset();
                    }
                    if (replayVideo != null && replayVideo.exists()) {
                        final byte[] videoBytes = FileUtils.readBytesFromFile(replayVideo.getPath(), 10485760L);
                        if (videoBytes.length > 0) {
                            replayPayload.put(SentryItemType.ReplayVideo.getItemType(), videoBytes);
                        }
                    }
                    serializeToMsgpack(replayPayload);
                    return;
                }
            }
            catch (final Throwable t) {
                logger.log(SentryLevel.ERROR, "Could not serialize replay recording", t);
                return null;
            }
            finally {
                if (replayVideo != null) {
                    if (cleanupReplayFolder) {
                        FileUtils.deleteRecursively(replayVideo.getParentFile());
                    }
                    else {
                        replayVideo.delete();
                    }
                }
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.ReplayVideo, () -> cachedItem.getBytes().length, null, null);
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    public static SentryEnvelopeItem fromLogs(@NotNull final ISerializer serializer, @NotNull final SentryLogEvents logEvents) {
        Objects.requireNonNull(serializer, "ISerializer is required.");
        Objects.requireNonNull(logEvents, "SentryLogEvents is required.");
        final CachedItem cachedItem = new CachedItem(() -> {
            try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
                new BufferedWriter(new OutputStreamWriter(stream, SentryEnvelopeItem.UTF_8));
                final BufferedWriter bufferedWriter;
                try (final Writer writer = bufferedWriter) {
                    serializer.serialize(logEvents, writer);
                    stream.toByteArray();
                    return;
                }
            }
        });
        final SentryEnvelopeItemHeader itemHeader = new SentryEnvelopeItemHeader(SentryItemType.Log, () -> cachedItem.getBytes().length, "application/vnd.sentry.items.log+json", null, null, null, logEvents.getItems().size());
        return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes());
    }
    
    private static byte[] serializeToMsgpack(@NotNull final Map<String, byte[]> map) throws IOException {
        try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            baos.write((byte)(0x80 | map.size()));
            for (final Map.Entry<String, byte[]> entry : map.entrySet()) {
                final byte[] keyBytes = entry.getKey().getBytes(SentryEnvelopeItem.UTF_8);
                final int keyLength = keyBytes.length;
                baos.write(-39);
                baos.write((byte)keyLength);
                baos.write(keyBytes);
                final byte[] valueBytes = entry.getValue();
                final int valueLength = valueBytes.length;
                baos.write(-58);
                baos.write(ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(valueLength).array());
                baos.write(valueBytes);
            }
            return baos.toByteArray();
        }
    }
    
    static {
        UTF_8 = Charset.forName("UTF-8");
    }
    
    private static class CachedItem
    {
        @Nullable
        private byte[] bytes;
        @Nullable
        private final Callable<byte[]> dataFactory;
        
        public CachedItem(@Nullable final Callable<byte[]> dataFactory) {
            this.dataFactory = dataFactory;
        }
        
        @NotNull
        public byte[] getBytes() throws Exception {
            if (this.bytes == null && this.dataFactory != null) {
                this.bytes = this.dataFactory.call();
            }
            return orEmptyArray(this.bytes);
        }
        
        @NotNull
        private static byte[] orEmptyArray(@Nullable final byte[] bytes) {
            return (bytes != null) ? bytes : new byte[0];
        }
    }
}
