import { insertScript } from '../../core'

const PDFJS_BUNDLE = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.0.279/build/pdf.min.js'
const WORKER_BUNDLE = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.0.279/build/pdf.worker.min.js'

class PDFViewer {
  constructor (el) {
    const {
      url,
      hidePaginator,
      hideLoader,
      scale,
      pageNum
    } = el.dataset

    if (url == null) {
      throw new Error('Cannot load PDF! Attribute `data-url` is not set.')
    }

    // props
    this.url = url
    this.hidePaginator = hidePaginator !== 'false'
    this.hideLoader = hideLoader !== 'false'
    this.scale = scale || 3

    // initial state
    this.pageNum = parseInt(pageNum, 10) || 1
    this.loaded = false
    this.pageRendering = false
    this.pageNumPending = null

    // DOM elements
    this.canvas = el.getElementsByClassName('pdf-canvas')[0]
    if (this.canvas == null) {
      throw new Error('canvas element not found!')
    };
    this.paginator = el.getElementsByClassName('paginator')[0]
    this.loadingWrapper = el.getElementsByClassName('loading-wrapper')[0]
    this.next = el.getElementsByClassName('next')[0]
    this.prev = el.getElementsByClassName('prev')[0]
    this.pageNum = el.getElementsByClassName('page-num')[0]
    this.pageCount = el.getElementsByClassName('page-count')[0]

    // context
    this.ctx = this.canvas.getContext('2d')

    // events
    this.next.addEventListener('click', this.handleNextPage.bind(this))
    this.prev.addEventListener('click', this.handlePrevPage.bind(this))

    this.showPaginator()
    this.showLoader()
    this.loadPDF()
  }

  /**
   * If we haven't disabled the loader, show loader and hide canvas
   */
  showLoader () {
    if (this.hideLoader) return
    this.loadingWrapper.style.display = 'flex'
    this.canvas.style.display = 'none'
  }

  /**
   * If we haven't disabled the paginator, show paginator
   */
  showPaginator () {
    if (this.hidePaginator) return
    this.paginator.style.display = 'block'
  }

  /**
   * Hides loader and shows canvas
   */
  showContent () {
    this.loadingWrapper.style.display = 'none'
    this.canvas.style.display = 'block'
  }

  /**
   * Asynchronously downloads PDF.
   */
  async loadPDF () {
    this.pdfDoc = await window.pdfjsLib.getDocument(this.url).promise

    this.pageCount.textContent = this.pdfDoc.numPages

    // If the user passed in a number that is out of range, render the last page.
    if (this.pageNum > this.pdfDoc.numPages) {
      this.pageNum = this.pdfDoc.numPages
    }

    this.renderPage(this.pageNum)
  }

  /**
   * Get page info from document, resize canvas accordingly, and render page.
   * @param num Page number.
   */
  async renderPage (num) {
    this.pageRendering = true

    const page = await this.pdfDoc.getPage(num)
    const viewport = page.getViewport({ scale: this.scale })
    this.canvas.height = viewport.height
    this.canvas.width = viewport.width

    // Wait for rendering to finish
    await page.render({
      canvasContext: this.ctx,
      viewport
    }).promise

    this.pageRendering = false
    this.showContent()

    if (this.pageNumPending !== null) {
      // New page rendering is pending
      this.renderPage(this.pageNumPending)
      this.pageNumPending = null
    }
    // Update page counters
    this.pageNum.textContent = num
  }

  /**
   * If another page rendering in progress, waits until the rendering is
   * finished. Otherwise, executes rendering immediately.
   */
  queueRenderPage (num) {
    if (this.pageRendering) {
      this.pageNumPending = num
    } else {
      this.renderPage(num)
    }
  }

  /**
   * Displays previous page.
   */
  handlePrevPage () {
    if (this.pageNum <= 1) {
      return
    }
    this.pageNum--
    this.queueRenderPage(this.pageNum)
  }

  /**
   * Displays next page.
   */
  handleNextPage () {
    if (this.pageNum >= this.pdfDoc.numPages) {
      return
    }
    this.pageNum++
    this.queueRenderPage(this.pageNum)
  }
}

insertScript('pdfjs', PDFJS_BUNDLE, () => {
  window.pdfjsLib.GlobalWorkerOptions.workerSrc = WORKER_BUNDLE
  Array.from(document.getElementsByClassName('pdf-viewer')).forEach(el => new PDFViewer(el))
})
