<commit_before>package me.minidigger.voxelgameslib.feature.features;

import com.google.gson.annotations.Expose;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Singleton;

import me.minidigger.voxelgameslib.components.inventory.BasicInventory;
import me.minidigger.voxelgameslib.components.inventory.InventoryHandler;
import me.minidigger.voxelgameslib.event.GameEvent;
import me.minidigger.voxelgameslib.event.events.game.GameJoinEvent;
import me.minidigger.voxelgameslib.feature.AbstractFeature;
import me.minidigger.voxelgameslib.feature.AbstractFeatureCommand;
import me.minidigger.voxelgameslib.feature.Feature;
import me.minidigger.voxelgameslib.feature.FeatureCommandImplementor;
import me.minidigger.voxelgameslib.feature.FeatureInfo;
import me.minidigger.voxelgameslib.game.DefaultGameData;
import me.minidigger.voxelgameslib.lang.Lang;
import me.minidigger.voxelgameslib.lang.LangKey;
import me.minidigger.voxelgameslib.map.MapInfo;
import me.minidigger.voxelgameslib.user.User;
import me.minidigger.voxelgameslib.user.UserHandler;
import me.minidigger.voxelgameslib.utils.ItemBuilder;
import me.minidigger.voxelgameslib.world.WorldConfig;

import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;

import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Syntax;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.java.Log;

@FeatureInfo(name = "VoteFeature", author = "MiniDigger", version = "1.0",
        description = "Allow players to vote on maps")
@Log
public class VoteFeature extends AbstractFeature implements FeatureCommandImplementor {
    //TODO add scoreboard
    @Getter
    @Setter
    @Expose
    private int maxMaps = 3;

    @Inject
    private WorldConfig config;
    @Inject
    private UserHandler userHandler;
    @Inject
    private InventoryHandler inventoryHandler;

    private Map<UUID, Integer> votes = new HashMap<>();
    private Map<Integer, MapInfo> availableMaps = new HashMap<>();

    @Getter
    @Setter
    private boolean enableVoteMenu = true;
    @Getter
    @Setter
    private ItemStack openMenuItem = new ItemStack(Material.PAPER, 1);

    @Override
    public void start() {
        String mode = getPhase().getGame().getGameMode().getName();
        int id = 0;
        for (MapInfo info : config.maps) {
            if (info.getGamemodes().contains(mode)) {
                availableMaps.put(id++, info);
            }

            if (id == maxMaps - 1) {
                break;
            }
        }

        if (availableMaps.size() == 0) {
            getPhase().getGame().broadcastMessage(LangKey.VOTE_NO_MAPS_FOUND);
            getPhase().getGame().abortGame();
            log.warning("Game " + getPhase().getGame().getUuid() + "(" + getPhase().getGame().getGameMode().getName() + ")" +
                    " was aborted because it didn't find any maps to play!");
            return;
        }

        getPhase().getGame().getPlayers().forEach(this::sendVoteMessage);
        if (enableVoteMenu) {
            getPhase().getGame().getPlayers().forEach(user -> user.getPlayer().getInventory().setItem(0, openMenuItem));
        }
    }

    @Override
    public void stop() {
        Map<Integer, Integer> votes = new HashMap<>();
        int max = -1;
        int maxMap = -1;
        for (Integer map : this.votes.values()) {
            int old = votes.getOrDefault(map, 0);
            old++;
            if (old > max) {
                max = old;
                maxMap = map;
            }
            votes.put(map, old);
        }

        MapInfo winner = availableMaps.get(maxMap);
        if (winner == null) {
            // use first map if nobody won
            winner = availableMaps.values().iterator().next();
        }

        DefaultGameData gameData = getPhase().getGame().getGameData(DefaultGameData.class).orElse(new DefaultGameData());
        gameData.voteWinner = winner;
        getPhase().getGame().putGameData(gameData);
        getPhase().getGame().broadcastMessage(LangKey.VOTE_END, winner.getName(), winner.getAuthor(), max);
    }

    @Override
    public void tick() {

    }

    @Override
    public void init() {

    }

    @Override
    public AbstractFeatureCommand getCommandClass() {
        return new VoteFeatureCommand();
    }

    /**
     * Sends the vote message to that user
     *
     * @param user the user that should receive the message
     */
    public void sendVoteMessage(User user) {
        Lang.msg(user, LangKey.VOTE_MESSAGE_TOP);
        for (int id : availableMaps.keySet()) {
            MapInfo info = availableMaps.get(id);
            Lang.msg(user, LangKey.VOTE_MESSAGE_MAP, id, info.getName(), info.getAuthor());
        }
        Lang.msg(user, LangKey.VOTE_MESSAGE_BOT);
    }

    @GameEvent
    public void onJoin(@Nonnull GameJoinEvent event) {
        log.finer("send vote msg");
        sendVoteMessage(event.getUser());
    }

    @GameEvent
    public void openVoteMenu(@Nonnull PlayerInteractEvent event) {
        userHandler.getUser(event.getPlayer().getUniqueId()).ifPresent(user -> {
            if ((event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) && event.getItem() == openMenuItem) {
                int start = 18;
                BasicInventory basicInventory = inventoryHandler.createInventory(BasicInventory.class, event.getPlayer(), "Vote for a map", 27);
                for (int id : availableMaps.keySet()) {
                    MapInfo info = availableMaps.get(id);
                    ItemStack item = new ItemBuilder(Material.PAPER).amount(id).color(Color.PURPLE).name(info.getName()).lore(info.getAuthor()).build();
                    basicInventory.getBukkitInventory().setItem(start, item);
                    basicInventory.addClickAction(item, ((itemStack, inventoryClickEvent) -> confirmVote(user, id)));
                }
            }
        });
    }

    /**
     * Confirms a vote for a map
     */
    private void confirmVote(User voter, Integer mapId) {
        if (votes.containsKey(voter.getUuid())) {
            Lang.msg(voter, LangKey.VOTE_ALREADY_VOTED);<commit_msg>fix votemenu item not being handed out<commit_after>package me.minidigger.voxelgameslib.feature.features;

import com.google.gson.annotations.Expose;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Singleton;

import me.minidigger.voxelgameslib.VoxelGamesLib;
import me.minidigger.voxelgameslib.components.inventory.BasicInventory;
import me.minidigger.voxelgameslib.components.inventory.InventoryHandler;
import me.minidigger.voxelgameslib.event.GameEvent;
import me.minidigger.voxelgameslib.event.events.game.GameJoinEvent;
import me.minidigger.voxelgameslib.feature.AbstractFeature;
import me.minidigger.voxelgameslib.feature.AbstractFeatureCommand;
import me.minidigger.voxelgameslib.feature.Feature;
import me.minidigger.voxelgameslib.feature.FeatureCommandImplementor;
import me.minidigger.voxelgameslib.feature.FeatureInfo;
import me.minidigger.voxelgameslib.game.DefaultGameData;
import me.minidigger.voxelgameslib.lang.Lang;
import me.minidigger.voxelgameslib.lang.LangKey;
import me.minidigger.voxelgameslib.map.MapInfo;
import me.minidigger.voxelgameslib.user.User;
import me.minidigger.voxelgameslib.user.UserHandler;
import me.minidigger.voxelgameslib.utils.ItemBuilder;
import me.minidigger.voxelgameslib.world.WorldConfig;

import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;

import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Syntax;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.java.Log;

@FeatureInfo(name = "VoteFeature", author = "MiniDigger", version = "1.0",
        description = "Allow players to vote on maps")
@Log
public class VoteFeature extends AbstractFeature implements FeatureCommandImplementor {
    //TODO add scoreboard
    @Getter
    @Setter
    @Expose
    private int maxMaps = 3;

    @Inject
    private WorldConfig config;
    @Inject
    private UserHandler userHandler;
    @Inject
    private InventoryHandler inventoryHandler;
    @Inject
    private VoxelGamesLib voxelGamesLib;

    private Map<UUID, Integer> votes = new HashMap<>();
    private Map<Integer, MapInfo> availableMaps = new HashMap<>();

    @Getter
    @Setter
    @Expose
    private boolean enableVoteMenu = true;
    @Getter
    @Setter
    //@Expose TODO figure out how we want to expose items
    private ItemStack openMenuItem = new ItemStack(Material.PAPER, 1);

    @Override
    public void start() {
        String mode = getPhase().getGame().getGameMode().getName();
        int id = 0;
        for (MapInfo info : config.maps) {
            if (info.getGamemodes().contains(mode)) {
                availableMaps.put(id++, info);
            }

            if (id == maxMaps - 1) {
                break;
            }
        }

        if (availableMaps.size() == 0) {
            getPhase().getGame().broadcastMessage(LangKey.VOTE_NO_MAPS_FOUND);
            getPhase().getGame().abortGame();
            log.warning("Game " + getPhase().getGame().getUuid() + "(" + getPhase().getGame().getGameMode().getName() + ")" +
                    " was aborted because it didn't find any maps to play!");
            return;
        }

        getPhase().getGame().getPlayers().forEach(this::sendVoteMessage);
        if (enableVoteMenu) {
            getPhase().getGame().getPlayers().forEach(this::giveVoteMenuItem);
        }
    }

    @Override
    public void stop() {
        Map<Integer, Integer> votes = new HashMap<>();
        int max = -1;
        int maxMap = -1;
        for (Integer map : this.votes.values()) {
            int old = votes.getOrDefault(map, 0);
            old++;
            if (old > max) {
                max = old;
                maxMap = map;
            }
            votes.put(map, old);
        }

        MapInfo winner = availableMaps.get(maxMap);
        if (winner == null) {
            // use first map if nobody won
            winner = availableMaps.values().iterator().next();
        }

        DefaultGameData gameData = getPhase().getGame().getGameData(DefaultGameData.class).orElse(new DefaultGameData());
        gameData.voteWinner = winner;
        getPhase().getGame().putGameData(gameData);
        getPhase().getGame().broadcastMessage(LangKey.VOTE_END, winner.getName(), winner.getAuthor(), max);
    }

    @Override
    public void tick() {

    }

    @Override
    public void init() {

    }

    @Override
    public AbstractFeatureCommand getCommandClass() {
        return new VoteFeatureCommand();
    }

    /**
     * Sends the vote message to that user
     *
     * @param user the user that should receive the message
     */
    public void sendVoteMessage(User user) {
        log.finer("send vote msg");
        Lang.msg(user, LangKey.VOTE_MESSAGE_TOP);
        for (int id : availableMaps.keySet()) {
            MapInfo info = availableMaps.get(id);
            Lang.msg(user, LangKey.VOTE_MESSAGE_MAP, id, info.getName(), info.getAuthor());
        }
        Lang.msg(user, LangKey.VOTE_MESSAGE_BOT);
    }

    public void giveVoteMenuItem(User user) {
        log.finer("give vote menu item");
        new BukkitRunnable() {
            @Override
            public void run() {
                if(!getPhase().isRunning())return;
                user.getPlayer().getInventory().setItem(0, openMenuItem);
                user.getPlayer().updateInventory();
            }
        }.runTaskLater(voxelGamesLib, 5); // delay because we might be in progress of switching worlds
    }

    @GameEvent
    public void onJoin(@Nonnull GameJoinEvent event) {
        sendVoteMessage(event.getUser());
        if (enableVoteMenu) {
            giveVoteMenuItem(event.getUser());
        }
    }

    @GameEvent
    public void openVoteMenu(@Nonnull PlayerInteractEvent event) {
        userHandler.getUser(event.getPlayer().getUniqueId()).ifPresent(user -> {
            if ((event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) && event.getItem() == openMenuItem) {
                int start = 18;
                BasicInventory basicInventory = inventoryHandler.createInventory(BasicInventory.class, event.getPlayer(), "Vote for a map", 27);
                for (int id : availableMaps.keySet()) {
                    MapInfo info = availableMaps.get(id);
                    ItemStack item = new ItemBuilder(Material.PAPER).amount(id).color(Color.PURPLE).name(info.getName()).lore(info.getAuthor()).build();
                    basicInventory.getBukkitInventory().setItem(start, item);
                    basicInventory.addClickAction(item, ((itemStack, inventoryClickEvent) -> confirmVote(user, id)));
                }
            }
        });
    }

    /**
     * Confirms a vote for a map
     */
    private void confirmVote(User voter, Integer mapId) {
        if (votes.containsKey(voter.getUuid())) {
            Lang.msg(voter, LangKey.VOTE_ALREADY_VOTED);
{'fraction_non_alphanumeric': 0.11562841530054645, 'fraction_numerical': 0.0022586520947176685, 'mean_word_length': 2.817018909899889, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '832115', 'n_tokens_mistral': 4219, 'n_tokens_neox': 4064, 'n_words': 795}