package cz.jzitnik.game.generation;

import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.entities.items.ItemBlockSupplier;
import cz.jzitnik.game.entities.items.ItemType;
import cz.jzitnik.game.sprites.Steve;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;

public class Generation {
    public static void generateWorld(Game game) {
        var world = game.getWorld();
        initializeWorld(world);

        Block steveBlock = new Block("steve", SpriteLoader.SPRITES.STEVE);
        steveBlock.setSpriteState(Steve.SteveState.FIRST);
        steveBlock.setGhost(true);
        Block steveBlock2 = new Block("steve", SpriteLoader.SPRITES.STEVE);
        steveBlock2.setSpriteState(Steve.SteveState.SECOND);
        steveBlock2.setGhost(true);

        int[] terrainHeight = generateTerrain();

        game.getPlayer().setPlayerBlock1(steveBlock);
        game.getPlayer().setPlayerBlock2(steveBlock2);

        populateWorld(world, terrainHeight);
        plantTrees(world, terrainHeight);

        // Spawn player at a valid starting point
        world[terrainHeight[256] - 1][256].add(steveBlock2);
        world[terrainHeight[256] - 2][256].add(steveBlock);
    }

    private static void initializeWorld(List<Block>[][] world) {
        for (int i = 0; i < 256; i++) {
            for (int j = 0; j < 512; j++) {
                world[i][j] = new CopyOnWriteArrayList<>();
            }
        }
    }

    private static int[] generateTerrain() {
        Random random = new Random();
        int baseHeight = 120;
        int[] terrainHeight = new int[512];

        terrainHeight[0] = baseHeight;
        for (int i = 1; i < 512; i++) {
            int heightChange = random.nextInt(3) - 1;
            terrainHeight[i] = Math.max(100, Math.min(140, terrainHeight[i - 1] + heightChange));
        }

        for (int i = 2; i < 510; i++) {
            terrainHeight[i] = (terrainHeight[i - 1] + terrainHeight[i] + terrainHeight[i + 1]) / 3;
        }

        return terrainHeight;
    }

    private static void populateWorld(List<Block>[][] world, int[] terrainHeight) {
        for (int i = 0; i < 512; i++) {
            int hillHeight = terrainHeight[i];

            world[hillHeight][i].add(ItemBlockSupplier.getBlock("grass"));

            for (int j = 1; j <= 4; j++) {
                if (hillHeight + j < 256) {
                    world[hillHeight + j][i].add(ItemBlockSupplier.getBlock("dirt"));
                }
            }

            for (int j = hillHeight + 5; j < 250; j++) {
                world[j][i].add(ItemBlockSupplier.getBlock("stone"));
            }

            world[255][i].add(new Block("bedrock", SpriteLoader.SPRITES.BEDROCK));
        }

        for (List<Block>[] lists : world) {
            for (List<Block> list : lists) {
                list.addFirst(new Block("air", SpriteLoader.SPRITES.AIR, true, false));
            }
        }
    }

    private static void plantTrees(List<Block>[][] world, int[] terrainHeight) {
        Random random = new Random();
        for (int i = 10; i < 502; i += random.nextInt(20) + 20) {
            int treeBase = terrainHeight[i];

            if (treeBase - 3 < 0) continue;

            for (int j = 0; j < 3; j++) {
                if (treeBase - j >= 0) {
                    world[treeBase - j - 1][i].add(ItemBlockSupplier.getBlock("oak_log"));
                }
            }

            int leafY = treeBase - 4;

            for (int layer = 0; layer < 3; layer++) {
                int size = 5 - (layer * 2);
                int offsetY = leafY - layer;

                for (int dx = -size / 2; dx <= size / 2; dx++) {
                    for (int dy = -size / 2; dy <= size / 2; dy++) {
                        if (i + dx >= 0 && i + dx < world[0].length && offsetY >= 0) {
                            world[offsetY][i + dx].add(new Block("oak_leaves", SpriteLoader.SPRITES.OAK_LEAF, 1, ItemType.SHEARS, new ArrayList<>()));
                        }
                    }
                }
            }
        }
    }
}