package net.programmer.igoodie.twitchspawn.tslanguage.action;

import net.minecraft.entity.player.ServerPlayerEntity;
import net.programmer.igoodie.twitchspawn.TwitchSpawn;
import net.programmer.igoodie.twitchspawn.configuration.ConfigManager;
import net.programmer.igoodie.twitchspawn.tslanguage.EventArguments;
import net.programmer.igoodie.twitchspawn.tslanguage.keyword.TSLActionKeyword;
import net.programmer.igoodie.twitchspawn.tslanguage.parser.TSLParser;
import net.programmer.igoodie.twitchspawn.tslanguage.parser.TSLSyntaxError;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class ReflectAction extends TSLAction {

    private boolean reflectEveryone;
    private int reflectRandomN;
    private List<String> reflectedUsers;
    private TSLAction action;

    public ReflectAction(List<String> words) throws TSLSyntaxError {
        this.silent = true;

        if (words.size() < 2)
            throw new TSLSyntaxError("Invalid length of words: " + words);

        // "REFLECT %% REFLECT %% ..." is not allowed
        String reflectActionName = TSLActionKeyword.ofClass(getClass());
        if (words.stream().anyMatch(word -> word.equalsIgnoreCase(reflectActionName)))
            throw new TSLSyntaxError("Cannot have a cyclic REFLECT rule.");

        String usersRaw = words.get(0);

        if (usersRaw.equals("*")) {
            this.reflectEveryone = true;
            this.reflectRandomN = 0;
            this.reflectedUsers = null;

        } else if (usersRaw.matches("\\d+")) {
            this.reflectEveryone = false;
            this.reflectRandomN = parseInt(usersRaw);
            this.reflectedUsers = null;

        } else {
            this.reflectEveryone = false;
            this.reflectRandomN = 0;
            this.reflectedUsers = Arrays.asList(usersRaw.split(",\\s*"));
        }

        this.action = TSLParser.parseAction(words.get(1), words.size() > 2
                ? words.subList(2, words.size())
                : Collections.emptyList());
    }

    @Override
    protected void performAction(ServerPlayerEntity player, EventArguments args) {
        action.reflectedUser = null;
        action.process(args);

        // Select where to reflect
        List<String> reflectedUsers = getReflectedPlayers(player);

        // Reflect to selected users
        reflectedUsers.forEach(username -> {
            if (username.equalsIgnoreCase(args.streamerNickname)) {
                TwitchSpawn.LOGGER.warn("Tried to reflect back to the streamer. Skipping reflection for them.");
                return;
            }

            ServerPlayerEntity reflectedPlayer = getPlayer(username);

            if (reflectedPlayer == null) {
                TwitchSpawn.LOGGER.info("{} was not online on the server. Skipping reflection for them", username);
                return;
            }

            // If user is streamer, queue. Otherwise, do instantly
            if (ConfigManager.RULESET_COLLECTION.hasStreamer(username)) {
                ConfigManager.RULESET_COLLECTION.getQueue(username)
                        .queue(() -> reflectAction(reflectedPlayer, args));

            } else {
                reflectAction(reflectedPlayer, args);
            }
        });
    }

    private List<String> getReflectedPlayers(ServerPlayerEntity player) {
        if (this.reflectEveryone) {
            return Arrays.asList(player.getServer().getOnlinePlayerNames());
        }

        if (this.reflectRandomN != 0) {
            List<String> reflectedPlayers = new LinkedList<>();
            List<String> onlinePlayers = new LinkedList<>(Arrays.asList(player.getServer().getOnlinePlayerNames()));

            for (int i = 0; i < reflectRandomN; i++) {
                if (onlinePlayers.size() == 0) break;

                int index = (int) (Math.random() * onlinePlayers.size());
                String selectedPlayer = onlinePlayers.remove(index);
                reflectedPlayers.add(selectedPlayer);
            }

            return reflectedPlayers;
        }

        return this.reflectedUsers;
    }


    private void reflectAction(ServerPlayerEntity player, EventArguments args) {
        action.reflectedUser = player;

        String title = action.titleMessage(args);
        String subtitle = action.subtitleMessage(args);

        action.notifyPlayer(player, title, subtitle);
        action.performAction(player, args);

        TwitchSpawn.LOGGER.info("Reflected {} action to {}", TSLActionKeyword.ofClass(action.getClass()), player);
    }

}

@@ -56,16 +56,72 @@
 ction;%0A%0A
+import jdk.nashorn.internal.runtime.regexp.joni.Config;%0A
 import n
@@ -167,16 +167,16 @@
 Entity;%0A
-
 import n
@@ -289,24 +289,89 @@
 figManager;%0A
+import net.programmer.igoodie.twitchspawn.eventqueue.EventQueue;%0A
 import net.p
@@ -810,16 +810,58 @@
 tion %7B%0A%0A
+    private boolean onlyReflectedPlayers;%0A
     priv
@@ -1484,24 +1484,242 @@
 T rule.%22);%0A%0A
+        LinkedList%3CString%3E wordsCloned = new LinkedList%3C%3E(words);%0A%0A        if (wordsCloned.get(0).equalsIgnoreCase(%22ONLY%22)) %7B%0A            this.onlyReflectedPlayers = true;%0A            wordsCloned.remove(0);%0A        %7D%0A%0A
         Stri
@@ -1733,24 +1733,30 @@
 sRaw = words
+Cloned
 .get(0);%0A%0A  
@@ -2322,16 +2322,22 @@
 on(words
+Cloned
 .get(1),
@@ -2338,24 +2338,30 @@
 et(1), words
+Cloned
 .size() %3E 2%0A
@@ -2383,16 +2383,22 @@
  ? words
+Cloned
 .subList
@@ -2402,24 +2402,30 @@
 ist(2, words
+Cloned
 .size())%0A   
@@ -2606,16 +2606,223 @@
 = null;%0A
+%0A        if (onlyReflectedPlayers) %7B%0A            EventQueue eventQueue = ConfigManager.RULESET_COLLECTION.getQueue(args.streamerNickname);%0A            eventQueue.cancelUpcomingSleep();%0A%0A        %7D else %7B%0A    
         
@@ -2838,24 +2838,34 @@
 ocess(args);
+%0A        %7D
 %0A%0A        //

add REFLECT ONLY syntax