package com.teamwizardry.wizardry.api.item;

import com.teamwizardry.librarianlib.features.base.item.IItemColorProvider;
import com.teamwizardry.librarianlib.features.helpers.NBTHelper;
import com.teamwizardry.wizardry.api.NBTConstants.NBT;
import com.teamwizardry.wizardry.common.block.fluid.ModFluids;
import kotlin.jvm.functions.Function2;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import javax.annotation.Nullable;
import java.awt.*;

/**
 * Implement this to make your item change it's color light nacre products.
 * Nacre Pearls use this.
 */
public interface INacreProduct extends IItemColorProvider {

	float curveConst = 0.75F / (1.0F - 1 / (float) Math.E);

	default void colorableOnUpdate(ItemStack stack, World world) {
		if (!world.isRemote) {
			if (!NBTHelper.hasNBTEntry(stack, NBT.RAND))
				NBTHelper.setFloat(stack, NBT.RAND, (world.getTotalWorldTime() / 140f) % 140f);

			if (!NBTHelper.hasNBTEntry(stack, NBT.PURITY)) {
				NBTHelper.setInt(stack, NBT.PURITY, NBT.NACRE_PURITY_CONVERSION);
				NBTHelper.setFloat(stack, NBT.PURITY_OVERRIDE, 1f);
			}

			if (!NBTHelper.getBoolean(stack, NBT.COMPLETE, false))
				NBTHelper.setBoolean(stack, NBT.COMPLETE, true);
		}
	}

	default void colorableOnEntityItemUpdate(EntityItem entityItem) {
		if (entityItem.world.isRemote) return;
		ItemStack stack = entityItem.getItem();

		if (!NBTHelper.hasNBTEntry(stack, NBT.RAND))
			NBTHelper.setFloat(stack, NBT.RAND, entityItem.world.rand.nextFloat());

		IBlockState state = entityItem.world.getBlockState(entityItem.getPosition());

		if (state.getBlock() == ModFluids.NACRE.getActualBlock() && !NBTHelper.getBoolean(stack, NBT.COMPLETE, false)) {
			int purity = NBTHelper.getInt(stack, NBT.PURITY, 0);
			purity = Math.min(purity + 1, NBT.NACRE_PURITY_CONVERSION * 2);
			NBTHelper.setInt(stack, NBT.PURITY, purity);
		} else if (NBTHelper.getInt(stack, NBT.PURITY, 0) > 0)
			NBTHelper.setBoolean(stack, NBT.COMPLETE, true);
	}

	default float getQuality(ItemStack stack) {
		if (!stack.hasTagCompound())
			return 1f;
		float override = NBTHelper.getFloat(stack, NBT.PURITY_OVERRIDE, -1f);
		if (override > 0)
			return override;

		float timeConstant = NBT.NACRE_PURITY_CONVERSION;
		int purity = NBTHelper.getInt(stack, NBT.PURITY, NBT.NACRE_PURITY_CONVERSION);
		if (purity > NBT.NACRE_PURITY_CONVERSION + 1)
			return Math.max(0, 2f - purity / timeConstant);
		else if (purity < NBT.NACRE_PURITY_CONVERSION - 1)
			return Math.max(0, purity / timeConstant);
		else
			return 1f;
	}

	@Nullable
	@Override
	@SideOnly(Side.CLIENT)
	default Function2<ItemStack, Integer, Integer> getItemColorFunction() {
		return (stack, tintIndex) -> {
			//float hue = NBTHelper.getFloat(stack, "hue", 0);
			//float sat = NBTHelper.getFloat(stack, "saturation", 0);
			//float pow = Math.min(1f, Math.max(0f, getQuality(stack)));

			//float saturation = curveConst * (1 - (float) Math.pow(Math.E, -pow)) * sat;

			if (tintIndex != 0) return 0xFFFFFF;
			float rand = NBTHelper.getFloat(stack, NBT.RAND, -1);
			float hue = rand < 0 ? (Minecraft.getMinecraft().world.getTotalWorldTime() / 140f) % 140f : rand;
			float pow = Math.min(1f, Math.max(0f, getQuality(stack)));

			float saturation = curveConst * (1 - (float) Math.pow(Math.E, -pow));

			return Color.HSBtoRGB(hue, saturation, 1f);
		};
	}

	interface INacreDecayProduct extends INacreProduct {
		double decayCurveDelimiter = 1 / 6.0;

		@Nullable
		@Override
		@SideOnly(Side.CLIENT)
		default Function2<ItemStack, Integer, Integer> getItemColorFunction() {
			return (stack, tintIndex) -> {
				if (tintIndex != 0) return 0xFFFFFF;
				if (!stack.hasTagCompound())
					return Color.HSBtoRGB(MathHelper.sin(Minecraft.getMinecraft().world.getTotalWorldTime() / 140f), 0.75f, 1f);

				long lastCast = NBTHelper.getLong(stack, NBT.LAST_CAST, -1);
				int decayCooldown = NBTHelper.getInt(stack, NBT.LAST_COOLDOWN, -1);
				long tick = Minecraft.getMinecraft().world.getTotalWorldTime();
				long timeSinceCooldown = tick - lastCast;
				float decayStage = (decayCooldown > 0) ? ((float) timeSinceCooldown) / decayCooldown : 1f;


				float rand = NBTHelper.getFloat(stack, NBT.RAND, -1);
				float hue = rand < 0 ? (tick / 140f) % 140f : rand;
				float pow = Math.min(1f, Math.max(0f, getQuality(stack)));

				double decaySaturation = (lastCast == -1 || decayCooldown <= 0 || decayStage >= 1f) ? 1f :
						(decayStage < decayCurveDelimiter) ? Math.pow(Math.E, -15 * decayStage) : Math.pow(Math.E, 3 * decayStage - 3);

				float saturation = curveConst * (1 - (float) Math.pow(Math.E, -pow)) * (float) decaySaturation;

				return Color.HSBtoRGB(hue, saturation, 1f);
			};
		}
	}
}