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

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

const bot = new Discord.Client();

async function initDB() {
    api.db = await open({
        filename: 'database/tardis.db',
        driver: sqlite3.Database
    });
}

const api = {
    db: undefined,
    queue: undefined,
    strings: require('./strings/'+process.env.STRING_SET+'.json'),
    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;
    })(),
    announcementVoice: process.env.ANNOUNCEMENT_VOICE,
    announcementEngine: undefined,

    respond: (message, text, voiceText) => {
        let toSend = message.member.displayName + ", " + (voiceText ? voiceText : text);
        if (message.member.voice.channel) {
            api.queue.add(__dirname + "/sysmsg.wav");
            api.speak(message.member.voice.channel, toSend);
        } else {
            message.reply(text);
        }
    },

    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.announcementEngine, voice = api.announcementVoice, params = {}) => {
        const filepath = await api.generateVoice(message, engine, voice, params);
        api.queue.add(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.content.split(" ");
        const command = args[0].substr(1, args[0].length);
        const execution = commandHandlers.get(command);
        if (command) {
            if (execution) execution(args, message);
        }
    }
}


api.announcementEngine = api.ttsEngines[process.env.ANNOUNCEMENT_ENGINE];
(async () => {
    await initDB();
    registerModules();
})()
bot.login(process.env.TOKEN);
bot.on('message', handleMessage);