// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com


package engine.objects;

import engine.Enum.ItemContainerType;
import engine.Enum.ItemType;
import engine.Enum.OwnerType;
import engine.gameManager.ConfigManager;
import engine.gameManager.DbManager;
import engine.gameManager.NPCManager;
import engine.gameManager.ZoneManager;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;

public class LootTable {

	private static final ConcurrentHashMap<Integer, LootTable> lootGroups = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
	private static final ConcurrentHashMap<Integer, LootTable> lootTables = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
	private static final ConcurrentHashMap<Integer, LootTable> modTables = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
	private static final ConcurrentHashMap<Integer, LootTable> modGroups = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
	private static final ConcurrentHashMap<Integer, Integer> statRuneChances = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
	private final ConcurrentHashMap<Integer, LootRow> lootTable = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);



	private static final int oneDrop = 95;
	private static final int twoDrop = 100;
	private static final int noDropHotZone = 79;
	private static final int oneDropHotZone = 98;

	public float minRoll = 320;
	public float maxRoll = 1;
	public static boolean initialized = false;

	public int lootTableID = 0;

	public static HashMap<ItemBase,Integer> itemsDroppedMap = new HashMap<>();
	public static HashMap<ItemBase,Integer> resourceDroppedMap = new HashMap<>();
	public static HashMap<ItemBase,Integer> runeDroppedMap = new HashMap<>();
	public static HashMap<ItemBase,Integer> contractDroppedMap = new HashMap<>();
	public static HashMap<ItemBase,Integer> glassDroppedMap = new HashMap<>();

	public static int rollCount = 0;
	public static int dropCount = 0;
	public static int runeCount = 0;
	public static int contractCount = 0;
	public static int resourceCount = 0;
	public static int glassCount = 0;


	/**
	 * Generic Constructor
	 */
	public LootTable(int lootTableID) {
		this.lootTableID = lootTableID;
	}

	public void addRow(float min, float max, int valueOne, int valueTwo, int valueThree, String action) {

		//hackey way to set the minimum roll for SHIAT!
		if (min < this.minRoll)
			this.minRoll = min;

		if (max > this.maxRoll)
			this.maxRoll = max;

		int minInt = (int) min;
		int maxInt = (int) max;

		//Round up min
		if (minInt != min){
			min = minInt + 1;
		}

		//Round down max;
		if (maxInt != max)
			max = maxInt;



		LootRow lootRow = new LootRow(valueOne, valueTwo, valueThree, action);
		for (int i = (int) min; i <= max; i++) {
			lootTable.put(i, lootRow);
		}
	}

	public static LootTable getLootGroup(int UUID) {

		if (lootGroups.containsKey(UUID))
			return lootGroups.get(UUID);

		LootTable lootGroup = new LootTable(UUID);
		lootGroups.put(UUID, lootGroup);
		return lootGroup;
	}

	public static LootTable getLootTable(int UUID) {

		if (lootTables.containsKey(UUID))
			return lootTables.get(UUID);

		LootTable lootTable = new LootTable(UUID);
		lootTables.put(UUID, lootTable);
		return lootTable;
	}

	/**
	 * @return the lootGroups
	 */
	public static ConcurrentHashMap<Integer, LootTable> getLootGroups() {
		return lootGroups;
	}

	/**
	 * @return the lootTables
	 */
	public static ConcurrentHashMap<Integer, LootTable> getLootTables() {
		return lootTables;
	}

	/**
	 * @return the modTables
	 */
	public static ConcurrentHashMap<Integer, LootTable> getModTables() {
		return modTables;
	}

	/**
	 * @return the modGroups
	 */
	public static ConcurrentHashMap<Integer, LootTable> getModGroups() {
		return modGroups;
	}


	public static LootTable getModGroup(int UUID) {
		if (modGroups.containsKey(UUID))
			return modGroups.get(UUID);
		LootTable modTable = new LootTable(UUID);
		modGroups.put(UUID, modTable);
		return modTable;
	}

	public static LootTable getModTable(int UUID) {
		if (modTables.containsKey(UUID))
			return modTables.get(UUID);
		LootTable modTypeTable = new LootTable(UUID);
		modTables.put(UUID, modTypeTable);
		return modTypeTable;
	}


	public LootRow getLootRow(int probability) {
		if (lootTable.containsKey(probability))
			return lootTable.get(probability);
		return null;
	}

	//call this on server startup to populate the tables
	public static void populateLootTables() {
		DbManager.LootQueries.populateLootGroups();
		DbManager.LootQueries.populateLootTables();
		DbManager.LootQueries.populateModTables();
		DbManager.LootQueries.populateModGroups();

		//preset chances for rune drops
		populateStatRuneChances();
	}

	//Returns a list of random loot for a mob based on level, lootTable and hotzone
	public static ArrayList<MobLoot> getMobLoot(Mob mobile, int mobLevel, int lootTable, boolean hotzone) {

		ArrayList<MobLoot> mobLoot;
		int calculatedLootTable;
		int randomRoll;

		mobLoot = new ArrayList<>();

		// Setup default loot table if none exists

		calculatedLootTable = lootTable;

		LootTable.rollCount++;

		if (MobLootBase.MobLootSet.get(mobile.getMobBase().getLoadID()).isEmpty()){

			randomRoll = ThreadLocalRandom.current().nextInt(100);

			if (randomRoll > 90)
				if (randomRoll > LootTable.oneDropHotZone)
					addMobLoot(mobile, mobLoot, mobLevel, calculatedLootTable, 1, true);
				else
					addMobLoot(mobile, mobLoot, mobLevel, calculatedLootTable, 1, true);
		}else{
			for (MobLootBase mlb:MobLootBase.MobLootSet.get(mobile.getMobBase().getLoadID())){

				float chance = mlb.getChance() *.01f;

				chance *= Float.parseFloat(ConfigManager.MB_NORMAL_DROP_RATE.getValue());

				calculatedLootTable = mlb.getLootTableID();

				if (ThreadLocalRandom.current().nextFloat() > chance)
					continue;

				addMobLoot(mobile, mobLoot, mobLevel, calculatedLootTable, 1, false);

			}
		}

		//calculatedLootTable = lootTable;

		if (calculatedLootTable <= 1)
			calculatedLootTable = 1300;  // GENERIC WORLD

		//handle hotzone random loot

		if (hotzone) {

			LootTable.rollCount++;

			if (!MobLootBase.MobLootSet.get(mobile.getMobBase().getLoadID()).isEmpty())
				for (MobLootBase mlb : MobLootBase.MobLootSet.get(mobile.getMobBase().getLoadID())) {
					if (!LootTable.lootGroups.containsKey(mlb.getLootTableID() + 1))
						continue;
					calculatedLootTable = mlb.getLootTableID();
					break;
				}

			randomRoll = ThreadLocalRandom.current().nextInt(100);

			if (randomRoll > 90)
				if (randomRoll > LootTable.oneDropHotZone)
					addMobLoot(mobile, mobLoot, mobLevel, calculatedLootTable + 1, 1, true);
				else
					addMobLoot(mobile, mobLoot, mobLevel, calculatedLootTable + 1, 1, true);
		}

		//handle mob specific special loot

		ArrayList bootyLoot = getBootyLoot(mobile);
		mobLoot.addAll(bootyLoot);

		return mobLoot;
	}

	public static ArrayList<MobLoot> getMobLootDeath(Mob mobile, int mobLevel, int lootTable) {
		ArrayList<MobLoot> mobLoot = new ArrayList<>();

		if (mobile == null)
			return mobLoot;

		//handle hotzone random loot
		boolean hotzone = ZoneManager.inHotZone(mobile.getLoc());
		if (hotzone) {

			if (MobLootBase.MobLootSet.get(mobile.getMobBase().getLoadID()).isEmpty()){
				lootTable += 1;

				if (lootTable <= 1)
					lootTable = 1301;  // GENERIC WORLD
				int roll = ThreadLocalRandom.current().nextInt(100);
				if (roll > 90)
					if (roll > LootTable.oneDropHotZone)
						addMobLoot(mobile, mobLoot, mobLevel, lootTable, 1, true);
					else
						addMobLoot(mobile, mobLoot, mobLevel, lootTable, 1, true);
			}else{
				for (MobLootBase mlb:MobLootBase.MobLootSet.get(mobile.getMobBase().getLoadID())){
					lootTable = mlb.getLootTableID() + 1;
					if (!LootTable.lootGroups.containsKey(lootTable))
						continue;

					int roll = ThreadLocalRandom.current().nextInt(100);
					if (roll > 90)
						if (roll > LootTable.oneDropHotZone)
							addMobLoot(mobile, mobLoot, mobLevel, (lootTable), 1, true);
						else
							addMobLoot(mobile, mobLoot, mobLevel, (lootTable), 1, true);

					break;
				}
			}


			if (mobLoot.isEmpty()){

				LootTable.rollCount++; //add another rollCount here.
				int resourceRoll = ThreadLocalRandom.current().nextInt(100);
				if (resourceRoll <=5)
					addMobLootResources(mobile, mobLoot, mobLevel, (lootTable), 1, true);
			}

		}


		//handle mob specific booty on death

		ArrayList bootyLoot = getBootyLoot(mobile);
		mobLoot.addAll(bootyLoot);

		return mobLoot;
	}

	private static ArrayList<MobLoot> getBootyLoot(Mob mob) {

		ArrayList<BootySetEntry> bootySetList;
		ArrayList<MobLoot> mobLootList = new ArrayList<>();

		if (mob.bootySet == 0)
			return mobLootList;

		bootySetList = NPCManager._bootySetMap.get(mob.bootySet);

			for (BootySetEntry bootyEntry : bootySetList)
					if (ThreadLocalRandom.current().nextInt(100) < bootyEntry.dropChance) {
						ItemBase itemBase = ItemBase.getItemBase(bootyEntry.itemBase);

						if (itemBase != null) {
							MobLoot mobLoot = new MobLoot(mob, itemBase, true);
							mobLootList.add(mobLoot);
					}
			}
		return mobLootList;
	}



	//called by getMobLoot to add the actual loot
	private static void addMobLoot(Mob mob, ArrayList<MobLoot> loot, int mobLevel, int lootTableID, int cnt, boolean hotzone) {

		// Member variable declaration
		float calculatedMobLevel;
		int minSpawn;
		int maxSpawn;
		int spawnQuanity = 0;
		int prefixValue = 0;
		int suffixValue = 0;
		int subTableID;
		String modifierPrefix = "";
		String modifierSuffix = "";

		// Lookup Table Variables
		LootTable lootTable;
		LootRow lootRow;
		LootTable lootGroup;
		LootRow groupRow = null;
		LootTable modTable;
		LootTable modGroup;
		LootRow modRow = null;

		// Used for actual generation of items

		int itemBaseUUID;
		ItemBase itemBase = null;
		MobLoot mobLoot;

		if (!LootTable.lootGroups.containsKey(lootTableID))
			return;

		lootGroup = LootTable.lootGroups.get(lootTableID);

		calculatedMobLevel = mobLevel;

		if (calculatedMobLevel > 49)
			calculatedMobLevel = 49;


		int randomRoll = 0;
		for (int i = 0; i < cnt; i++) {

			Random random = new Random();

			randomRoll = random.nextInt(100) + 1; //random roll between 1 and 100
			groupRow = lootGroup.getLootRow(randomRoll);

			if (groupRow == null)
				return;

			//get loot table for this group
			if (!LootTable.lootTables.containsKey(groupRow.getValueOne()))
				return;

			lootTable = LootTable.lootTables.get(groupRow.getValueOne());

			int minRoll =  (int) ((calculatedMobLevel - 5) * 5);
			int maxRoll = (int) ((calculatedMobLevel + 15) * 5);

			if (minRoll < (int)lootTable.minRoll)
				minRoll = (int)lootTable.minRoll;

			if (maxRoll < minRoll)
				maxRoll = minRoll;

			if (maxRoll > lootTable.maxRoll)
				maxRoll = (int) lootTable.maxRoll;

			if (maxRoll > 320)
				maxRoll = 320;

			randomRoll = (int) ThreadLocalRandom.current().nextDouble(minRoll, maxRoll + 1); //Does not return Max, but does return min?

			lootRow = lootTable.getLootRow(randomRoll); //get the item row from the bell's curve of level +-15

			if (lootRow == null)
				continue; //no item found for roll

			itemBaseUUID = lootRow.getValueOne();

			if (lootRow.getValueOne() == 0)
				continue;

			//handle quantities > 1 for resource drops

			minSpawn = lootRow.getValueTwo();
			maxSpawn = lootRow.getValueThree();

			// spawnQuantity between min spawn (inclusive) and max spawn (inclusive)
			if (maxSpawn > 1)
				spawnQuanity = ThreadLocalRandom.current().nextInt((maxSpawn + 1 - minSpawn)) + minSpawn;

			//get modifierPrefix

			calculatedMobLevel = mobLevel;

			if (calculatedMobLevel < 16)
				calculatedMobLevel = 16;

			if (calculatedMobLevel > 49)
				calculatedMobLevel = 49;

			int chanceMod = ThreadLocalRandom.current().nextInt(100) + 1;

			if (chanceMod < 25) {
				modGroup = LootTable.modGroups.get(groupRow.getValueTwo());

				if (modGroup != null) {

					for (int a = 0;a<10;a++){
						randomRoll = ThreadLocalRandom.current().nextInt(100) + 1;
						modRow = modGroup.getLootRow(randomRoll);
						if (modRow != null)
							break;
					}

					if (modRow != null) {
						subTableID = modRow.getValueOne();

						if (LootTable.modTables.containsKey(subTableID)) {

							modTable = LootTable.modTables.get(subTableID);

							randomRoll = gaussianLevel((int)calculatedMobLevel);

							if (randomRoll < modTable.minRoll)
								randomRoll = (int) modTable.minRoll;

							if (randomRoll > modTable.maxRoll)
								randomRoll = (int) modTable.maxRoll;

							modRow = modTable.getLootRow(randomRoll);

							if (modRow != null) {
								prefixValue = modRow.getValueOne();
								modifierPrefix = modRow.getAction();
							}
						}
					}
				}
			}else if(chanceMod < 50) {
				modGroup = LootTable.modGroups.get(groupRow.getValueThree());

				if (modGroup != null) {

					for (int a = 0;a<10;a++){
						randomRoll = ThreadLocalRandom.current().nextInt(100) + 1;
						modRow = modGroup.getLootRow(randomRoll);
						if (modRow != null)
							break;
					}

					if (modRow != null) {

						subTableID = modRow.getValueOne();

						if (LootTable.modTables.containsKey(subTableID)) {

							modTable = LootTable.modTables.get(subTableID);
							randomRoll = gaussianLevel((int)calculatedMobLevel);

							if (randomRoll < modTable.minRoll)
								randomRoll = (int) modTable.minRoll;

							if (randomRoll > modTable.maxRoll)
								randomRoll = (int) modTable.maxRoll;

							modRow = modTable.getLootRow(randomRoll);

							if (modRow == null)
								modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f));


							if (modRow != null) {
								suffixValue = modRow.getValueOne();
								modifierSuffix = modRow.getAction();
							}
						}
					}
				}
			}else{
				modGroup = LootTable.modGroups.get(groupRow.getValueTwo());

				if (modGroup != null) {


					for (int a = 0;a<10;a++) {
						randomRoll = ThreadLocalRandom.current().nextInt(100) + 1;
						modRow = modGroup.getLootRow(randomRoll);
						if (modRow != null)
							break;
					}


					if (modRow != null) {
						subTableID = modRow.getValueOne();

						if (LootTable.modTables.containsKey(subTableID)) {

							modTable = LootTable.modTables.get(subTableID);

							randomRoll = gaussianLevel((int)calculatedMobLevel);

							if (randomRoll < modTable.minRoll)
								randomRoll = (int) modTable.minRoll;

							if (randomRoll > modTable.maxRoll)
								randomRoll = (int) modTable.maxRoll;

							modRow = modTable.getLootRow(randomRoll);

							if (modRow == null)
								modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f));

							if (modRow != null) {
								prefixValue = modRow.getValueOne();
								modifierPrefix = modRow.getAction();
							}
						}
					}
				}

				//get modifierSuffix
				modGroup = LootTable.modGroups.get(groupRow.getValueThree());

				if (modGroup != null) {

					for (int a = 0;a<10;a++){
						randomRoll = ThreadLocalRandom.current().nextInt(100) + 1;
						modRow = modGroup.getLootRow(randomRoll);
						if (modRow != null)
							break;
					}

					if (modRow != null) {

						subTableID = modRow.getValueOne();

						if (LootTable.modTables.containsKey(subTableID)) {

							modTable = LootTable.modTables.get(subTableID);
							randomRoll = gaussianLevel((int)calculatedMobLevel);

							if (randomRoll < modTable.minRoll)
								randomRoll = (int) modTable.minRoll;

							if (randomRoll > modTable.maxRoll)
								randomRoll = (int) modTable.maxRoll;

							modRow = modTable.getLootRow(randomRoll);

							if (modRow == null)
								modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f));

							if (modRow != null) {
								suffixValue = modRow.getValueOne();
								modifierSuffix = modRow.getAction();
							}
						}
					}
				}
			}

			itemBase = ItemBase.getItemBase(itemBaseUUID);

			if (itemBase == null)
				return;

			//Handle logging of drops
			LootTable.HandleDropLogs(itemBase);

			if (itemBase.getType() == ItemType.OFFERING)
				spawnQuanity = 1;

			if (spawnQuanity > 0)
				mobLoot = new MobLoot(mob, itemBase, spawnQuanity, false);
			else
				mobLoot = new MobLoot(mob, itemBase, false);

			if (!modifierPrefix.isEmpty())
				mobLoot.addPermanentEnchantment(modifierPrefix, 0, prefixValue, true);

			if (!modifierSuffix.isEmpty())
				mobLoot.addPermanentEnchantment(modifierSuffix, 0, suffixValue, false);

			mobLoot.loadEnchantments();

			loot.add(mobLoot);

		}
	}

	private static void addMobLootResources(Mob mob, ArrayList<MobLoot> loot, int mobLevel, int lootTableID, int cnt, boolean hotzone) {

		// Member variable declaration
		float calculatedMobLevel;
		int minSpawn;
		int maxSpawn;
		int spawnQuanity = 0;

		// Lookup Table Variables
		LootTable lootTable;
		LootRow lootRow;
		LootTable lootGroup;
		LootRow groupRow = null;

		// Used for actual generation of items
		int itemBaseUUID;
		ItemBase itemBase;
		MobLoot mobLoot;

		if (!LootTable.lootGroups.containsKey(lootTableID))
			return;

		lootGroup = LootTable.lootGroups.get(lootTableID);

		calculatedMobLevel = mobLevel;

		if (calculatedMobLevel > 49)
			calculatedMobLevel = 49;

		int roll = 0;
		for (int i = 0; i < cnt; i++) {

			if  (lootTableID == 1901)
				groupRow = lootGroup.getLootRow(66);
			else if (lootTableID == 1501)
				groupRow = lootGroup.getLootRow(98);
			else
				groupRow = lootGroup.getLootRow(80);

			if (groupRow == null)
				return;

			//get loot table for this group

			if (!LootTable.lootTables.containsKey(groupRow.getValueOne()))
				return;

			lootTable = LootTable.lootTables.get(groupRow.getValueOne());

			int minRoll =  (int) ((calculatedMobLevel-5) * 5);
			int maxRoll = (int) ((calculatedMobLevel + 15) *5);

			if (minRoll < (int)lootTable.minRoll)
				minRoll = (int)lootTable.minRoll;

			if (maxRoll < minRoll)
				maxRoll = minRoll;

			if (maxRoll > 320)
				maxRoll = 320;

			roll = ThreadLocalRandom.current().nextInt(minRoll, maxRoll + 1); //Does not return Max, but does return min?
			lootRow = lootTable.getLootRow(roll); //get the item row from the bell's curve of level +-15

			if (lootRow == null)
				continue; //no item found for roll

			itemBaseUUID = lootRow.getValueOne();

			if (lootRow.getValueOne() == 0)
				continue;

			//handle quantities > 1 for resource drops

			minSpawn = lootRow.getValueTwo();
			maxSpawn = lootRow.getValueThree();

			// spawnQuanity between minspawn (inclusive) and maxspawn (inclusive)

			if (maxSpawn > 1)
				spawnQuanity = ThreadLocalRandom.current().nextInt((maxSpawn + 1 - minSpawn)) + minSpawn;

			itemBase = ItemBase.getItemBase(itemBaseUUID);

			if (itemBase == null)
				return;

			LootTable.HandleDropLogs(itemBase);

			switch (itemBase.getUUID()){
			case 19290:
				continue;
			case 19291:
				continue;
			case 19292:
				continue;
			case 27530:
				continue;
			case 973000:
				continue;
			case 973200:
				continue;

			case 26360:
				continue;
			}

			// Handle drop rates of resources/runes/contracts.
			// We intentionally drop them in half

			if (itemBase.getType() == ItemType.OFFERING)
				spawnQuanity = 1;

			if (spawnQuanity > 0)
				mobLoot = new MobLoot(mob, itemBase, spawnQuanity, false);
			else
				mobLoot = new MobLoot(mob, itemBase, false);

			loot.add(mobLoot);

		}
	}

	public static int gaussianLevel(int level) {
		int ret = -76;

		while (ret < -75 || ret > 75) {
			ret = (int) (ThreadLocalRandom.current().nextGaussian() * 75);
		}

		return (level * 5) + ret;

	}

	//This set's the drop chances for stat runes.
	public static void populateStatRuneChances() {
		//+3, Increased
		statRuneChances.put(250018, 60);
		statRuneChances.put(250009, 60);
		statRuneChances.put(250027, 60);
		statRuneChances.put(250036, 60);
		statRuneChances.put(250000, 60);

		//+5, Enhanced
		statRuneChances.put(250019, 60);
		statRuneChances.put(250010, 60);
		statRuneChances.put(250028, 60);
		statRuneChances.put(250037, 60);
		statRuneChances.put(250001, 60);

		//+10 Exceptional
		statRuneChances.put(250020, 60);
		statRuneChances.put(250011, 60);
		statRuneChances.put(250029, 60);
		statRuneChances.put(250038, 60);
		statRuneChances.put(250002, 60);

		//+15, Amazing
		statRuneChances.put(250021, 60);
		statRuneChances.put(250012, 60);
		statRuneChances.put(250030, 60);
		statRuneChances.put(250039, 60);
		statRuneChances.put(250003, 60);

		//+20, Incredible
		statRuneChances.put(250022, 60);
		statRuneChances.put(250013, 60);
		statRuneChances.put(250031, 60);
		statRuneChances.put(250040, 60);
		statRuneChances.put(250004, 60);

		//+25, Great
		statRuneChances.put(250023, 60);
		statRuneChances.put(250014, 60);
		statRuneChances.put(250032, 60);
		statRuneChances.put(250041, 60);
		statRuneChances.put(250005, 60);

		//+30, Heroic
		statRuneChances.put(250024, 60);
		statRuneChances.put(250015, 60);
		statRuneChances.put(250033, 60);
		statRuneChances.put(250042, 60);
		statRuneChances.put(250006, 60);

		//+35, Legendary
		statRuneChances.put(250025, 60);
		statRuneChances.put(250016, 60);
		statRuneChances.put(250034, 60);
		statRuneChances.put(250043, 60);
		statRuneChances.put(250007, 60);

		//+40, of the Gods
		statRuneChances.put(250026, 60);
		statRuneChances.put(250017, 60);
		statRuneChances.put(250035, 60);
		statRuneChances.put(250044, 60);
		statRuneChances.put(250008, 60);
	}

	public ConcurrentHashMap<Integer, LootRow> getLootTable() {
		return lootTable;
	}

	private static void HandleDropLogs(ItemBase itemBase){

		if (itemBase == null)
			return;

		LootTable.dropCount++; //item dropped, add to all item count.


		if (LootTable.itemsDroppedMap.get(itemBase) == null){
			LootTable.itemsDroppedMap.put(itemBase, 1); //First time dropping, make count 1.
		}else{
			int dropCount = LootTable.itemsDroppedMap.get(itemBase);
			dropCount++;
			LootTable.itemsDroppedMap.put(itemBase, dropCount);
		}

		switch (itemBase.getType()){
		case RESOURCE:
			LootTable.resourceCount++;

			if (LootTable.resourceDroppedMap.get(itemBase) == null){
				LootTable.resourceDroppedMap.put(itemBase, 1); //First time dropping, make count 1.
			}else{
				int dropCount = LootTable.resourceDroppedMap.get(itemBase);
				dropCount++;
				LootTable.resourceDroppedMap.put(itemBase, dropCount);
			}
			break;
		case RUNE:
			LootTable.runeCount++;
			if (LootTable.runeDroppedMap.get(itemBase) == null){
				LootTable.runeDroppedMap.put(itemBase, 1); //First time dropping, make count 1.
			}else{
				int dropCount = LootTable.runeDroppedMap.get(itemBase);
				dropCount++;
				LootTable.runeDroppedMap.put(itemBase, dropCount);
			}
			break;
		case CONTRACT:
			LootTable.contractCount++;

			if (LootTable.contractDroppedMap.get(itemBase) == null){
				LootTable.contractDroppedMap.put(itemBase, 1); //First time dropping, make count 1.
			}else{
				int dropCount = LootTable.contractDroppedMap.get(itemBase);
				dropCount++;
				LootTable.contractDroppedMap.put(itemBase, dropCount);
			}

			break;
		case WEAPON: //Glass Drop

			if (itemBase.isGlass()){
				LootTable.glassCount++;
				if (LootTable.glassDroppedMap.get(itemBase) == null){
					LootTable.glassDroppedMap.put(itemBase, 1); //First time dropping, make count 1.
				}else{
					int dropCount = LootTable.glassDroppedMap.get(itemBase);
					dropCount++;
					LootTable.glassDroppedMap.put(itemBase, dropCount);
				}
			}

			break;
		}

	}

	public static Item CreateGamblerItem(Item item, PlayerCharacter gambler){

		if (item == null)
			return null;

		int groupID = 0;

		switch (item.getItemBase().getUUID()){
		case 971050: //Wrapped Axe
			groupID = 3000;
			break;
		case 971051://Wrapped Great Axe
			groupID = 3005;
			break;
		case 971052://Wrapped Throwing Axe
			groupID = 3010;
			break;
		case 971053://	Wrapped Bow
			groupID = 3015;
			break;
		case 971054://Wrapped Crossbow
			groupID = 3020;
			break;
		case 971055:	//Wrapped Dagger
			groupID = 3025;
			break;
		case 971056:	//	Wrapped Throwing Dagger
			groupID = 3030;
			break;
		case 971057:	//	Wrapped Hammer
			groupID = 3035;
			break;
		case 971058://			Wrapped Great Hammer
			groupID = 3040;
			break;
		case 971059://			Wrapped Throwing Hammer
			groupID = 3045;
			break;
		case 971060://			Wrapped Polearm
			groupID = 3050;
			break;
		case 971061://			Wrapped Spear
			groupID = 3055;
			break;
		case 971062://			Wrapped Staff
			groupID = 3060;
			break;
		case 971063://			Wrapped Sword
			groupID = 3065;
			break;
		case 971064://			Wrapped Great Sword
			groupID = 3070;
			break;
		case 971065://			Wrapped Unarmed Weapon
			groupID = 3075;
			break;
		case 971066://			Wrapped Cloth Armor
			groupID = 3100;
			break;
		case 971067://			Wrapped Light Armor
			groupID = 3105;
			break;
		case 971068://			Wrapped Medium Armor
			groupID = 3110;
			break;
		case 971069://			Wrapped Heavy Armor
			groupID = 3115;
			break;
		case 971070://			Wrapped Rune
			groupID = 3200;
			break;
		case 971071://			Wrapped City Improvement
			groupID = 3210;
			break;
		}
		//couldnt find group
		if (groupID == 0)
			return null;


		LootTable lootGroup = LootTable.lootGroups.get(groupID);

		if (lootGroup == null)
			return null;
		float calculatedMobLevel;
		int minSpawn;
		int maxSpawn;
		int spawnQuanity = 0;
		int prefixValue = 0;
		int suffixValue = 0;
		int subTableID;
		String modifierPrefix = "";
		String modifierSuffix = "";

		// Lookup Table Variables
		LootTable lootTable;
		LootRow lootRow;
		LootRow groupRow = null;
		LootTable modTable;
		LootTable modGroup;
		LootRow modRow = null;

		// Used for actual generation of items
		int itemBaseUUID;
		ItemBase itemBase = null;
		MobLoot mobLoot;



		int roll = ThreadLocalRandom.current().nextInt(100) + 1; //Does not return Max, but does return min?

		groupRow = lootGroup.getLootRow(roll);

		lootTable = LootTable.lootTables.get(groupRow.getValueOne());
		roll = ThreadLocalRandom.current().nextInt(100) + 1;
		lootRow = lootTable.getLootRow(roll + 220); //get the item row from the bell's curve of level +-15

		if (lootRow == null)
			return null; //no item found for roll

		itemBaseUUID = lootRow.getValueOne();



		if (lootRow.getValueOne() == 0)
			return null;

		//handle quantities > 1 for resource drops
		minSpawn = lootRow.getValueTwo();
		maxSpawn = lootRow.getValueThree();

		// spawnQuanity between minspawn (inclusive) and maxspawn (inclusive)
		if (maxSpawn > 1)
			spawnQuanity = ThreadLocalRandom.current().nextInt((maxSpawn + 1 - minSpawn)) + minSpawn;



		//get modifierPrefix

		calculatedMobLevel = 49;



		int chanceMod = ThreadLocalRandom.current().nextInt(100) + 1;

		if (chanceMod < 25){
			modGroup = LootTable.modGroups.get(groupRow.getValueTwo());

			if (modGroup != null) {


				for (int a = 0;a<10;a++){
					roll = ThreadLocalRandom.current().nextInt(100) + 1;
					modRow = modGroup.getLootRow(roll);
					if (modRow != null)
						break;
				}


				if (modRow != null) {
					subTableID = modRow.getValueOne();

					if (LootTable.modTables.containsKey(subTableID)) {

						modTable = LootTable.modTables.get(subTableID);

						roll = gaussianLevel((int)calculatedMobLevel);

						if (roll < modTable.minRoll)
							roll = (int) modTable.minRoll;

						if (roll > modTable.maxRoll)
							roll = (int) modTable.maxRoll;



						modRow = modTable.getLootRow(roll);

						if (modRow != null) {
							prefixValue = modRow.getValueOne();
							modifierPrefix = modRow.getAction();
						}
					}
				}
			}
		}else if(chanceMod < 50){
			modGroup = LootTable.modGroups.get(groupRow.getValueThree());

			if (modGroup != null) {

				for (int a = 0;a<10;a++){
					roll = ThreadLocalRandom.current().nextInt(100) + 1;
					modRow = modGroup.getLootRow(roll);
					if (modRow != null)
						break;
				}

				if (modRow != null) {

					subTableID = modRow.getValueOne();

					if (LootTable.modTables.containsKey(subTableID)) {

						modTable = LootTable.modTables.get(subTableID);
						roll = gaussianLevel((int)calculatedMobLevel);

						if (roll < modTable.minRoll)
							roll = (int) modTable.minRoll;

						if (roll > modTable.maxRoll)
							roll = (int) modTable.maxRoll;

						modRow = modTable.getLootRow(roll);

						if (modRow == null){
							modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f));
						}

						if (modRow != null) {
							suffixValue = modRow.getValueOne();
							modifierSuffix = modRow.getAction();
						}
					}
				}
			}
		}else{
			modGroup = LootTable.modGroups.get(groupRow.getValueTwo());

			if (modGroup != null) {


				for (int a = 0;a<10;a++){
					roll = ThreadLocalRandom.current().nextInt(100) + 1;
					modRow = modGroup.getLootRow(roll);
					if (modRow != null)
						break;
				}


				if (modRow != null) {
					subTableID = modRow.getValueOne();

					if (LootTable.modTables.containsKey(subTableID)) {

						modTable = LootTable.modTables.get(subTableID);

						roll = gaussianLevel((int)calculatedMobLevel);

						if (roll < modTable.minRoll)
							roll = (int) modTable.minRoll;

						if (roll > modTable.maxRoll)
							roll = (int) modTable.maxRoll;



						modRow = modTable.getLootRow(roll);

						if (modRow == null){
							modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f));
						}

						if (modRow != null) {
							prefixValue = modRow.getValueOne();
							modifierPrefix = modRow.getAction();
						}
					}
				}
			}

			//get modifierSuffix
			modGroup = LootTable.modGroups.get(groupRow.getValueThree());

			if (modGroup != null) {

				for (int a = 0;a<10;a++){
					roll = ThreadLocalRandom.current().nextInt(100) + 1;
					modRow = modGroup.getLootRow(roll);
					if (modRow != null)
						break;
				}

				if (modRow != null) {

					subTableID = modRow.getValueOne();

					if (LootTable.modTables.containsKey(subTableID)) {

						modTable = LootTable.modTables.get(subTableID);
						roll = gaussianLevel((int)calculatedMobLevel);

						if (roll < modTable.minRoll)
							roll = (int) modTable.minRoll;

						if (roll > modTable.maxRoll)
							roll = (int) modTable.maxRoll;

						modRow = modTable.getLootRow(roll);

						if (modRow == null){
							modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f));
						}

						if (modRow != null) {
							suffixValue = modRow.getValueOne();
							modifierSuffix = modRow.getAction();
						}
					}
				}
			}
		}


		itemBase = ItemBase.getItemBase(itemBaseUUID);
		byte charges = (byte) itemBase.getNumCharges();
		short dur = (short) itemBase.getDurability();



		short weight = itemBase.getWeight();
		if (!gambler.getCharItemManager().hasRoomInventory(weight)) {
			return null;
		}


		Item gambledItem = new Item(itemBase, gambler.getObjectUUID(),
				OwnerType.PlayerCharacter, charges, charges, dur, dur,
				true, false,ItemContainerType.INVENTORY,(byte) 0,
                new ArrayList<>(),"");

		if (spawnQuanity == 0 && itemBase.getType().equals(ItemType.RESOURCE))
			spawnQuanity = 1;

		if (spawnQuanity > 0)
			item.setNumOfItems(spawnQuanity);

		try {
			gambledItem = DbManager.ItemQueries.ADD_ITEM(gambledItem);

		} catch (Exception e) {
			Logger.error(e);
		}

		if (gambledItem == null) {

			return null;
		}
		if (!modifierPrefix.isEmpty())
			gambledItem.addPermanentEnchantment(modifierPrefix, 0);

		if (!modifierSuffix.isEmpty())
			gambledItem.addPermanentEnchantment(modifierSuffix, 0);



		//add item to inventory
		gambler.getCharItemManager().addItemToInventory(gambledItem);

		gambler.getCharItemManager().updateInventory();

		return gambledItem;
	}

}
