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

package io.sentry;

import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Collection;
import java.util.Calendar;
import java.util.Currency;
import java.util.UUID;
import java.net.InetAddress;
import java.net.URI;
import java.util.concurrent.atomic.AtomicBoolean;
import io.sentry.util.JsonSerializationUtils;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.Locale;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Set;
import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public final class JsonReflectionObjectSerializer
{
    private final Set<Object> visiting;
    private final int maxDepth;
    
    JsonReflectionObjectSerializer(final int maxDepth) {
        this.visiting = new HashSet<Object>();
        this.maxDepth = maxDepth;
    }
    
    @Nullable
    public Object serialize(@Nullable final Object object, @NotNull final ILogger logger) throws Exception {
        if (object == null) {
            return null;
        }
        if (object instanceof Character) {
            return object.toString();
        }
        if (object instanceof Number) {
            return object;
        }
        if (object instanceof Boolean) {
            return object;
        }
        if (object instanceof String) {
            return object;
        }
        if (object instanceof Locale) {
            return object.toString();
        }
        if (object instanceof AtomicIntegerArray) {
            return JsonSerializationUtils.atomicIntegerArrayToList((AtomicIntegerArray)object);
        }
        if (object instanceof AtomicBoolean) {
            return ((AtomicBoolean)object).get();
        }
        if (object instanceof URI) {
            return object.toString();
        }
        if (object instanceof InetAddress) {
            return object.toString();
        }
        if (object instanceof UUID) {
            return object.toString();
        }
        if (object instanceof Currency) {
            return object.toString();
        }
        if (object instanceof Calendar) {
            return JsonSerializationUtils.calendarToMap((Calendar)object);
        }
        if (object.getClass().isEnum()) {
            return object.toString();
        }
        if (this.visiting.contains(object)) {
            logger.log(SentryLevel.INFO, "Cyclic reference detected. Calling toString() on object.", new Object[0]);
            return object.toString();
        }
        this.visiting.add(object);
        if (this.visiting.size() > this.maxDepth) {
            this.visiting.remove(object);
            logger.log(SentryLevel.INFO, "Max depth exceeded. Calling toString() on object.", new Object[0]);
            return object.toString();
        }
        Object serializedObject = null;
        try {
            if (object.getClass().isArray()) {
                serializedObject = this.list((Object[])object, logger);
            }
            else if (object instanceof Collection) {
                serializedObject = this.list((Collection<?>)object, logger);
            }
            else if (object instanceof Map) {
                serializedObject = this.map((Map<?, ?>)object, logger);
            }
            else {
                final Map<String, Object> objectAsMap = this.serializeObject(object, logger);
                if (objectAsMap.isEmpty()) {
                    serializedObject = object.toString();
                }
                else {
                    serializedObject = objectAsMap;
                }
            }
        }
        catch (final Exception exception) {
            logger.log(SentryLevel.INFO, "Not serializing object due to throwing sub-path.", exception);
        }
        finally {
            this.visiting.remove(object);
        }
        return serializedObject;
    }
    
    @NotNull
    public Map<String, Object> serializeObject(@NotNull final Object object, @NotNull final ILogger logger) throws Exception {
        final Field[] fields = object.getClass().getDeclaredFields();
        final Map<String, Object> map = new HashMap<String, Object>();
        for (final Field field : fields) {
            if (!Modifier.isTransient(field.getModifiers())) {
                if (!Modifier.isStatic(field.getModifiers())) {
                    final String fieldName = field.getName();
                    try {
                        field.setAccessible(true);
                        final Object fieldObject = field.get(object);
                        map.put(fieldName, this.serialize(fieldObject, logger));
                        field.setAccessible(false);
                    }
                    catch (final Exception exception) {
                        logger.log(SentryLevel.INFO, "Cannot access field " + fieldName + ".", new Object[0]);
                    }
                }
            }
        }
        return map;
    }
    
    @NotNull
    private List<Object> list(@NotNull final Object[] objectArray, @NotNull final ILogger logger) throws Exception {
        final List<Object> list = new ArrayList<Object>();
        for (final Object object : objectArray) {
            list.add(this.serialize(object, logger));
        }
        return list;
    }
    
    @NotNull
    private List<Object> list(@NotNull final Collection<?> collection, @NotNull final ILogger logger) throws Exception {
        final List<Object> list = new ArrayList<Object>();
        for (final Object object : collection) {
            list.add(this.serialize(object, logger));
        }
        return list;
    }
    
    @NotNull
    private Map<String, Object> map(@NotNull final Map<?, ?> map, @NotNull final ILogger logger) throws Exception {
        final Map<String, Object> hashMap = new HashMap<String, Object>();
        for (final Object key : map.keySet()) {
            final Object object = map.get(key);
            if (object != null) {
                hashMap.put(key.toString(), this.serialize(object, logger));
            }
            else {
                hashMap.put(key.toString(), null);
            }
        }
        return hashMap;
    }
}
