package roycurtis.jdiscordirc.managers;

import net.dv8tion.jda.core.events.guild.member.GuildMemberJoinEvent;
import net.dv8tion.jda.core.events.guild.member.GuildMemberLeaveEvent;
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
import org.pircbotx.Colors;
import org.pircbotx.User;
import roycurtis.jdiscordirc.util.CurrentThread;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import static roycurtis.jdiscordirc.JDiscordIRC.*;

/**
 * Acts as a coordinator of events between IRC and Discord. It forces the handling of incoming
 * events (from either IRC or Discord) to happen in the main thread, in a queued fashion. This is
 * to ensure a reliable timeline of events.
 *
 * For example: IRC might be sending a bunch of messages and Discord is busy processing them. During
 * which, IRC loses connection. With this system, Discord will not suddenly dump a disconnect
 * message in the middle of other messages (as has happened during testing, without this system).
 */
public class BridgeManager
{
    //<editor-fold desc="Manager methods (main thread)">
    private Queue<Runnable> queue = new ConcurrentLinkedQueue<>();

    /** Executes a task from the queue in the main thread, to maintain order */
    public void pump()
    {
        Runnable task = queue.poll();

        if (task == null)
        {
            CurrentThread.sleep(50);
            return;
        }

        try
        {
            task.run();
        }
        catch (Exception e)
        {
            log( "[!] Exception during bridge event: %s", e.getMessage() );
            e.printStackTrace();
        }

        log( "[Bridge] Pumped events; %d remain", queue.size() );
        CurrentThread.sleep(10);
    }
    //</editor-fold>

    //<editor-fold desc="IRC->Discord (via IRC thread)">
    public void onIRCConnect()
    {
        queue.add( () -> {
            DISCORD.sendMessage("Connected to IRC");
            DISCORD.setStatus(OnlineStatus.ONLINE);

            // Courtesy message for those on IRC
            IRC.sendMessage( DISCORD.isAvailable()
                ? "Hello! I am now bridging chat between Discord and IRC"
                : "Hello! I will bridge chat between Discord and IRC momentarily..."
            );
        } );
    }

    public void onIRCDisconnect()
    {
        queue.add( () -> {
            DISCORD.sendMessage("Lost connection to IRC; reconnecting...");
            DISCORD.setStatus(OnlineStatus.DO_NOT_DISTURB);
        } );
    }

    public void onIRCMessage(User user, String message)
    {
        queue.add( () -> DISCORD.sendMessageAs(user.getNick() + " (IRC)", "%s", message) );
    }

    public void onIRCAction(User user, String action)
    {
        queue.add( () -> DISCORD.sendMessageAs(user.getNick() + " (IRC)", "_%s_", action) );
    }

    public void onIRCJoin(User user)
    {
        queue.add( () -> DISCORD.sendMessage( "%s joined the channel", user.getNick() ) );
    }

    public void onIRCPart(User user, String reason)
    {
        queue.add( () -> DISCORD.sendMessage("%s left the channel (%s)", user.getNick(), reason) );
    }

    public void onIRCQuit(User user, String reason)
    {
        queue.add( () -> DISCORD.sendMessage("%s quit the server (%s)", user.getNick(), reason) );
    }

    public void onIRCKick(User target, User kicker, String reason)
    {
        queue.add( () -> {
            DISCORD.sendMessage("%s was kicked by %s (%s)",
                target.getNick(),
                kicker.getNick(),
                reason
            );
        });
    }

    public void onIRCNickChange(String oldNick, String newNick)
    {
        queue.add( () -> DISCORD.sendMessage("%s changed nick to %s", oldNick, newNick) );
    }
    //</editor-fold>

    //<editor-fold desc="Discord->IRC (via Discord thread)">
    public void onDiscordConnect()
    {
        queue.add( () -> {
            IRC.sendMessage("Connected to Discord");

            // Courtesy message for those on Discord
            DISCORD.sendMessage( IRC.isAvailable()
                ? "Hello! I am now bridging chat between Discord and IRC"
                : "Hello! I will bridge chat between Discord and IRC momentarily..."
            );
        } );
    }

    public void onDiscordDisconnect()
    {
        IRC.sendMessage("Lost connection to Discord; reconnecting. . .");
    }

    public void onDiscordMessage(MessageReceivedEvent event)
    {
        String msg = event.getMessage().getContent();
        String who = event.getMember().getEffectiveName();

        // Special handling for Discord action messages
        if ( msg.startsWith("_") && msg.endsWith("_") )
            IRC.sendAction( who, msg.substring( 1, msg.length() - 1 ) );
        else
            IRC.sendMessage("<%s%s%s> %s",
                Colors.BOLD, who, Colors.NORMAL,
                msg);
    }

    public void onDiscordUserJoin(GuildMemberJoinEvent event)
    {
        IRC.sendMessage( "%s joined the server", event.getMember().getEffectiveName() );
    }

    public void onDiscordUserLeave(GuildMemberLeaveEvent event)
    {
        IRC.sendMessage( "%s quit the server", event.getMember().getEffectiveName() );
    }
    //</editor-fold>
}

@@ -4286,32 +4286,63 @@
 connect()%0A    %7B%0A
+        queue.add( () -%3E %7B%0A    
         IRC.send
@@ -4395,24 +4395,36 @@
 ing. . .%22);%0A
+        %7D);%0A
     %7D%0A%0A    p
@@ -4477,32 +4477,63 @@
 nt event)%0A    %7B%0A
+        queue.add( () -%3E %7B%0A    
         String m
@@ -4574,24 +4574,28 @@
 ();%0A        
+   
+ 
 String who =
@@ -4642,16 +4642,20 @@
         
+    
 // Speci
@@ -4698,24 +4698,28 @@
 ges%0A        
+  
+  
 if ( msg.sta
@@ -4754,16 +4754,20 @@
 (%22_%22) )%0A
+    
         
@@ -4843,13 +4843,21 @@
     
+    
 else%0A
+    
     
@@ -4895,16 +4895,20 @@
 s%3E %25s%22,%0A
+    
         
@@ -4964,19 +4964,35 @@
         
-msg
+    msg);%0A        %7D
 );%0A    %7D
@@ -5053,32 +5053,63 @@
 nt event)%0A    %7B%0A
+        queue.add( () -%3E %7B%0A    
         IRC.send
@@ -5177,24 +5177,36 @@
 veName() );%0A
+        %7D);%0A
     %7D%0A%0A    p
@@ -5262,32 +5262,63 @@
 nt event)%0A    %7B%0A
+        queue.add( () -%3E %7B%0A    
         IRC.send
@@ -5384,24 +5384,36 @@
 veName() );%0A
+        %7D);%0A
     %7D%0A    //

Fix Discord events not being in queue