<template>
  <div class="paginated-media">
    <div class="media" ref="media">
      <div v-for="image in images"
           v-bind:key="image.identifier"
           class="image"
           @click="$emit('click', image)">
        <img :alt="image.name" :src="image.src">

        <div v-if="current && current.identifier === image.identifier"
             class="active"/>

        <div v-if="deletable"
             class="delete"
             @click="deleteElement(image)">
          <img alt="remove" src="@/assets/icons/x-circle.svg">
        </div>
      </div>
    </div>

    <div v-if="!inline" class="pagination">
      <div class="button" :class="{clickable: page > 1}" @click="prevPage">
        &lt;
      </div>
      <div class="text">
        {{ page }} / {{ pageCount }}
      </div>
      <div class="button" :class="{clickable: page < pageCount}" @click="nextPage">
        &gt;
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "paginated-media",
  props: ['rows', 'width', 'inline', 'deletable', 'current', 'filter'],
  mounted: function () {
    window.addEventListener('resize', this.resize);
    window.addEventListener('wheel', this.scroll);

    this.$root.socket.on('create-file', this.change);
    this.$root.socket.on('edit-file', this.edit);
    this.$root.socket.on('remove-file', this.change);

    this.get(() => this.$emit('click', this.images[0]));
  },
  destroyed: function () {
    window.removeEventListener('resize', this.resize);
    window.removeEventListener('wheel', this.scroll);

    this.$root.socket.off('create-file', this.change);
    this.$root.socket.off('edit-file', this.edit);
    this.$root.socket.off('remove-file', this.change);
  },
  data: function () {
    return {
      resizeEvent: false,
      page: 1,
      pageCount: 1,
      images: [],
      elements: {
        previous: null,
        next: null
      }
    }
  },
  methods: {
    resize: function () {
      // clear existing timeout
      if (this.resizeEvent)
        window.clearTimeout(this.resizeEvent);

      // clear images so the following calculation succeeds
      this.images = [];
      this.$refs.media.style.gridTemplateRows = '';
      this.$refs.media.style.gridTemplateColumns = '';

      // add a timeout to start calculation after resizing finished
      this.resizeEvent = window.setTimeout(() => {
        this.get();
        this.resizeEvent = false;
      }, 500);
    },
    change: function (file) {
      if (file.project_id === this.$root.project.identifier)
        this.get();
    },
    edit: function (file) {
      // edited file is now in the current collection
      // filter is stored as str, so '==' is intentional
      if (file.collection_id == this.filter) {
        this.get();
        return;
      }

      // edited file is in the current image list
      if (this.filter !== false) {
        for (let image of this.images) {
          if (image.identifier === file.identifier) {
            this.get(() => {
              // click the first image if the current shown was removed
              if (this.current.identifier === file.identifier) {
                this.$emit('click', this.images[0]);
              }
            });
            return;
          }
        }
      }
    },
    deleteElement: function (element) {
      this.$root.socket.post(`/data/${element.identifier}/remove`, {remove: true});
    },
    prevPage: function (callback) {
      if (this.page > 1)
        this.page -= 1;

      this.get(callback);
    },
    nextPage: function (callback) {
      if (this.page < this.pageCount)
        this.page += 1;

      this.get(callback);
    },
    prevElement: function () {
      if (this.elements.previous)
        this.$emit('click', this.elements.previous);
    },
    nextElement: function () {
      if (this.elements.next)
        this.$emit('click', this.elements.next);
    },
    get: function (callback) {
      // get container size
      const width = this.$refs.media.offsetWidth;
      const height = this.$refs.media.offsetHeight;

      // calculate grid size
      const cols = Math.round(width / (this.width ? this.width : 180));
      const rows = this.rows ? this.rows : Math.round(height / 120);

      const elementWidth = width / cols;
      const elementHeight = height === 0 ? elementWidth / 3 * 2 : height / rows;

      this.$refs.media.style.gridTemplateColumns = `repeat(${cols}, ${elementWidth}px)`;
      this.$refs.media.style.gridTemplateRows = `repeat(${rows}, ${elementHeight}px)`;

      // receive elements
      if (this.page === 0)
        this.page = 1;

      const limit = rows * cols;
      const offset = (this.page - 1) * limit;

      const requestWidth = Math.ceil(elementWidth / 200) * 200;
      const requestHeight = Math.ceil(elementHeight / 200) * 200;

      let url;
      if (this.filter === undefined || this.filter === false)
        url = `/projects/${this.$root.project.identifier}/data/${offset}/${limit}`;
      else if (this.filter === null)
        url = `/projects/${this.$root.project.identifier}/data/0/${offset}/${limit}`;
      else
        url = `/projects/${this.$root.project.identifier}/data/${this.filter}/${offset}/${limit}`;

      // call endpoint
      this.$root.socket.get(url)
          .then(response => response.json())
          .then(elements => {
            this.images = elements.files;
            this.pageCount = Math.ceil(elements.count / limit);

            for (let image of this.images) {
              image.src = this.$root.socket.media(image, requestWidth, requestHeight);
            }

            if (this.pageCount > 0 && this.page > this.pageCount) {
              this.page = this.pageCount;
              return this.get(callback);
            }

            // emit events
            this.$emit('hasPreviousPage', this.page > 1);
            this.$emit('hasNextPage', this.page < this.pageCount);

            if (callback && typeof callback === 'function') {
              callback();
            }
          });
    },
    scroll: function (event) {
      if (event.deltaX < 0 || event.deltaY < 0)
        this.prevPage();
      else if (event.deltaX > 0 || event.deltaY > 0)
        this.nextPage();
    },
    findCurrent: function () {
      if (this.images.length === 0)
        return;

      if (this.current.identifier < this.images[0].identifier)
        this.prevPage(this.findCurrent);
      else if (this.current.identifier > this.images[this.images.length - 1].identifier)
        this.nextPage(this.findCurrent);
    }
  },
  watch: {
    current: function (newVal) {
      if (!newVal) {
        this.elements.previous = false;
        this.elements.next = false;
        this.$emit('hasPreviousElement', false);
        this.$emit('hasNextElement', false);
        return;
      }

      // find current in list
      this.findCurrent();

      // receive previous and next element
      this.$root.socket.get(`/data/${this.current.identifier}/previous_next`)
          .then(response => response.json())
          .then(data => {
            if (this.filter === undefined || this.filter === false) {
              this.elements.previous = data.previous;
              this.elements.next = data.next;
            } else {
              this.elements.previous = data.previousInCollection;
              this.elements.next = data.nextInCollection;
            }

            this.$emit('hasPreviousElement', this.elements.previous);
            this.$emit('hasNextElement', this.elements.next);
          });
    },
    filter: function () {
      this.get(() => {
        if (this.images.length === 0)
          this.$emit('click', false);
        else
          this.$emit('click', this.images[0]);
      });
    }
  }
}
</script>

<style scoped>
.paginated-media {
  display: flex;
  flex-direction: column;
  overflow-y: hidden;
}

.media {
  flex-grow: 1;
  overflow-y: hidden;

  display: grid;
  justify-content: space-between;
  align-content: space-between;
}

.media .image {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}

.media img {
  max-width: 100%;
  max-height: 100%;
}

.media .active {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;

  box-shadow: 0 0 20px -5px var(--primary) inset;
}

.media .delete {
  position: absolute;
  top: 0.15rem;
  right: 0.15rem;

  background-color: rgba(255, 255, 255, 0.4);
  border-radius: 2rem;
  padding: 0.3rem 0.3rem 0.1rem;
  filter: invert(1);
}

.media .delete img {
  width: 1rem;
  height: 1rem;
}

.pagination {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

.pagination .button {
  font-weight: bold;
  font-size: 200%;
  padding: 0 1.5rem;
  cursor: pointer;
  opacity: 0.33;
}

.pagination .clickable {
  cursor: pointer;
  opacity: 1;
}
</style>
