// you may not need all of these but they come up a lot
import fs from "fs";
import assert from "assert";
import logging from '../lib/logging.js';
import glob from "fast-glob";
import path from "path";
import template from "lodash/template.js";
import { defer } from "../lib/api.js";
import PDFDocument from "pdfkit";

const log = logging.create(import.meta.url);

const title_box = { x: 17, y: 415, width: 405, align: "center" };
const summary_box = { width: 405, height: 540 };
const main_title = { align: "center", width: 1196, height: 883, x: 586, y: 99};
const main_box = { width: 928, height: 535, x: 720, y: 323};


export const description = "Generates a PDF presentation from an input .md file and a background template."

// your command uses the npm package commander's options format
export const options = [
  ["--heading-font <path>", "Font file to use for headings.", "static/fonts/VictorMono-Bold.ttf"],
  ["--body-font <path>", "Font file to use for body text.", "static/fonts/VictorMono-Medium.ttf"],
  ["--output <path>", "The output .pdf file to write. Defaults to <input>.pdf"],
]

// put required options in the required variable
export const required = [
  ["--input <path>", "The input .md file for the generated presentation."],
  ["--template <path>", "The background template image to use."],
]

// handy function for checking things are good and aborting
const check = (test, fail_message) => {
  if(!test) {
    log.error(fail_message);
    process.exit(1);
  }
}

export const start_pdf = (opts) => {
  const doc = new PDFDocument({
    font: "Courier",
    size: [1920, 1080],
    info: {
      "Title": "Test",
      "Author": "Zed A. Shaw",
      "Subject": "Test subject",
      "Keywords": "test test test",
      "CreationDate": new Date(),
      "ModDate": new Date(),
    }});

  const out_stream = doc.pipe(fs.createWriteStream(opts.output));

  doc.registerFont("heading", opts.headingFont);
  doc.registerFont("body", opts.bodyFont);

  return [doc, out_stream];
}

const write_page = (doc, template, content) => {
  // place the background image
  doc.image(template, 0, 0, {
    width: 1920,
    height: 1080
  });

  // create the left side constant summary
  doc.fontSize(40);
  doc.font("heading").text(content.title, title_box.x, title_box.y, title_box);
  doc.fontSize(30);
  doc.moveDown();
  // the lesson summary
  doc.font("body").text(content.summary, summary_box);
  doc.fillColor("white");


  if(content.type == "title-bullets") {
    doc.fontSize(120);
    // add the title/body text
    doc.font("heading").text(content.slide_title, main_title.x, main_title.y, main_title);

    doc.fontSize(80);

    doc.list(content.slide_body, main_box.x, main_box.y, main_box);

  } else if(content.type == "title-image") {
    // it's a title only slide
    doc.fontSize(120);
    // add the title/body text
    doc.font("heading").text(content.slide_title, main_title.x, main_title.y, main_title);

    doc.image(content.slide_body, main_box.x, main_box.y, {
      fit: [main_box.width, main_box.height],
      align: "center",
      valign: "center"
    });
  } else if(content.type == "title-only") {
    // it's a title only slide
    doc.fontSize(160);
    doc.font("heading").text(content.slide_title, main_title.x, main_title.y + (main_title.height / 3.5), main_title);
  } else {
    // this handles title-text slides, and anything else
    doc.fontSize(120);
    // add the title/body text
    doc.font("heading").text(content.slide_title, main_title.x, main_title.y, main_title);

    // if I put a # I want this to be a subtitle
    if(content.slide_body.startsWith("#")) {
      doc.fontSize(100);
      const trim_body = content.slide_body.slice(1).trim();
      doc.font("body").text(trim_body, main_box.x, main_box.y, {align: "center", ...main_box});
    } else {
      doc.fontSize(60);
      doc.font("body").text(content.slide_body, main_box.x, main_box.y, main_box);
    }

  }
}

const next_page = (doc) => {
  doc.addPage();
}

const end_pdf = (doc) => {
  doc.end();
}

const parse_input = (input) => {
  const data = fs.readFileSync(input).toString();
  const result = [];

  const [head_data, slides_data] = data.split("===");
  const head = JSON.parse(head_data);

  for(let slide of slides_data.split("---")) {
    const split = slide.trim().split("\n");
    const title = split.shift();
    const body = split.join("\n").trim();

    if(title || body) {
      const page = {
        slide_title: title,
        slide_body: body,
        ...head
      }

      if(!body) {
        page.type = "title-only";
      } else if(body.startsWith("*")) {
        page.type = "title-bullets";
        page.slide_body = body.split("\n").map(l => l.slice(1).trim());
      } else if(body.startsWith("[")) {
        page.type = "title-image";
        page.slide_body = body.slice(1,-1);
      } else {
        page.type = "title-text";
      }

      result.push(page);
    }
  }

  return result;
}

const make_pdf_path = (input) => {
  const result = path.parse(input);

  return path.join(result.dir, `${result.name}.pdf`);
}

const generate_presentation = (opts, waiting) => {
  try {
    if(!opts.output) {
      opts.output = make_pdf_path(opts.input);
    }

    const [doc, out_stream] = start_pdf(opts);

    out_stream.on("finish", () => waiting.resolve());

    const pages = parse_input(opts.input);

    for(let i = 0; i < pages.length; i++) {
      const page = pages[i];

      write_page(doc, opts.template, page);

      // don't add a page when at the end
      if(i !== pages.length - 1) {
        next_page(doc);
      }
    }

    end_pdf(doc);
  } catch(error) {
    console.error(error);
    waiting.resolve();
  }
}

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

  for(let file of in_files) {
    console.log(file);
    const waiting = defer(file);
    const settings = {...opts};
    settings.input = file;
    generate_presentation(settings, waiting);
    await waiting;
  }

  process.exit(0);
}
