/*
 * Copyright (c) 2022 Tectonicus contributors.  All rights reserved.
 *
 * This file is part of Tectonicus. It is subject to the license terms in the LICENSE file found in
 * the top-level directory of this distribution.  The full list of project contributors is contained
 * in the AUTHORS file found in the same location.
 *
 */

package tectonicus;

import tectonicus.chunk.ChunkCoord;
import tectonicus.raw.RawChunk;

public class CaveMaskFactory113 implements BlockMaskFactory
{
	@Override
	public BlockMask createMask(ChunkCoord coord, RawChunk rawChunk)
	{
		int[][] heights = calcSmoothedSurfaceHeights(rawChunk);

		BlockMask mask = new BlockMask();
		mask.setAllVisible();

		for (int x=0; x<RawChunk.WIDTH; x++)
		{
			for (int z=0; z<RawChunk.DEPTH; z++)
			{
				final int surfaceHeight = heights[x][z];

				for (int y = surfaceHeight; y< Minecraft.getChunkHeight(); y++)
				{
					mask.setVisible(x, y, z, false);
				}
			}
		}

		return mask;
	}

	public static int[][] calcSmoothedSurfaceHeights(RawChunk rawChunk)
	{
		int[][] heights = calcSurfaceHeights(rawChunk);
		return smoothSurfaceHeights(heights);
	}

	private static int[][] calcSurfaceHeights(RawChunk rawChunk)
	{
		int[][] heights = new int[RawChunk.WIDTH][RawChunk.DEPTH];

		for (int x=0; x<RawChunk.WIDTH; x++)
		{
			for (int z=0; z<RawChunk.DEPTH; z++)
			{
				final int surfaceHeight = calcSurfaceHeight(rawChunk, x, z);
				heights[x][z] = surfaceHeight;
			}
		}

		return heights;
	}

	private static int[][] smoothSurfaceHeights(int[][] heights)
	{
		int[][] smoothedHeights = new int[RawChunk.WIDTH][RawChunk.DEPTH];

		for (int x=0; x<RawChunk.WIDTH; x++)
		{
			for (int z=0; z<RawChunk.DEPTH; z++)
			{
				final int N = getHeight(x-1, z, heights);
				final int S = getHeight(x+1, z, heights);
				final int E = getHeight(x, z-1, heights);
				final int W = getHeight(1, z+1, heights);

				final int minHeight = Math.min(N, Math.min(E, Math.min(S, W)));

				smoothedHeights[x][z] = minHeight;
			}
		}

		return smoothedHeights;
	}

	// Calculate by casting a ray down vertically
	private static int calcSurfaceHeight(RawChunk rawChunk, final int x, final int z)
	{
		int penetration = 0;

		int y;
		for (y=Minecraft.getChunkHeight()-1; y>=0; y--)
		{
			final String blockName = rawChunk.getBlockName(x, y, z);
			if (blockName.equals("minecraft:air")
					|| blockName.equals("minecraft:water")
					|| blockName.contains("leaves")
					|| blockName.contains("log")
					|| blockName.contains("sapling")
					|| blockName.contains("mushroom")
					|| blockName.equals("minecraft:tall_grass"))
			{
				// Probably an above surface block

				// Idea: reset penetration? Or penetration = Math.max(penetration-1, 0) ?

				// Decay penetration
				penetration = Math.max(penetration-2, 0);
			}
			else
			{
				penetration++;
			}

			if (penetration >= 5)
			{
				break;
			}
		}

		y = Math.max(y-2, 0);
		return y;
	}

	private static int getHeight(int x, int z, int[][] heights)
	{
		x = Math.max(x, 0);
		x = Math.min(x, RawChunk.WIDTH-1);

		z = Math.max(z, 0);
		z = Math.min(z, RawChunk.DEPTH-1);

		return heights[x][z];
	}
}
