//dotenv
require('dotenv').config();

//ffmpeg
var ffmpeg = require('fluent-ffmpeg');

//nextcloud client
const { Client, Server, GetFilesRecursivelyCommand, CommandStatus } = require("nextcloud-node-client");
const server = new Server({
  basicAuth: {
    password: process.env.nextcloud_PASSWD,
    username: process.env.nextcloud_ID,
  },
  url: process.env.nextcloud_URL,
});

//built-in
const path = require("path");
const fs = require('fs').promises;
const util = require('util');
// const { pipeline } = require('stream');
// const pump = util.promisify(pipeline);

//uuid
const {
  v1: uuidv1,
  v4: uuidv4,
} = require('uuid');

//short-uuid
const short = require('short-uuid');

//moment
const moment = require("moment-timezone");

//fastify
const fastify = require("fastify")({
  logger: false,
});
fastify.register(require("fastify-static"), {
  root: path.join(__dirname, "public"),
  prefix: "/"
});
fastify.register(require("fastify-formbody"));
fastify.register(require("fastify-multipart"));
fastify.register(require("point-of-view"), {
  engine: {
    handlebars: require("handlebars")
  }
});

//socket.io
var io = require("socket.io")(fastify.server, {
  pingInterval: 1000,
  pingTimeout: 3000
});

//get '/'
fastify.get("/", async function (request, reply) {
  //get list
  let list = await fs.readdir(process.env.userdata);
  list.reverse();

  // console.log(list);

  let folders = [];
  for (const item of list) {
    let json = await fs.readFile(process.env.userdata + item + '/fields.json')
    .catch((err) => {
      console.error(err);
    });
    if (json != undefined) {
      var fields = JSON.parse(json.toString('utf8'));
      folders.push({
        foldername: item,
        group: fields.group,
        title: fields.title,
        comment: fields.comment,
        anchor: fields.anchor
      });
    }
  }

  // console.log(folders);

  //
  reply.view("/src/pages/parade.html", {
    list: folders
  });
});

//get '/projection'
fastify.get("/projection", async function (request, reply) {
  //get list
  let list = await fs.readdir(process.env.userdata);
  list.reverse();

  // console.log(list);

  let folders = [];
  for (const item of list) {
    let json = await fs.readFile(process.env.userdata + item + '/fields.json')
    .catch((err) => {
      console.error(err);
    });
    if (json != undefined) {
      var fields = JSON.parse(json.toString('utf8'));
      folders.push({
        foldername: item,
        group: fields.group,
        title: fields.title,
        comment: fields.comment,
        anchor: fields.anchor
      });
    }
  }

  // console.log(folders);

  //
  reply.view("/src/pages/projection.html", {
    list: folders
  });
});

//get '/en/'
fastify.get("/en/", async function (request, reply) {
  reply.view("/src/pages/parade.en.html", {});
});

//get '/list'
fastify.get("/list", async function (request, reply) {
  //get list
  let list = await fs.readdir(process.env.userdata);
  list.reverse();

  // console.log(list);

  let folders = [];
  for (const item of list) {
    let json = await fs.readFile(process.env.userdata + item + '/fields.json')
    .catch((err) => {
      console.error(err);
    });
    if (json != undefined) {
      var fields = JSON.parse(json.toString('utf8'));
      folders.push({
        foldername: item,
        group: fields.group,
        title: fields.title,
        comment: fields.comment,
        anchor: fields.anchor
      });
    }
  }

  // console.log(folders);

  //
  reply.send(folders);
});

//get '/short'
fastify.get("/short", function (request, reply) {

  let shorts = [];
  for (var idx = 0; idx < 100; idx++) {
    shorts.push(short.generate());
  }

  reply.send(shorts);
});

//get '/uploads/:foldername/:filename'
fastify.get("/uploads/:foldername/:filename", async function (request, reply) {

  //get file & send
  var file = await fs.readFile(process.env.userdata + request.params.foldername + '/' + request.params.filename);

  reply.send(file);
});

//get '/live
fastify.get("/live", function (request, reply) {
  reply.view("/src/pages/live.html", {});
});

//get '/preview/:foldername' --> request.params.foldername
fastify.get("/preview/:foldername", function (request, reply) {
  reply.view("/src/pages/preview.html", {});
});

//get '/entries'
fastify.get("/entries", async function (request, reply) {

  //get list
  let list = await fs.readdir(process.env.userdata);

  reply.send(list);
});

//get '/fields'
fastify.get("/fields", async function (request, reply) {

  //get list
  let list = await fs.readdir(process.env.userdata);
  //list.reverse();

  // console.log(list);

  let folders = [];
  for (const item of list) {
    let json = await fs.readFile(process.env.userdata + item + '/fields.json')
    .catch((err) => {
      console.error(err);
    });
    if (json != undefined) {
      var fields = JSON.parse(json.toString('utf8'));
      folders.push({
        foldername: item,
        group: fields.group,
        title: fields.title,
        comment: fields.comment,
        anchor: fields.anchor
      });
    }
  }

  reply.send(folders);
});

//get '/delete'
fastify.get("/delete/:foldername/:pass", async function (request, reply) {

  //get pw
  var fields = JSON.parse((await fs.readFile(process.env.userdata + request.params.foldername + '/fields.json')).toString('utf8'));

  var res = false;
  if (fields.pass == request.params.pass) {
    // console.log('good pass');

    //ok. let's move it to trashbin. (mv ../sound-parade.trash)
    // await fs.rename(process.env.userdata + request.params.foldername, '/media/storage/public/sound-parade.trash/' + request.params.foldername);

    const client = new Client(server);
    const folder = await client.getFolder(process.env.userdata_nextcloud + request.params.foldername);
    await folder.move(process.env.userdata_nextcloud_trash + request.params.foldername);

    //
    res = true;
  } else {
    // console.log('wrong pass');
  }

  reply.send({ result: res });
});

//post on '/'
fastify.post("/", async function (request, reply) {

  console.log("-- h.."); // got the event.

  // stores files to tmp dir and return paths
  const files = await request.saveRequestFiles().catch(err => {
    console.error(err);
  });
  let audiofile = files.find(f => f.fieldname == 'audiofile');
  let pixelfile = files.find(f => f.fieldname == 'pixels');
  let tmpdir = path.dirname(audiofile.filepath);
  // console.log(audiofile.fields.message.value);

  console.log("-- hi."); // got all files.

  //conversion needed?
  var conversion = false;
  if (path.extname(audiofile.filename) !== ".mp3") {
    conversion = true;
    console.log("-- well.."); //conversion. is scheduled.
  } else {
    console.log("-- good"); //no conversion_
  }

  //upload
  const client = new Client(server);

  console.log("-- ready"); //file server opened

  //create unique folder ==> timestamp + uuid
  const folder = await client.createFolder(process.env.userdata_nextcloud + moment().tz('Asia/Seoul').format('YYYYMMDD-HHmmss-') + uuidv1());
  //
  const json = await folder.createFile("fields.json", Buffer.from(JSON.stringify({
    group: audiofile.fields.group.value,
    title: audiofile.fields.title.value,
    comment: audiofile.fields.comment.value,
    pass: audiofile.fields.pass.value,
    anchor: short.generate()
  })));
  const image = await folder.createFile("pixels.png", await fs.readFile(pixelfile.filepath));
  //
  if (conversion) {
    console.log('---- hi conv');
    //save original file as is. + we have scheduled a conversion.
    let afile = await fs.readFile(audiofile.filepath)
    .catch(err => {
      console.error(err);
    });
    //
    if (afile != undefined) {
      const file = await folder.createFile(audiofile.filename, afile)
      .catch(err => {
        console.error(err);
        console.log('---- createFile err.');
        console.log('afile', afile);
      });
    } else {
      console.log('afile == undef!');
    }
    console.log('---- yes conv');
    //
  } else {
    console.log('---- no conv');
    //rename & save original file.
    const file = await folder.createFile("audio.mp3", await fs.readFile(audiofile.filepath).catch(err => console.error(err)));
  }

  console.log("-- saved"); //saved in file server.

  //conversion needed?
  var converted = false;
  if (conversion) {
    console.log("-- mp3..."); //converting to mp3...
    function converter() {
      return new Promise((resolve, reject) => {
        //
        let outputFile = tmpdir + "/converted.mp3";
        // --> https://stackoverflow.com/a/36109219 (a quick tip on fluent-ffmpeg)
        ffmpeg()
          .addInput(audiofile.filepath)
          .on("error", function(err) {
            console.log("-- err:", err);
            reject(err);
          })
          .on("end", function() {
            console.log("-- fine."); //conversion succeesful
            converted = true;
            resolve(outputFile);
          })
          .outputOptions('-b:a 192000')
          .output(outputFile)
          .run();
      });
    }
    await converter().catch((err) => { console.error(err); });

    if (converted) {
      const file = await folder.createFile("audio.mp3", await fs.readFile(tmpdir + '/converted.mp3'));
      console.log("-- done");
    }

  }

  console.log("-- well done");

  reply.send('done!');
  //reply.redirect('/submit');
});

//socket.io
//var score = require("./public/score.json");

//
//there will be 16 rooms called: "room0", "room1", ... , "room15"
//if any other room is requested.. well, we will simply reject.
var roommax = 16;

//
io.on("connection", function(socket) {
  console.log("someone connected.");
  socket.on("disconnect", function() { console.log("someone disconnected."); });

  socket.on("room", function(room, fn) {
    // parseInt(room)
    if (room >= 0 && room < roommax) {
      socket.join("room" + room);
      fn(true);
    } else {
      fn(false);
    }
  });

  socket.on("flow", function(req) {
    io.emit("flow", req);
  });

  socket.on("info", async (fn) => {

    //get list
    let list = await fs.readdir(process.env.userdata);
    //list.reverse();

    // console.log(list);

    let folders = [];
    for (const item of list) {
      var fields = JSON.parse((await fs.readFile(process.env.userdata + item + '/fields.json')).toString('utf8'));
      folders.push({
        foldername: item,
        group: fields.group,
        title: fields.title,
        comment: fields.comment,
        anchor: fields.anchor
      });
    }

    fn(folders);
  });

});

//
var pointer = 0; // pointer : 0 ~ (length-1)
var looper;
(looper = function(timeout) {
  setTimeout(async function() {

    //pointer = 20;
    // console.log(score[pointer]);

    //
    for (var index = 0; index < roommax; index++) {

      // NOTE: 'pointer' must be 'remembered' since 'pointer' will increase almost immediately! pass as argument => 'pointed'
      // NOTE: 'index' is same => 'indexed'
      setTimeout(function(pointed, indexed) {

        // io.to("room" + indexed).emit("post", score[pointed]);
        io.to("room" + indexed).emit("post", pointed);

        // }, score[pointer].object.showtime * index, pointer, index);
      }, 30000 * index, pointer, index);
    }

    //var timegap = 10000 + Math.random()*(40000);
    var timegap = 1000 + Math.random()*(40000);
    console.log(timegap);

    //get # list
    let list = await fs.readdir(process.env.userdata);
    console.log(list.length);

    //loop over...
    pointer++;
    if (pointer >= list.length) pointer = 0;

    looper(timegap);
  }, timeout);
})(1000);

//listen
fastify.listen(process.env.port, function (err, address) {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  console.log(`Your app is listening on ${address}`)
  fastify.log.info(`server listening on ${address}`)
});
