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

package com.hypixel.hytale.builtin.adventure.objectives;

import com.hypixel.hytale.builtin.adventure.objectives.config.ObjectiveAsset;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Store;
import java.io.IOException;
import java.util.logging.Level;
import java.util.Iterator;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.builtin.adventure.objectives.task.ObjectiveTaskRef;
import com.hypixel.hytale.builtin.adventure.objectives.task.ObjectiveTask;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.datastore.DataStore;
import java.util.Set;
import java.util.UUID;
import java.util.Map;

public class ObjectiveDataStore
{
    private final Map<UUID, Objective> objectives;
    private final Map<UUID, Map<String, Set<UUID>>> entityObjectiveUUIDsPerPlayer;
    @Nonnull
    private final DataStore<Objective> dataStore;
    private final Map<Class<? extends ObjectiveTask>, Set<ObjectiveTaskRef<? extends ObjectiveTask>>> taskRefByType;
    @Nonnull
    private final HytaleLogger logger;
    
    public ObjectiveDataStore(@Nonnull final DataStore<Objective> dataStore) {
        this.objectives = new ConcurrentHashMap<UUID, Objective>();
        this.entityObjectiveUUIDsPerPlayer = new ConcurrentHashMap<UUID, Map<String, Set<UUID>>>();
        this.taskRefByType = new ConcurrentHashMap<Class<? extends ObjectiveTask>, Set<ObjectiveTaskRef<? extends ObjectiveTask>>>();
        this.dataStore = dataStore;
        this.logger = ObjectivePlugin.get().getLogger();
    }
    
    public Objective getObjective(final UUID objectiveUUID) {
        return this.objectives.get(objectiveUUID);
    }
    
    public Map<String, Set<UUID>> getEntityTasksForPlayer(final UUID playerUUID) {
        return this.entityObjectiveUUIDsPerPlayer.get(playerUUID);
    }
    
    @Nonnull
    public Collection<Objective> getObjectiveCollection() {
        return this.objectives.values();
    }
    
    public <T extends ObjectiveTask> Set<ObjectiveTaskRef<T>> getTaskRefsForType(final Class<T> taskClass) {
        return (Set)this.taskRefByType.get(taskClass);
    }
    
    public <T extends ObjectiveTask> void addTaskRef(@Nonnull final ObjectiveTaskRef<T> taskRef) {
        this.taskRefByType.get(taskRef.getObjectiveTask().getClass()).add((Object)taskRef);
    }
    
    public <T extends ObjectiveTask> void removeTaskRef(@Nullable final ObjectiveTaskRef<T> taskRef) {
        if (taskRef == null) {
            return;
        }
        this.taskRefByType.get(taskRef.getObjectiveTask().getClass()).remove(taskRef);
    }
    
    public <T extends ObjectiveTask> void registerTaskRef(final Class<T> taskClass) {
        this.taskRefByType.put(taskClass, (Set<ObjectiveTaskRef<? extends ObjectiveTask>>)ConcurrentHashMap.newKeySet());
    }
    
    public void saveToDisk(final String objectiveId, @Nonnull final Objective objective) {
        if (!objective.consumeDirty()) {
            return;
        }
        this.dataStore.save(objectiveId, objective);
    }
    
    public void saveToDiskAllObjectives() {
        for (final Map.Entry<UUID, Objective> entry : this.objectives.entrySet()) {
            this.saveToDisk(entry.getKey().toString(), entry.getValue());
        }
    }
    
    public boolean removeFromDisk(final String objectiveId) {
        try {
            this.dataStore.remove(objectiveId);
        }
        catch (final IOException e) {
            this.logger.at(Level.WARNING).withCause(e).log("Failed removal of objective with UUID: %s", objectiveId);
            return false;
        }
        return true;
    }
    
    public boolean addObjective(final UUID objectiveUUID, final Objective objective) {
        return this.objectives.putIfAbsent(objectiveUUID, objective) == null;
    }
    
    public void removeObjective(final UUID objectiveUUID) {
        this.objectives.remove(objectiveUUID);
    }
    
    public void addEntityTaskForPlayer(final UUID playerUUID, final String taskId, final UUID objectiveUUID) {
        this.entityObjectiveUUIDsPerPlayer.computeIfAbsent(playerUUID, s -> new ConcurrentHashMap()).computeIfAbsent(taskId, s -> ConcurrentHashMap.newKeySet()).add(objectiveUUID);
    }
    
    public void removeEntityTask(final UUID objectiveUUID, final String taskId) {
        final Iterator<Map.Entry<UUID, Map<String, Set<UUID>>>> entityObjectiveUUIDsPerPlayerIterator = this.entityObjectiveUUIDsPerPlayer.entrySet().iterator();
        while (entityObjectiveUUIDsPerPlayerIterator.hasNext()) {
            final Map.Entry<UUID, Map<String, Set<UUID>>> entityObjectiveUUIDsEntry = entityObjectiveUUIDsPerPlayerIterator.next();
            final Map<String, Set<UUID>> entityObjectiveUUIDs = entityObjectiveUUIDsEntry.getValue();
            final Set<UUID> objectiveUUIDs = entityObjectiveUUIDs.get(taskId);
            if (objectiveUUIDs == null) {
                continue;
            }
            if (!objectiveUUIDs.remove(objectiveUUID)) {
                continue;
            }
            if (objectiveUUIDs.isEmpty()) {
                entityObjectiveUUIDs.remove(taskId);
            }
            if (!entityObjectiveUUIDs.isEmpty()) {
                continue;
            }
            entityObjectiveUUIDsPerPlayerIterator.remove();
        }
    }
    
    public void removeEntityTaskForPlayer(final UUID objectiveUUID, final String taskId, final UUID playerUUID) {
        final Map<String, Set<UUID>> entityObjectiveUUIDs = this.entityObjectiveUUIDsPerPlayer.get(playerUUID);
        if (entityObjectiveUUIDs == null) {
            return;
        }
        final Set<UUID> objectiveUUIDs = entityObjectiveUUIDs.get(taskId);
        if (objectiveUUIDs == null) {
            return;
        }
        if (!objectiveUUIDs.remove(objectiveUUID)) {
            return;
        }
        if (objectiveUUIDs.isEmpty()) {
            entityObjectiveUUIDs.remove(taskId);
        }
        if (entityObjectiveUUIDs.isEmpty()) {
            this.entityObjectiveUUIDsPerPlayer.remove(playerUUID);
        }
    }
    
    @Nullable
    public Objective loadObjective(@Nonnull final UUID objectiveUUID, @Nonnull final Store<EntityStore> store) {
        Objective objective = this.objectives.get(objectiveUUID);
        if (objective != null) {
            return objective;
        }
        try {
            objective = this.dataStore.load(objectiveUUID.toString());
        }
        catch (final IOException e) {
            this.logger.at(Level.WARNING).withCause(e).log("Unable to load objective with UUID '%s'", objectiveUUID);
            return null;
        }
        if (objective == null) {
            this.logger.at(Level.WARNING).log("No objective saved with UUID '%s'", objectiveUUID);
            return null;
        }
        final String objectiveId = objective.getObjectiveId();
        if (ObjectiveAsset.getAssetMap().getAsset(objectiveId) == null) {
            this.logger.at(Level.WARNING).log("Couldn't find objective '%s'. Skipping objective.", objectiveId);
            return null;
        }
        if (!objective.setupCurrentTasks(store)) {
            this.logger.at(Level.WARNING).log("A problem occurred while setting up the objective '%s'. Skipping objective.", objectiveId);
            return null;
        }
        this.addObjective(objectiveUUID, objective);
        return objective;
    }
    
    public void unloadObjective(final UUID objectiveUUID) {
        final Objective objective = this.objectives.get(objectiveUUID);
        if (objective == null) {
            return;
        }
        objective.unload();
        this.removeObjective(objective.getObjectiveUUID());
    }
}
