package thebetweenlands.common.world.storage;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;

import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.hash.TObjectIntHashMap;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraftforge.common.util.Constants;
import thebetweenlands.api.loot.ISharedLootPool;
import thebetweenlands.api.network.IGenericDataManagerAccess;
import thebetweenlands.api.storage.IWorldStorage;
import thebetweenlands.api.storage.LocalRegion;
import thebetweenlands.api.storage.StorageID;
import thebetweenlands.common.loot.shared.SharedLootPool;

public class SharedLootPoolStorage extends LocalStorageImpl {
	private Map<ResourceLocation, SharedLootPool> sharedLootPools = new HashMap<>();
	private TObjectIntMap<ResourceLocation> lootInventories = new TObjectIntHashMap<>();

	private long lootSeed;

	public SharedLootPoolStorage(IWorldStorage worldStorage, StorageID id, LocalRegion region) {
		super(worldStorage, id, region);
	}

	public SharedLootPoolStorage(IWorldStorage worldStorage, StorageID id, LocalRegion region, long lootSeed) {
		super(worldStorage, id, region);
		this.lootSeed = lootSeed;
	}

	@Override
	public AxisAlignedBB getBoundingBox() {
		//No bounding box required.
		//Loot inventories are directly linked to the storage by storage ID.
		return null;
	}

	@Override
	public IGenericDataManagerAccess getDataManager() {
		return null;
	}

	@Override
	public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
		nbt = super.writeToNBT(nbt);

		if(!this.sharedLootPools.isEmpty()) {
			NBTTagList sharedLootPoolsNbt = new NBTTagList();

			for(SharedLootPool sharedLootPool : this.sharedLootPools.values()) {
				sharedLootPoolsNbt.appendTag(sharedLootPool.writeToNBT(new NBTTagCompound()));
			}

			nbt.setTag("sharedLootPools", sharedLootPoolsNbt);
		}

		if(!this.lootInventories.isEmpty()) {
			NBTTagList lootInventoriesNbt = new NBTTagList();
			this.lootInventories.forEachEntry((table, count) -> {
				NBTTagCompound entryNbt = new NBTTagCompound();
				entryNbt.setString("table", table.toString());
				entryNbt.setInteger("count", count);
				lootInventoriesNbt.appendTag(entryNbt);
				return true;
			});
			nbt.setTag("lootInventories", lootInventoriesNbt);
		}

		return nbt;
	}

	@Override
	public void readFromNBT(NBTTagCompound nbt) {
		super.readFromNBT(nbt);

		this.sharedLootPools.clear();
		NBTTagList sharedLootPoolsNbt = nbt.getTagList("sharedLootPools", Constants.NBT.TAG_COMPOUND);

		for(int i = 0; i < sharedLootPoolsNbt.tagCount(); i++) {
			SharedLootPool sharedLootPool = new SharedLootPool(sharedLootPoolsNbt.getCompoundTagAt(i), this);
			ResourceLocation lootTable = sharedLootPool.getLootTable();
			if(lootTable != null) {
				this.sharedLootPools.put(lootTable, sharedLootPool);
			}
		}

		this.lootInventories.clear();
		NBTTagList lootInventoriesNbt = nbt.getTagList("lootInventories", Constants.NBT.TAG_COMPOUND);
		for(int i = 0; i < lootInventoriesNbt.tagCount(); i++) {
			NBTTagCompound entryNbt = lootInventoriesNbt.getCompoundTagAt(i);
			this.lootInventories.put(new ResourceLocation(entryNbt.getString("table")), entryNbt.getInteger("count"));
		}
	}

	public int getSharedLootInventories(ResourceLocation lootTable) {
		return this.lootInventories.get(lootTable);
	}

	public void registerSharedLootInventory(BlockPos pos, ResourceLocation lootTable) {
		this.lootInventories.adjustOrPutValue(lootTable, 1, 1);

		//Make sure this storage is linked to the chunk the loot inventory is in
		this.linkChunkSafely(new ChunkPos(pos));

		this.markDirty();
	}

	@Nullable
	public ISharedLootPool getSharedLootPool(ResourceLocation lootTable) {
		return this.sharedLootPools.get(lootTable);
	}

	@Nullable
	public ISharedLootPool getOrCreateSharedLootPool(ResourceLocation lootTable) {
		ISharedLootPool pool = this.getSharedLootPool(lootTable);

		if(pool != null) {
			return pool;
		}

		SharedLootPool newPool = new SharedLootPool(lootTable, this.lootSeed, this);
		this.sharedLootPools.put(lootTable, newPool);
		this.markDirty();

		return newPool;
	}

	@Nullable
	public ISharedLootPool removeSharedLootPool(ResourceLocation lootTable) {
		ISharedLootPool pool = this.sharedLootPools.remove(lootTable);
		if(pool != null) {
			this.markDirty();
		}

		return pool;
	}

	public Set<ResourceLocation> getSharedLootPoolKeys() {
		return this.sharedLootPools.keySet();
	}
}
