const Discord = require('discord.js');
require('dotenv').config();
const fetch = require('node-fetch');
const fs = require('fs');
const sha1 = require('sha1');
const sqlite = require('sqlite3');

let joinedVoiceChannels = [];
let modules = [];
let commandHandlers = new Map();

const bot = new Discord.Client();

const db = new sqlite.Database(process.env.DB_FILE);

const api = {
    db: db,
    ttsEngines: (() => {
		let engines={};
	    console.log(`Registering TTS engines...`);
	    const engineDirectories = fs.readdirSync('./tts');
	    engineDirectories.forEach((dir) => {
			if(dir.startsWith('.')) return;
	        eng=require(`./tts/${dir}/index.js`);
			engines[dir]=new eng;
	        console.log(`Loading ./tts/${dir}/index.js`)
	    })
    	return engines;
    })(),
	
    isInVoiceChannel: (channel) => {
        return joinedVoiceChannels.includes(channel);
    },

    getConnectionForVoiceChannel: (channel) => {
        return bot.voice.connections.find((conn) => conn.channel === channel);
    },

    generateVoice: async (string, engine, voice, params) => {
        const hash = sha1(voice+string);
        const filepath = process.env.VOICE_TMP_PATH + hash + '.' + engine.fileExtension;
        if (!fs.existsSync(filepath)) {
            await engine.getSpeechFile(string, filepath, voice, params);
        }
        return filepath;
    },

    joinChannel: async (channel) => {
        if (!api.isInVoiceChannel(channel)) {
            const res = await channel.join();
            joinedVoiceChannels.push(channel);
        }
    },

    leaveChannel: async (channel) => {
        if (joinedVoiceChannels.includes(channel)) {
            joinedVoiceChannels = joinedVoiceChannels.filter((chan) => chan !== channel);
            await channel.leave();
        }
    },

    speak: async (channel, message, engine=api.ttsEngines.gtranslate, voice='en-us', params={}) => {
        const conn = api.getConnectionForVoiceChannel(channel);
        const filepath = await api.generateVoice(message, engine, voice, params);
        if (conn) conn.play(filepath);
    },

    registerCommand: async (commandString, commandFunc) => {
        commandHandlers.set(commandString, commandFunc);
    }
}

function registerModules() {
	console.log(`Registering modules...`);
    const moduleDirectories = fs.readdirSync('./modules');
    moduleDirectories.forEach((dir) => {
		if(dir.startsWith('.')) return;
        modules.push(require(`./modules/${dir}/index.js`));
        console.log(`Loading ./modules/${dir}/index.js`)
    })
    modules.forEach((mod) => mod(bot, api));
}

function handleMessage(message) {
    if (message.content.startsWith(process.env.PREFIX)) {
        const args = message.contents.split(" ");
        const command = args[0].substr(1, args[0].length);
        const execution = commandHandlers.get(command);
        if (command) {
            command(args, message);
        }
    }
}

registerModules();
bot.login(process.env.TOKEN);
bot.on('message', handleMessage);