<template>
  <snt-loading
    :loading="loading"
    class="printer-preview">
    <div class="d-flex flex-column">
      <slot
        :submit="submit"
        :download="download"
        :currentPage="currentPage"
        :pagesCount="pagesCount"
        :goToPrevPage="goToPrevPage"
        :goToNextPage="goToNextPage"
        name="header">
        <div class="d-flex justify-center align-center gap-6 mb-3">
          <v-btn
            :ripple="false"
            :disabled="currentPage <= 1"
            color="primary"
            icon
            @click="goToPrevPage()">
            <v-icon>mdi-chevron-left</v-icon>
          </v-btn>
          <div>{{ currentPage }} of {{ pagesCount }}</div>
          <v-btn
            :ripple="false"
            :disabled="currentPage >= pagesCount"
            color="primary"
            icon
            @click="goToNextPage()">
            <v-icon>mdi-chevron-right</v-icon>
          </v-btn>
        </div>
      </slot>
      <div
        ref="printerPreview"
        class="printer-preview__container modern-scrollbar overflow-auto pa-0 pa-md-8">
        <div class="mx-auto">
          <SntPaper
            :items="pages"
            :height="height"
            :width="width"
            :inner-height="innerHeight"
            :inner-width="innerWidth" />
        </div>
      </div>
      <slot
        :submit="submit"
        :download="download"
        :currentPage="currentPage"
        :pagesCount="pagesCount"
        :goToPrevPage="goToPrevPage"
        :goToNextPage="goToNextPage"
        name="footer">
        <div class="d-flex gap-3 pa-4">
          <v-spacer />
          <v-btn
            class="pr-4"
            color="primary"
            depressed
            rounded
            @click="download()">
            <v-icon left>
              mdi-download
            </v-icon>
            Download
          </v-btn>
          <v-btn
            color="primary"
            depressed
            rounded
            @click="submit()">
            พิมพ์
          </v-btn>
        </div>
      </slot>
    </div>
  </snt-loading>
</template>

<script>
import Vue from 'vue'
import printJS from 'print-js'
import {
  VBtn,
  VIcon,
  VSpacer
} from 'vuetify/lib'
import VNodeElement from './components/VNodeElement.vue'
import SntPaper from './SntPaper.vue'
import { SntLoading } from '../SntLoading'
import { savePdf } from '../../utils/html-convert'

const convertToPixel = (number) => `${number}px`
export default {
  components: {
    VBtn,
    VIcon,
    VSpacer,
    SntPaper,
    SntLoading
  },
  props: {
    items: {
      type: Array,
      default: () => ([])
    },
    headers: {
      type: Array,
      default: () => ([])
    },
    footers: {
      type: Array,
      default: () => ([])
    },
    callback: {
      type: Function,
      default: () => {}
    },
    width: {
      type: Number,
      default: 0
    },
    height: {
      type: Number,
      default: 0
    },
    innerWidth: {
      type: Number,
      default: 0
    },
    innerHeight: {
      type: Number,
      default: 0
    }
  },
  data  () {
    return {
      currentPage: 1,
      loading: false,
      callbackItems: []
    }
  },
  computed: {
    isSinglePage () {
      return !this.height
    },
    pages () {
      const innerPages = this.callbackItems.map((item) => this.getPages(this.items, item, true)).flat()
      if (this.headers.length) {
        innerPages.unshift({
          data: {},
          el: this.headers
        })
      }
      if (this.footers.length) {
        innerPages.push({
          data: {},
          el: this.footers
        })
      }
      if (this.isSinglePage) return [innerPages]
      const cols = this.$_.floor(this.width / this.innerWidth) || 1
      const rows = this.$_.floor(this.height / this.innerHeight) || 1
      const perPage = cols * rows
      return this.$_.chunk(innerPages, perPage)
    },
    pagesCount () {
      return this.pages.length
    }
  },
  mounted () {
    this.getCallback()
  },
  methods: {
    async getCallback (perPage = 100) {
      if (!this.callback) return new Promise()
      this.loading = true
      const { pagesCount } = await this.callback({
        page: 1,
        perPage: 1
      })
      this.callbackItems = []
      this.$queue.beginTransition()
      this.$_.times(pagesCount, (i) => {
        const page = i + 1
        this.$queue.add(() => this.callback({
          page,
          perPage
        }).then(({ nodes }) => {
          this.callbackItems = this.$_.concat(this.callbackItems, nodes)
          this.$queue.next()
        }))
      })
      const result = new Promise((resolve) => {
        this.$queue.add(() => {
          this.loading = false
          resolve(this.callbackItems)
        })
      })
      this.$queue.endTransition()
      return result
    },
    goToPrevPage () {
      return this.goToPage(this.currentPage - 1)
    },
    goToNextPage () {
      return this.goToPage(this.currentPage + 1)
    },
    goToPage (page = 1) {
      if (page > 1) {
        this.currentPage = page > this.pagesCount ? this.pagesCount : page
      } else {
        this.currentPage = 1
      }
      const element = document.getElementById(`printer-${this.currentPage}`)
      const top = element.offsetTop
      this.$refs.printerPreview.scrollTo({ top: top - 250, behavior: 'smooth' })
    },
    setPage (page = 1) {
      if (page > 1) {
        this.currentPage = page > this.pagesCount ? this.pagesCount : page
      } else {
        this.currentPage = 1
      }
    },
    getPages (items, data, repos = false, position = 0) {
      const canvas = this.$_.cloneDeep(items)
      const table = canvas.find(({ type }) => type === 'products')
      const currCanvas = []
      const sorted = this.$_.sortBy(canvas, [(el) => parseInt(el.properties.style.top, 10)])
      const heading = this.$_.head(sorted)
      if (table) {
        const innerHeight = this.innerHeight
        const tableTop = parseInt(table.properties.style.top, 10)
        const tableHeight = parseInt(table.properties.style.height, 10)
        const tableBottom = tableTop + tableHeight
        const minHeightRow = 30
        const maxRowsCount = Math.ceil(innerHeight / minHeightRow)
        const nextCanvas = []
        let endTable = true
        sorted.forEach((el) => {
          const top = parseInt(el.properties.style.top, 10)
          const height = parseInt(el.properties.style.height, 10)
          const bottom = top + height
          const isUpper = top < tableBottom
          const isLower = (bottom < innerHeight) && endTable
          if (el.type === 'products') {
            el.properties.style.height = '0px'
            if (!el.children[0].children[1].children[0].cursor) {
              el.children[0].children[1].children[0].cursor = 0
            }
            const { newPerPage, paging } = this.getHeightTable(el, data, maxRowsCount)
            el.properties.style.height = ''
            el.children[0].children[1].children[0].perPage = newPerPage
            if (paging) {
              const cloneEl = this.$_.cloneDeep(el)
              cloneEl.properties.style.height = tableHeight
              cloneEl.children[0].children[1].children[0].cursor += newPerPage
              cloneEl.properties.style.top = heading.properties.style.top
              nextCanvas.push(cloneEl)
              endTable = false
            }
            currCanvas.push(el)
          } else if (isUpper || isLower) {
            currCanvas.push(el)
          } else {
            nextCanvas.push(el)
          }
        })
        if (nextCanvas.length && !this.isSinglePage) {
          const newPosition = repos ? tableBottom : position
          return [{
            el: currCanvas,
            data
          }, ...this.getPages(nextCanvas, data, false, newPosition)]
        }
      } else {
        sorted.forEach((el) => {
          const top = parseInt(el.properties.style.top, 10)
          el.properties.style.top = convertToPixel(top - position)
          currCanvas.push(el)
        })
      }
      return [{
        el: currCanvas,
        data
      }]
    },
    getHeightTable (el, data, perPage = 100) {
      const cloneEl = this.$_.cloneDeep(el)
      const tableTop = parseInt(cloneEl.properties.style.top, 10)
      cloneEl.children[0].children[1].children[0].perPage = perPage
      const vnode = new (Vue.extend(VNodeElement))({
        el: document.createElement('div'),
        propsData: {
          element: cloneEl,
          item: data
        }
      })
      const container = document.body
      container.appendChild(vnode.$el)
      const child = vnode.$el.querySelector('table')
      const tableNewHeight = child.clientHeight
      container.removeChild(vnode.$el)
      const tableNewBottom = tableTop + tableNewHeight
      if (tableNewBottom > this.innerHeight) {
        const newPerPage = perPage - 1
        const trying = this.getHeightTable(cloneEl, data, newPerPage)
        return {
          ...trying,
          paging: true
        }
      }
      return {
        newPerPage: perPage,
        tableNewHeight,
        paging: false
      }
    },
    download () {
      const items = []
      this.$_.times(this.pagesCount, (i) => {
        items.push(`printer-${i + 1}`)
      })
      savePdf(items, { width: this.width, height: this.height })
    },
    submit () {
      const items = this.pages
      const width = this.width
      const height = this.height
      const innerHeight = this.innerHeight
      const innerWidth = this.innerWidth
      const el = document.createElement('div')
      const vm = new (Vue.extend(SntPaper))({
        el,
        propsData: {
          items,
          height,
          width,
          innerHeight,
          innerWidth
        }
      })
      vm.$mount(el)
      vm.$nextTick(() => {
        const html = vm.$el.innerHTML
        const div = document.createElement('div')
        div.innerHTML = html.trim()
        const mm = 0.2645833333
        const widthPaper = this.width * mm
        const heightPaper = this.height * mm
        printJS({
          printable: div.innerHTML,
          type: 'raw-html',
          honorColor: true,
          css: [
            'https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css'
          ],
          style: `
            html {
              font-family: 'prompt', sans-serif;
              -webkit-print-color-adjust: exact;
            }
            @page {
              size: ${widthPaper}mm ${heightPaper}mm;
              margin: 0;
            }
            * {
              color: #333;
            }
          `,
          targetStyles: ['*']
        })
      })
    }
  }
}
</script>

<style lang="scss" scoped>
  .printer-preview {
    &__container {
      display: flex;
      min-height: 300px;
      height: calc(100vh - 320px);
      background: #B5B5B5;
    }
  }
</style>
