import path from "path";
import ffmpeg from "fluent-ffmpeg";
import { glob, mkdir } from "../lib/builderator.js";
import { defer } from "../lib/api.js";
import { existsSync as exists } from "fs";
import assert from "assert";

import logging from "../lib/logging.js";
const log = logging.create("commands/convert.js");

const dev_null = process.platform == "win32" ? "NUL" : "/dev/null";

export const description = "Converts videos from GrassValley HQX  to .mp4.";

export const options = [
  ["--scale <int>", "720, 1080, or 2160 for example", 1080],
  ["--test <int>", "Make a test video <int> seconds long."],
  ["--test-start <int>", "When to start the test clip."],
  ["--init-outdir", "if outdir doesn't exist create it", false],
  ["--br-video <int>", "video bitrate", 900],
  ["--br-audio <int>", "video bitrate", 192],
  ["--speed <str>", "ffmpeg speed preset", "veryslow"],
  ["--crf <int>", "constant rate factor", 30],
  ["--tune <str>", "special tuning setting for mp4 try film", "animation"],
  ["--debug <level>", "1=print the ffmpeg command, 2=and its stderr output"],
  ["--clean-filename", "don't modify the output file with scale info", false],
  ["--progress", "Show percent progress.  Not accurate (thanks ffmpeg)", false],
  ["--output <string>", "specific output file name"],
  ["--outdir <string>", "write the file to this dir (can't combine with output)"],
  ["--vp9", "encode with vp9"],
];

export const required = [
  ["--input [string]", "input file name or glob/regex"],
];

const generate_output_file = (opts) => {
  if(opts.output) {
    return opts.output;
  } else {
    const { dir, name } = path.parse(opts.input);
    const ext = opts.vp9 ? "webm" : "mp4";
    const target_dir = opts.outdir ? opts.outdir : dir;

    if(opts.initOutdir) mkdir(target_dir);

    assert(exists(target_dir), `Target directory ${target_dir} does not exist.`);

    if(opts.cleanFilename) {
      return path.join(target_dir, `${name}.${ext}`);
    } else {
      return path.join(target_dir, `${name}.${opts.scale}.${ext}`);
    }
  }
}

const run = async (pass, output, opts) => {
  const encode_defer = defer();

  // taken from https://developers.google.com/media/vp9/settings/vod

  // 22m without these, 13m with these
  const vp9_opts = [
    ["-pass", pass],
    ["-passlogfile", `ffmpeg2pass-${ process.pid }`],
    ["-minrate", `${opts.brVideo / 2 }k`],
    ["-maxrate", `${opts.brVideo * 1.45 }k`],
    ["-quality", "good"],
    ["-speed", pass == 1 ? 4 : 2],
    ["-threads", "8"],
    // this is different for 1080 videos vs 720
    ["-tile-columns", pass + 1],
    // key frames at 240
    ["-g", "240"],
  ];

  const mp4_opts = [
    ["-vf", `scale=-1:${opts.scale}:flags=lanczos`],
    ["-pix_fmt","yuv420p"],
    ["-tune", opts.tune],
    ["-movflags", "faststart"],
    ["-pass", pass],
    ["-passlogfile", `ffmpeg2pass-${ process.pid }.log`],
    ["-preset", opts.speed],
  ];

  if(opts.crf) {
    vp9_opts.push(["-crf", opts.crf]);
    mp4_opts.push(["-crf", opts.crf]);
  }

  let encode = ffmpeg(opts.input, {logger: log})
    .inputOptions([]);

  if(opts.testStart || opts.test) {
    encode.seek(opts.testStart || opts.test);
  }

  // this logic is too convoluted
  if((opts.vp9 && pass < 2) || (!opts.vp9 && pass < 3)) {
    encode.noAudio();
  } else {
    encode.audioBitrate(opts.brAudio);
  }

  // we have to use flat here because weirdly ffmpeg-fluent expects them in a big list
  encode.videoBitrate(opts.brVideo)
    .outputOptions(opts.vp9 ? vp9_opts.flat() : mp4_opts.flat())
    .output(output)

  if(opts.vp9) {
    encode.audioCodec("libopus")
      .videoCodec("libvpx-vp9")
      .format("webm");
  } else {
    encode.audioCodec("aac")
      .videoCodec("libx264")
      .format("mp4");
  }

  if(opts.test) {
    encode.duration(opts.test);
  }

  if(opts.progress) {
    encode.on("progress", (progress) => {
      process.stdout.write(`${path.basename(opts.input)} -> ${ output } ${Math.round(progress.percent)}%  \r`)
    });
  }

  if(opts.debug) {
    encode.on("start", line => console.log("FFMPEG: ", line));

    if(opts.debug == 2) {
      encode.on("stderr", line => console.log(line));
    }
  }

  encode.on("end", () => encode_defer.resolve());

  console.log("------ PASS", pass, opts.input);
  encode.run();

  return encode_defer;
}

export const process_file = async (opts) => {
  const output = generate_output_file(opts);

  if(opts.vp9) {
    await run(1, dev_null, opts);
    await run(2, output, opts);
  } else {
    await run(1, dev_null, opts);
    await run(2, dev_null, opts);
    await run(3, output, opts);
  }
}

export const main = async (opts) => {
  const inputs = glob(opts.input);

  for(let fname of inputs) {
    opts.input = fname;
    await process_file(opts);
  }

  process.exit(0);
}
