const Discord = require('discord.js');
const Voice = require("@discordjs/voice");
const adapterCreator = require("./adapter");
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 joinedVoiceChannelConnections = new Map();

let modules = [];

let commandHandlers = new Map();
const player = Voice.createAudioPlayer();

const rest = new Discord.REST({ version: '10' }).setToken(process.env["TOKEN"]);
const bot = new Discord.Client({
    intents: [
        Discord.GatewayIntentBits.GuildMembers,
        Discord.GatewayIntentBits.GuildMessageReactions,
        Discord.GatewayIntentBits.GuildMessages,
        Discord.GatewayIntentBits.GuildPresences,
        Discord.GatewayIntentBits.GuildVoiceStates,
        Discord.GatewayIntentBits.Guilds,
        Discord.GatewayIntentBits.MessageContent
    ]
});

async function initDB() {
    console.log(__dirname);
    api.db = await open({
        filename: process.env["DB_FILE"],
        driver: sqlite3.Database
    });
}

const api = {
    player: player,
    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,

    play: (file) => {
        return player.play(Voice.createAudioResource(file));
    },
    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);
        }
    },

    getActiveVoiceChannel: () => joinedVoiceChannels[0],
    
    isInVoiceChannel: (channel) => {
        return joinedVoiceChannels.includes(channel);
    },

    getConnectionForVoiceChannel: (channel) => {
        return joinedVoiceChannelConnections.get(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 = Voice.joinVoiceChannel({
                channelId: channel.id,
                guildId: channel.guild.id,
                adapterCreator: adapterCreator(channel)
            });
            res.subscribe(player);
            joinedVoiceChannels.push(channel);
            joinedVoiceChannelConnections.set(channel, res);
        }
    },

    leaveChannel: async (channel) => {
        if (joinedVoiceChannels.includes(channel)) {
            let con = joinedVoiceChannelConnections.get(channel);
            joinedVoiceChannels = joinedVoiceChannels.filter((chan) => chan !== channel);
            con.disconnect();
            joinedVoiceChannelConnections.delete(channel);
        }
    },

    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}`));
        console.log(`Loading ./modules/${dir}/index.js`)
    })
    modules.forEach((mod) => mod(bot, api));
}

function handleMessage(message) {
    console.log(`I got 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 function start() {
    await initDB();
    registerModules();
}
bot.login(process.env.TOKEN);
bot.on('messageCreate', handleMessage);

start();