<template>
  <div class="project-labels-window">
    <h1 class="headline"
        :class="{target: target}"
        @dragover="dragover" @dragleave="dragleave" @drop="drop">
      Labels
    </h1>

    <template v-if="labels !== false">
      <label-tree-view v-for="label in sortedLabels" :key="label.identifier"
                       :label="label"
                       :targetable="true"
                       indent="2rem"/>

      <div class="label">
        <input-group>
          <text-input placeholder="New Label"
                      v-model="createLabelValue"
                      @enter="createLabel"/>
          <button-input type="primary" @click="createLabel">
            create
          </button-input>
        </input-group>
      </div>
    </template>
    <template v-else>
      <loading-icon/>
    </template>
  </div>
</template>

<script>
import TextInput from "@/components/base/text-input";
import ButtonInput from "@/components/base/button-input";
import InputGroup from "@/components/base/input-group";
import LabelTreeView from "@/components/other/LabelTreeView";
import LoadingIcon from "@/components/base/loading-icon";

export default {
  name: "project-labels-window",
  components: {LoadingIcon, LabelTreeView, InputGroup, ButtonInput, TextInput},
  created: function () {
    // get labels
    this.getLabels();

    // subscribe to changes
    this.$root.socket.on('connect', this.getLabels);
    this.$root.socket.on('create-label', this.addLabel);
    this.$root.socket.on('remove-label', this.removeLabel);
    this.$root.socket.on('edit-label', this.editLabel);
  },
  destroyed: function () {
    this.$root.socket.off('connect', this.getLabels);
    this.$root.socket.off('create-label', this.addLabel);
    this.$root.socket.off('remove-label', this.removeLabel);
    this.$root.socket.off('edit-label', this.editLabel);
  },
  data: function () {
    return {
      labels: false,
      createLabelValue: '',
      target: false
    }
  },
  computed: {
    sortedLabels: function () {
      return [...this.labels].sort((a, b) => a.name < b.name ? -1 : +1);
    }
  },
  methods: {
    getLabels: function () {
      this.$root.socket.get(`/projects/${this.$root.project.identifier}/labels/tree`)
          .then(response => response.json())
          .then(labels => {
            this.labels = labels;
          });
    },
    findLabel: function (labels, identifier) {
      for (let label of labels) {
        if (label.identifier === identifier)
          return label;

        const child = this.findLabel(label.children, identifier);
        if (child)
          return child;
      }

      return null;
    },
    addLabel: function (label) {
      // abort if label is not part of this project
      if (label['project_id'] !== this.$root.project.identifier)
        return;

      // prepare some values
      let labels = this.labels;

      if (!label.children)
        label.children = [];

      // label is not on root level
      if (label.parent_id !== null) {
        const parent = this.findLabel(labels, label.parent_id);

        if (parent) {
          labels = parent.children;
        } else {
          console.warn('could not find parent');
          return;
        }
      }

      // abort if label is already contained
      for (let l of labels)
        if (l.identifier === label.identifier)
          return;

      // add label
      labels.push(label);
    },
    removeLabel: function (label) {
      let labels = this.labels;

      // label is not on root level
      if (label.parent_id !== null) {
        const parent = this.findLabel(labels, label.parent_id);

        if (parent) {
          labels = parent.children;
        } else {
          console.warn('could not find parent');
          return;
        }
      }

      // remove label from list
      for (let i = 0; i < labels.length; i++) {
        if (labels[i].identifier === label.identifier) {
          labels.splice(i, 1);
          return;
        }
      }
    },
    editLabel: function (label) {
      // remove label if it is already contained
      const l = this.findLabel(this.labels, label.identifier);
      if (l) {
        this.removeLabel(l);
        label.children = l.children;
      }

      // add label on it's new position
      this.addLabel(label);
    },
    createLabel: function () {
      if (!this.createLabelValue)
        return;

      // TODO then / error
      this.$root.socket.post(`/projects/${this.$root.project.identifier}/labels`, {name: this.createLabelValue});
      this.createLabelValue = '';
    },
    dragover: function (e) {
      e.preventDefault();
      this.target = true;
    },
    dragleave: function () {
      this.target = false;
    },
    drop: function (e) {
      this.dragleave();

      const element = e.dataTransfer.getData('text/identifier');
      this.$root.socket.post(`/projects/${this.$root.project.identifier}/labels/${element}/parent`, {parent: null});
    }
  }
}
</script>

<style scoped>
.project-labels-window {
  padding: 1rem;
  overflow: auto;
}

h1.target {
  text-decoration: underline;
}

.loading-icon {
  width: 4rem;
  height: 4rem;
}
</style>
