package com.seibel.distanthorizons.core.render;

import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.world.IDhClientWorld;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;

/**
 * An extension of {@link DhApiRenderParam}
 * that allows additional validation and putting all
 * rendering variables in a single place.
 */
public class RenderParams extends DhApiRenderParam
{
	private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
	private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
	
	private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
	
	private static final long TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS = 10_000;
	private static boolean initialLoadingComplete = false;
	
	
	public IDhClientWorld dhClientWorld;
	public IDhClientLevel dhClientLevel;
	/** more specific override of the API value {@link DhApiRenderParam#clientLevelWrapper} */
	public IClientLevelWrapper clientLevelWrapper;
	public ILightMapWrapper lightmap;
	public RenderBufferHandler renderBufferHandler;
	public IDhGenericRenderer genericRenderer;
	public Vec3d exactCameraPosition;
	/** @see DhRenderState#vanillaFogEnabled */
	public boolean vanillaFogEnabled;
	
	public boolean hasBeenValidated = false;
	
	
	
	//=============//
	// constructor //
	//=============//
	//region
	
	public RenderParams(EDhApiRenderPass renderPass, DhRenderState renderState)
	{
		this(renderPass,
			renderState.partialTickTime,
			renderState.mcProjectionMatrix, renderState.mcModelViewMatrix,
			renderState.clientLevelWrapper,
			renderState.vanillaFogEnabled
		);
	}
	private RenderParams(
			EDhApiRenderPass renderPass,
			float newPartialTicks,
			Mat4f newMcProjectionMatrix, Mat4f newMcModelViewMatrix,
			IClientLevelWrapper clientLevelWrapper,
			boolean vanillaFogEnabled
		)
	{
		super(renderPass,
			newPartialTicks,
			RenderUtil.getNearClipPlaneInBlocks(), RenderUtil.getFarClipPlaneDistanceInBlocks(),
			newMcProjectionMatrix, newMcModelViewMatrix,
			RenderUtil.createLodProjectionMatrix(newMcProjectionMatrix), RenderUtil.createLodModelViewMatrix(newMcModelViewMatrix),
			clientLevelWrapper.getMinHeight(),
			clientLevelWrapper);
		
		
		this.dhClientWorld = SharedApi.tryGetDhClientWorld();
		if (this.dhClientWorld != null)
		{
			this.dhClientLevel = this.dhClientWorld.getOrLoadClientLevel(clientLevelWrapper);
			if (this.dhClientLevel != null)
			{
				this.renderBufferHandler = this.dhClientLevel.getRenderBufferHandler();
				this.genericRenderer = this.dhClientLevel.getGenericRenderer();
			}
		}
		
		this.clientLevelWrapper = clientLevelWrapper;
		this.lightmap = MC_RENDER.getLightmapWrapper(this.clientLevelWrapper);
		
		if (MC_CLIENT.playerExists())
		{
			this.exactCameraPosition = MC_RENDER.getCameraExactPosition();
		}
		
		this.vanillaFogEnabled = vanillaFogEnabled;
		
	}
	
	//endregion
	
	
	
	//======================//
	// parameter validation //
	//======================//
	//region
	
	/** 
	 * Should be called before rendering is done.
	 * @return a message if LODs shouldn't be rendered, null if the LODs can render 
	 */
	public String getValidationErrorMessage(long firstRenderTimeMs)
	{
		// Note: all strings here should be constants to prevent String allocations
		
		this.hasBeenValidated = true;
		
		
		if (!MC_CLIENT.playerExists())
		{
			return "No Player Exists";
		}
		
		if (this.dhClientWorld == null)
		{
			return "No DH Client World Loaded";
		}
		
		if (this.dhClientLevel == null)
		{
			return "No DH Client Level Loaded";
		}
		
		if (this.clientLevelWrapper == null)
		{
			return "No Client Level Wrapper Loaded";
		}
		
		if (this.lightmap == null)
		{
			return "No Lightmap Loaded";
		}
		
		if (this.renderBufferHandler == null)
		{
			return "No RenderBufferHandler Present";
		}
		
		if (this.genericRenderer == null)
		{
			return "No Generic Renderer Present";
		}
		
		if (this.dhModelViewMatrix == null
			|| this.mcModelViewMatrix == null)
		{
			return "No MVM or Proj Matrix Given";
		}
		
		if (OPTIFINE_ACCESSOR != null
			&& MC_RENDER.getTargetFramebuffer() == -1)
		{
			// wait for MC to finish setting up their renderer
			return "Optifine Target Frame Buffer not set";
		}
		
		
		return null;
	}
	
	//endregion
	
	
	
}
