package net.minecraft.stats;

import org.apache.logging.log4j.LogManager;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundAwardStatsPacket;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.server.level.ServerPlayer;
import java.util.Collection;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import net.minecraft.SharedConstants;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import com.google.common.collect.Maps;
import com.google.gson.JsonPrimitive;
import net.minecraft.nbt.Tag;
import java.util.Map;
import com.google.gson.JsonObject;
import java.util.function.Function;
import java.util.Optional;
import java.util.Iterator;
import net.minecraft.nbt.CompoundTag;
import com.google.gson.JsonElement;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.core.Registry;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.util.datafix.DataFixTypes;
import com.google.gson.internal.Streams;
import java.io.Reader;
import com.google.gson.stream.JsonReader;
import java.io.StringReader;
import com.mojang.datafixers.DataFixer;
import net.minecraft.world.entity.player.Player;
import com.google.gson.JsonParseException;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import com.google.common.collect.Sets;
import java.util.Set;
import java.io.File;
import net.minecraft.server.MinecraftServer;
import org.apache.logging.log4j.Logger;

public class ServerStatsCounter extends StatsCounter {
    private static final Logger LOGGER;
    private final MinecraftServer server;
    private final File file;
    private final Set<Stat<?>> dirty;
    private int lastStatRequest;
    
    public ServerStatsCounter(final MinecraftServer minecraftServer, final File file) {
        this.dirty = Sets.newHashSet();
        this.lastStatRequest = -300;
        this.server = minecraftServer;
        this.file = file;
        if (file.isFile()) {
            try {
                this.parseLocal(minecraftServer.getFixerUpper(), FileUtils.readFileToString(file));
            }
            catch (IOException iOException4) {
                ServerStatsCounter.LOGGER.error("Couldn't read statistics file {}", file, iOException4);
            }
            catch (JsonParseException jsonParseException4) {
                ServerStatsCounter.LOGGER.error("Couldn't parse statistics file {}", file, jsonParseException4);
            }
        }
    }
    
    public void save() {
        try {
            FileUtils.writeStringToFile(this.file, this.toJson());
        }
        catch (IOException iOException2) {
            ServerStatsCounter.LOGGER.error("Couldn't save stats", (Throwable)iOException2);
        }
    }
    
    @Override
    public void setValue(final Player ayj, final Stat<?> aao, final int integer) {
        super.setValue(ayj, aao, integer);
        this.dirty.add(aao);
    }
    
    private Set<Stat<?>> getDirty() {
        final Set<Stat<?>> set2 = Sets.newHashSet(this.dirty);
        this.dirty.clear();
        return set2;
    }
    
    public void parseLocal(final DataFixer dataFixer, final String string) {
        try (final JsonReader jsonReader4 = new JsonReader(new StringReader(string))) {
            jsonReader4.setLenient(false);
            final JsonElement jsonElement6 = Streams.parse(jsonReader4);
            if (jsonElement6.isJsonNull()) {
                ServerStatsCounter.LOGGER.error("Unable to parse Stat data from {}", this.file);
                return;
            }
            CompoundTag jt7 = fromJson(jsonElement6.getAsJsonObject());
            if (!jt7.contains("DataVersion", 99)) {
                jt7.putInt("DataVersion", 1343);
            }
            jt7 = NbtUtils.update(dataFixer, DataFixTypes.STATS, jt7, jt7.getInt("DataVersion"));
            if (jt7.contains("stats", 10)) {
                final CompoundTag jt8 = jt7.getCompound("stats");
                for (final String string2 : jt8.getAllKeys()) {
                    if (jt8.contains(string2, 10)) {
                        final CompoundTag jt9;
                        final Iterator<String> iterator2;
                        String string3;
                        Util.<StatType<?>>ifElse(Registry.STAT_TYPE.getOptional(new ResourceLocation(string2)), aaq -> {
                            jt9 = jt8.getCompound(string2);
                            jt9.getAllKeys().iterator();
                            while (iterator2.hasNext()) {
                                string3 = iterator2.next();
                                if (jt9.contains(string3, 99)) {
                                    Util.<Stat<Object>>ifElse(this.getStat(aaq, string3), aao -> this.stats.put(aao, jt9.getInt(string3)), () -> ServerStatsCounter.LOGGER.warn("Invalid statistic in {}: Don't know what {} is", this.file, string3));
                                }
                                else {
                                    ServerStatsCounter.LOGGER.warn("Invalid statistic value in {}: Don't know what {} is for key {}", this.file, jt9.get(string3), string3);
                                }
                            }
                            return;
                        }, () -> ServerStatsCounter.LOGGER.warn("Invalid statistic type in {}: Don't know what {} is", this.file, string2));
                    }
                }
            }
        }
        catch (JsonParseException | IOException ex2) {
            final Exception ex;
            final Exception exception4 = ex;
            ServerStatsCounter.LOGGER.error("Unable to parse Stat data from {}", this.file, exception4);
        }
    }
    
    private <T> Optional<Stat<T>> getStat(final StatType<T> aaq, final String string) {
        return Optional.<ResourceLocation>ofNullable(ResourceLocation.tryParse(string)).flatMap(aaq.getRegistry()::getOptional).<Stat<T>>map(aaq::get);
    }
    
    private static CompoundTag fromJson(final JsonObject jsonObject) {
        final CompoundTag jt2 = new CompoundTag();
        for (final Map.Entry<String, JsonElement> entry4 : jsonObject.entrySet()) {
            final JsonElement jsonElement5 = entry4.getValue();
            if (jsonElement5.isJsonObject()) {
                jt2.put(entry4.getKey(), fromJson(jsonElement5.getAsJsonObject()));
            }
            else {
                if (!jsonElement5.isJsonPrimitive()) {
                    continue;
                }
                final JsonPrimitive jsonPrimitive6 = jsonElement5.getAsJsonPrimitive();
                if (!jsonPrimitive6.isNumber()) {
                    continue;
                }
                jt2.putInt(entry4.getKey(), jsonPrimitive6.getAsInt());
            }
        }
        return jt2;
    }
    
    protected String toJson() {
        final Map<StatType<?>, JsonObject> map2 = Maps.newHashMap();
        for (final Object2IntMap.Entry<Stat<?>> entry4 : this.stats.object2IntEntrySet()) {
            final Stat<?> aao5 = entry4.getKey();
            map2.computeIfAbsent(aao5.getType(), aaq -> new JsonObject()).addProperty(ServerStatsCounter.getKey(aao5).toString(), entry4.getIntValue());
        }
        final JsonObject jsonObject3 = new JsonObject();
        for (final Map.Entry<StatType<?>, JsonObject> entry5 : map2.entrySet()) {
            jsonObject3.add(Registry.STAT_TYPE.getKey(entry5.getKey()).toString(), entry5.getValue());
        }
        final JsonObject jsonObject4 = new JsonObject();
        jsonObject4.add("stats", jsonObject3);
        jsonObject4.addProperty("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
        return jsonObject4.toString();
    }
    
    private static <T> ResourceLocation getKey(final Stat<T> aao) {
        return aao.getType().getRegistry().getKey(aao.getValue());
    }
    
    public void markAllDirty() {
        this.dirty.addAll(this.stats.keySet());
    }
    
    public void sendStats(final ServerPlayer xe) {
        final int integer3 = this.server.getTickCount();
        final Object2IntMap<Stat<?>> object2IntMap4 = (Object2IntMap<Stat<?>>)new Object2IntOpenHashMap();
        if (integer3 - this.lastStatRequest > 300) {
            this.lastStatRequest = integer3;
            for (final Stat<?> aao6 : this.getDirty()) {
                object2IntMap4.put(aao6, this.getValue(aao6));
            }
        }
        xe.connection.send(new ClientboundAwardStatsPacket(object2IntMap4));
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
