<template>
  <ul
    v-show="visible"
    ref="contextmenu"
    :class="classes"
    :style="styles"
  >
    <!-- TODO: group acitions by prop ex groupName into submenus -->
    <c-context-menu-sub-menu title="Akcje" v-if="isAnyDataSetActionToDisplay">
      <c-context-menu-item 
        v-for="action in dataSetActionsToDisplay" 
        :key="action.actionIdent"
      >
        <c-btna
          :appWindowIdent="appWindowIdent"
          :dataSetIdent="dataSetIdent"
          :actionIdent="action.actionIdent"
        />
      </c-context-menu-item>
    </c-context-menu-sub-menu>
    
    <c-context-menu-item divider v-if="isAnyDataSetActionToDisplay && $scopedSlots.default" />

    <slot />
  </ul>
</template>

<script>
import { mapGetters } from "vuex"
import cContextMenuItem from "./c-context-menu-item"
import cContextMenuSubMenu from "./c-context-menu-sub-menu"

export default {
  provide () {
    return {
      $$contextmenu: this,
    }
  },
  components: {
    "c-context-menu-item": cContextMenuItem,
    "c-context-menu-sub-menu": cContextMenuSubMenu,
  },
  props: {
    eventType: {
      type: String,
      default: 'contextmenu',
      validator: (value) => ['contextmenu', 'click'].includes(value)
    },
    autoPlacement: {
      type: Boolean,
      default: true,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    appWindowIdent: {
      type: String,
      default: null
    },
    dataSetIdent: {
      type: String,
      default: null
    }
  },
  data() {
    return {
      visible: false,
      references: [],
      styles: {
        top: 0,
        left: 0
      }
    }
  },
  computed: {
    ...mapGetters("csAppWindowsUtils", [
      "getDataSet",
      "getDataSetActions"
    ]),
    dataSetActionsToDisplay() {
      const dataSet = this.getDataSet(this.appWindowIdent, this.dataSetIdent)
      const dataSetActions = this.getDataSetActions(dataSet)
      return dataSetActions.filter(action => action.isAuto == 1 && action.addCurrentRow == 1)
    },
    isAnyDataSetActionToDisplay() {
      return this.dataSetActionsToDisplay.length > 0
    },
    clickOutsideHandler() {
      return this.visible ? this.hide : () => {}
    },
    isClick() {
      return this.eventType === 'click'
    },
    classes() {
      return ['c-context-menu']
    }
  },
  methods: {
    hide() {
      this.visible = false
    },
    hideAll() {
      Object.keys(window.$$cContextMenu).forEach(key => window.$$cContextMenu[key].hide())
    },
    show(position) {
      Object.keys(window.$$cContextMenu).forEach(key => {
        if (key === this.$contextMenuId) {
          window.$$cContextMenu[key].hide()
        }
      })

      if (position) {
        this.styles = {
          top: `${position.top}px`,
          left: `${position.left}px`
        }
      }

      if (this.isAnyDataSetActionToDisplay || this.$scopedSlots.default)
        this.visible = true
    },
    handleBodyClick(event) {
      const notOutside = this.$el.contains(event.target) || (
        this.isClick && this.references.some(ref => ref.el.contains(event.target))
      )

      if (!notOutside)
        this.visible = false
    },
    handleReferenceContextMenu(event) {
      event.preventDefault()

      if (this.disabled) return

      const reference = this.references.find(ref => ref.el.contains(event.target))

      this.$emit('contextmenu', reference ? reference.vnode : null)

      const eventX = event.pageX
      const eventY = event.pageY

      this.show()

      this.$nextTick(() => {
        const contextMenuPosition = {
          top: eventY,
          left: eventX
        }

        if (this.autoPlacement) {
          const contextMenuWidth = this.$refs.contextmenu.clientWidth
          const contextMenuHeight = this.$refs.contextmenu.clientHeight

          if (contextMenuHeight + eventY >= window.innerHeight)
            contextMenuPosition.top -= contextMenuHeight

          if (contextMenuWidth + eventX >= window.innerWidth)
            contextMenuPosition.left -= contextMenuWidth
        }

        this.styles = {
          top: `${contextMenuPosition.top}px`,
          left: `${contextMenuPosition.left}px`
        }
      })
    },
    addRef(ref) {
      this.references.push(ref)

      ref.el.addEventListener(this.eventType, this.handleReferenceContextMenu)
    },
    removeRef(ref) {
      ref.el.removeEventListener(this.eventType, this.handleReferenceContextMenu)

      this.references.filter(r => r !== ref)
    },
    removeAlLRefs() {
      this.references.forEach(ref => {
        this.removeRef(ref)
      })
    }
  },
  mounted() {
    document.body.appendChild(this.$el)

    if (window.$$cContextMenu)
      window.$$cContextMenu[this.$contextMenuId] = this
    else
      window.$$cContextMenu = { [this.$contextMenuId]: this }
  },
  beforeDestroy() {
    document.body.removeChild(this.$el)

    delete window.$$cContextMenu[this.$contextMenuId]

    this.removeAlLRefs()

    document.body.removeEventListener('click', this.handleBodyClick)
  },
  watch: {
    visible(value) {
      if (value) {
        this.$emit('show', this)

        document.body.addEventListener('click', this.handleBodyClick)
      } else {
        this.$emit('hide', this)

        document.body.removeEventListener('click', this.handleBodyClick)
      }
    }
  }
}
</script>
