package de.yannicpunktdee.yoshibot.main;

import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
import com.sedmelluq.discord.lavaplayer.source.local.LocalAudioSourceManager;
import de.yannicpunktdee.yoshibot.audio.AudioPlayerListener;
import de.yannicpunktdee.yoshibot.audio.AudioSendHandlerImpl;
import de.yannicpunktdee.yoshibot.command.YoshiCommandContext;
import de.yannicpunktdee.yoshibot.command.YoshiCommandDistributor;
import de.yannicpunktdee.yoshibot.listeners.CommandLine;
import de.yannicpunktdee.yoshibot.listeners.DiscordEventListener;
import de.yannicpunktdee.yoshibot.utils.Logger;
import de.yannicpunktdee.yoshibot.utils.Resources;
import de.yannicpunktdee.yoshibot.utils.SauceProvider;
import lombok.Getter;
import lombok.SneakyThrows;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.Guild;

import javax.security.auth.login.LoginException;
import java.io.File;
import java.nio.file.Files;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Repräsentiert einen Yoshi-Bot. Der Bot initialisiert alle Ressourcen und schaltet sich in der startYoshiBot-Methode
 * online und beginnt dann zu lauschen. Parallel lauscht ein Thread auf Konsoleneingaben für administrative Zwecke, die
 * nicht über den Chat erledigt werden sollten.
 *
 * @author Yannic Link
 */
public final class YoshiBot {
	
	private CommandLine commandLineThread;
	
	/**
	 * Erlaubt es einige Einstellungen vor und nach der Erzeugung eines Bots vorzunehmen.
	 */
	public JDABuilder jdaBuilder;
	/**
	 * Instanz vom aktuell laufenden Bot.
	 */
	public JDA jda;
	/**
	 * LavaPlayer AudioPlayerManager.
	 */
	public AudioPlayerManager audioPlayerManager;
	
	public Guild guild;
	
	public AudioPlayer audioPlayer;
	
	private static YoshiBot instance = null;
	
	@Getter
	private final Random random = new Random();
	
	/**
	 * Initialisiert alle dynamisch hinzugefügten und statischen Ressourcen. Startet aber nicht den Bot selbst.
	 */
	public boolean init(String configPath) {
		return Resources.init(configPath);
	}
	
	/**
	 * Startet den Bot und schaltet ihn online. Beginnt auf Konsoleneingaben für administrative Zwecke zu lauschen.
	 *
	 * @throws LoginException Falls das Token ungültig ist.
	 */
	@SneakyThrows
	public void start() throws LoginException {
		System.out.println("Starte YoshiBot.");
		
		jdaBuilder = JDABuilder.createDefault(Resources.getJda_builder_string());
		jdaBuilder.setAutoReconnect(true);
		
		jdaBuilder.addEventListeners(new DiscordEventListener());
		
		audioPlayerManager = new DefaultAudioPlayerManager();
		audioPlayerManager.registerSourceManager(new LocalAudioSourceManager());
		AudioSourceManagers.registerRemoteSources(audioPlayerManager);
		
		jda = jdaBuilder.build();
		try {
			jda.awaitReady();
		} catch (InterruptedException e) {
			Logger.logError("Konnte nicht auf jda warten. Thread unterbrochen.");
			return;
		}
		
		jda.awaitReady();
		guild = jda.getGuildById(Resources.getGuild_id());
		
		audioPlayer = audioPlayerManager.createPlayer();
		audioPlayer.addListener(new AudioPlayerListener(guild.getAudioManager()));
		guild.getAudioManager().setSendingHandler(new AudioSendHandlerImpl(audioPlayer));
		
		jdaBuilder.setStatus(OnlineStatus.ONLINE);
		
		System.out.println("YoshiBot online.");
		
		commandLineThread = new CommandLine();
		commandLineThread.start();
		
		new SauceProvider(300);
		
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(YoshiBot::setRandomActivity, 0, 10, TimeUnit.HOURS);
	}
	
	public synchronized void stop() {
		commandLineThread.stopCommandLine();
		System.out.println("Beende YoshiBot ...");
		jdaBuilder.setStatus(OnlineStatus.OFFLINE);
		jda.shutdown();
		System.out.println("YoshiBot offline.");
	}
	
	/**
	 * Leitet den Context an den CommandDistributor weiter.
	 *
	 * @param command Der Kontext für das Kommando.
	 */
	public static void executeCommand(YoshiCommandContext command) {
		YoshiCommandDistributor.distribute(command);
	}
	
	public static YoshiBot getInstance() {
		if (YoshiBot.instance == null) {
			YoshiBot.instance = new YoshiBot();
		}
		return YoshiBot.instance;
	}
	
	@SneakyThrows
	private static void setRandomActivity() {
		YoshiBot yoshiBot = YoshiBot.getInstance();
		yoshiBot.jda.awaitReady();
		List<String> text = Files.readAllLines(new File(Resources.getActivitiesPath()).toPath());
		String activity = text.get(yoshiBot.random.nextInt(text.size()));
		yoshiBot.jda.getPresence().setActivity(Activity.playing(activity));
		Logger.logInfo("Setze Aktivität auf " + activity);
	}
	
}
