<template>
  <div class="pdf-export-markup">
    <slot />
  </div>
</template>

<script>
  import JsPDF from 'jspdf';
  import html2canvas from 'html2canvas';

  /**
   * PDF Export
   *
   * @vuedoc
   * @exports TfPdfExport
   * @category components
   */
  export default {
    name: 'TfPdfExport',
    props: {
      filename: {
        type: String,
        default: 'export.pdf',
      },
    },
    data() {
      return { pages: [] };
    },
    mounted() {
      this.exportPdf();
    },
    methods: {
      /**
       * Get element height including margin and padding
       *
       * @param {object} element
       * @returns {number}
       */
      getHeight(element) {
        return [
          'height',
          'margin-top',
          'margin-bottom',
          'padding-top',
          'padding-bottom',
        ].reduce(
          (height, propertyName) => height + Number(
            document
              .defaultView
              .getComputedStyle(element, '')
              .getPropertyValue(propertyName)
              .replace(/px$/, ''),
          ),
          0,
        );
      },
      /**
       * Create new DIV element
       *
       * @param {string} className
       * @returns {object}
       */
      generateDiv(className) {
        const div = document.createElement('div');
        if (className) {
          div.className = className;
        }
        return div;
      },
      /**
       * Generate PDF via JsPDF
       *
       * @returns {object}
       */
      generatePdf() {
        const pdfDocument = document.implementation.createHTMLDocument('pdf-export');
        pdfDocument.className = 'pdf-export-document';

        // page 1
        let pdfPage = this.generateDiv('pdf-export-page');
        let pageHeight = 40; // design has 40px margin all around
        let blocksInDocument = 0;
        let lastBlockOnPage = 0;
        const blocks = [];

        this.$el.querySelectorAll('.pdf-export-block').forEach((block) => {
          const cloneBlock = block;
          // this is the only point at which the height value reflects reality, so we need to store it
          cloneBlock.setAttribute('block-height', this.getHeight(block));
          blocks.push(cloneBlock);
        });

        while (blocks.length > 0) {
          const block = blocks.splice(0, 1)[0];
          const blockHeight = Number(block.getAttribute('block-height'));
          if (pageHeight + blockHeight > 730) {
            // we need to add another page
            lastBlockOnPage = blocksInDocument;
            this.pages.push(pdfPage);
            pdfPage = this.generateDiv('pdf-export-page');
            pageHeight = 40;
          }

          block.style.position = 'absolute';
          block.style.left = '40px';
          block.style.top = `${pageHeight}px`;
          pdfPage.appendChild(block);
          pageHeight += blockHeight;
          blocksInDocument += 1;
        }

        if (blocksInDocument > lastBlockOnPage + 1) {
          // one or more elements were added to a pdfPage but the page wasn't added to the pages array yet
          this.pages.push(pdfPage);
        }

        // insert pagination div
        this.pages.forEach((page, index) => {
          const pagination = this.generateDiv('pagination');
          pagination.innerHTML = `Page ${index + 1} of ${this.pages.length}`;
          page.appendChild(pagination);
          pdfDocument.body.appendChild(page);
        });

        return pdfDocument.body;
      },
      /**
       * Export PDF via JsPDF.
       * jest/mocha (well, node) can't deal with this version of jsPDF at this time so we're cheating here and using require.
       * TODO:
       * - conditionally load node version here; note that that library does not expose a save() method because Node is expected to handle it's own file saving
       * - if we want to test the result, we'd have to account for that some other way
       */
      exportPdf() {
        const pdfFile = new JsPDF('p', 'pt', 'letter');

        // needed for the .html() method on jsPDF (but is somehow not a requirement for jsPDF)
        if (!window.html2canvas) {
          window.html2canvas = html2canvas;
        }

        pdfFile.html(
          this.generatePdf(),
          {
            callback: () => {
              // occasionally when window page height is larger or equal to 730px blank page(s) will be added
              // to end of pdf document. these need to be checked after generation and be will deleted before save
              let pdfPages = pdfFile.internal.getNumberOfPages();

              if (pdfPages > this.pages.length) {
                for (pdfPages; pdfPages > this.pages.length; pdfPages -= 1) {
                  pdfFile.deletePage(pdfPages);
                }
              }
              pdfFile.save(this.filename);
              this.$emit('save');
            },
            html2canvas: {
              scale: 1,
              logging: false, // this was spitting out console logs
            },
          },
        );
      },
    },
  };
</script>

<style lang="scss">
  /**
  * This file is intentially unscoped since there are divs being created in the methods.
  */
  @import 'src/assets/styles/variables';

  .pdf-export-document {
    width: 610px;
  }

  .pdf-export-markup {
    position: fixed;
    top: -1000em;
    left: -1000em;
    width: 630px;
  }

  .pdf-export-page {
    position: relative;
    width: 610px;
    height: 792px;
    padding: 0;
    margin: 0;
    font-family: Arial, sans-serif;
  }

  .pagination {
    position: absolute;
    bottom: 30px;
    right: 40px;
    font-size: 10px;
    color: $tf-dark-gray;
  }
</style>
