<template>
  <div
    v-if="loaded"
    class="crops"
  >
    <cropper-view
      v-if="cutout"
      :src="src"
      :aspect-ratio="aspectRatio"
      cancel-initial-status
      class-handler="cutout"
      reset-button
      :original-dimensions="originalDimensions"
      :reset-initial-status="!!cutoutLength"
      :crop-guide="cropGuide || initialCropGuide"
      :title="`Editing ${aspectRatioString} ${name} Rendition`"
      @closeView="$emit('closeCropView')"
      @newCropGuide="setCropGuide"
      @saveChanges="handleSaveClick"
      @resetChanges="setCropGuide"
    />
    <cropper-view
      v-if="masterCrop && aspectRatio && cropGuide"
      cancel-initial-status
      save-initial-status
      :src="src"
      :aspect-ratio="aspectRatio"
      :original-dimensions="originalDimensions"
      class-handler="master-crop"
      :crop-guide="cropGuide || initialCropGuide"
      title="Editing Master Crop"
      @closeView="$emit('closeCropView')"
      @newCropGuide="setCropGuide"
      @saveChanges="handleSaveMasterCrop"
    >
      <template v-slot:options>
        <p class="master-crop--aspect-ratio-title">
          Master Crop Aspect Ratio
        </p>
        <div class="master-crop--aspect-ratio-actions-container">
          <ui-icon-button
            v-for="item in Object.values(masterCropConfig.crops)"
            :key="`ui-button-${item.id}`"
            :ref="`button-${item.id}`"
            class="master-crop--aspect-ratio-button"
            :class="{ active: aspectRatioString === item.id }"
            :tooltip="item.name"
            type="secondary"
            button-type="button"
            @click="setAspectRatio(item.id)"
          >
            <span
              :style="getMasterCropStyle(item)"
              class="aspect-ratio"
            />
          </ui-icon-button>
        </div>
      </template>
    </cropper-view>
  </div>
</template>
<script>

import { addCutout, deleteCutout } from 'Api';
import { hasStatusError } from 'Helpers/api';
import { getOriginalImageName } from 'Helpers/asset';
import { DEFAULT_ASPECT_RATIO } from 'Config/constants';
import CropperView from './CropperView.vue';

const { UiIconButton } = window.kiln.utils.components;

export default {
  components: {
    CropperView,
    UiIconButton
  },
  props: {
    asset: {
      type: Object,
      default: () => ({})
    },
    assetId: {
      type: String,
      required: true
    },
    cutouts: {
      type: Object,
      default: () => ({})
    },
    cutout: {
      type: Boolean,
      default: false
    },
    fileName: {
      type: String,
      default: ''
    },
    loaded: {
      type: Boolean,
      default: false
    },
    masterCrop: {
      type: Boolean,
      default: false
    },
    renditionDetails: {
      type: Object,
      default: () => ({})
    },
    masterCropConfig: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      name: '',
      aspectRatio: 1,
      aspectRatioString: DEFAULT_ASPECT_RATIO,
      cropGuide: {},
      initialCropGuide: null,
      renditionKey: '',
      src: ''
    };
  },
  computed: {
    ASPECT_RATIO() {
      return { ...this.masterCropConfig };
    },
    cutoutObject() {
      return this.cutouts[this.renditionKey] || {};
    },
    cutoutLength() {
      return Object.keys(this.cutoutObject).length;
    },
    path() {
      return this.asset && this.asset.path ? this.asset.path : '';
    },
    originalImageName() {
      return getOriginalImageName(this.path, this.fileName);
    },
    originalDimensions() {
      const { dimension: { width, height } = {}, masterCrop = {} } = this.asset || {},
        hasMasterCrop = masterCrop && Object.keys(masterCrop).length;

      if (this.cutout && !this.ignoreMasterCrop && hasMasterCrop) {
        if (width && height) {
          return {
            originalWidth: width * (masterCrop.right - masterCrop.left),
            originalHeight: height * (masterCrop.bottom - masterCrop.top)
          };
        }
      }

      return {
        originalWidth: width,
        originalHeight: height
      };
    },
    ignoreMasterCrop() {
      return this.renditionDetails && this.renditionDetails.ignoreMasterCrop;
    }
  },
  mounted() {
    if (this.cutout && Object.keys(this.renditionDetails).length) {
      this.setCutoutCropGuide();
    } else if (this.masterCrop) {
      this.setMasterCropGuide();
    }
  },
  methods: {
    // General methods
    /**
     * Sets cutout to the value obtained from child component
     * @param {Object|null} newCutoutObject New cutout to set
     */
    setCropGuide(newCutoutObject) {
      this.cropGuide = newCutoutObject;
    },

    getMasterCropStyle({ value }) {
      const max = 20;

      // horizontal
      if (value > 1) {
        return {
          width: `${max}px`,
          height: `${max / value}px`
        };
      }

      // vertical
      return {
        width: `${max * value}px`,
        height: `${max}px`
      };
    },

    // Cutout methods
    /**
     * Calculates the crop guide from cutout
     * @returns {Object} crop guide to use in cropper
     */
    cropGuideCalculation() {
      const { defaultRenditionCrop } = this.renditionDetails,
        [x, y, cropWidth, cropHeight] = defaultRenditionCrop.map(Math.abs).map(Math.ceil),
        { dimension: { width, height } = {}, masterCrop = {} } = this.asset || {},
        { originalHeight, originalWidth } = this.originalDimensions;

      let top = y / originalHeight,
        left = x / originalWidth;

      // if there is master crop, use it for renditions
      if (masterCrop && Object.keys(masterCrop).length) {
        top = (y - masterCrop.top * height) / originalHeight;
        left = (x - masterCrop.left * width) / originalWidth;
      }

      return {
        top,
        left,
        right: left + cropWidth / originalWidth,
        bottom: top + cropHeight / originalHeight
      };
    },
    /**
     * Handles click on Save button
     */
    handleSaveClick() {
      this.cropGuide ? this.saveCutout() : this.removeCutout();
    },
    /**
     * Removes the cutout
     */
    removeCutout() {
      deleteCutout(this.assetId, this.renditionKey)
        .then((data) => {
          if (hasStatusError(data)) {
            this.$emit('alert', `${data.error}: ${data.message}`);
          } else {
            this.$emit('closeCropView', true);
          }
        });
    },
    /**
     * Saves the new cutout into asset details
     */
    saveCutout() {
      addCutout(this.assetId, this.renditionKey, this.cropGuide)
        .then((data) => {
          if (hasStatusError(data)) {
            this.$emit('alert', `${data.error}: ${data.message}`);
          } else {
            this.$emit('closeCropView', true);
          }
        });
    },
    /**
     * Sets the initial crop guide for cutouts
     */
    setCutoutCropGuide() {
      const {
        name,
        aspectRatioString,
        aspectRatio,
        renditionKey,
        ignoreMasterCrop
      } = this.renditionDetails;

      this.name = name;
      this.aspectRatioString = aspectRatioString;
      this.aspectRatio = aspectRatio;
      this.renditionKey = renditionKey;
      this.src = ignoreMasterCrop ? this.originalImageName : this.path;

      this.initialCropGuide = this.cropGuideCalculation();

      if (this.cutoutLength) {
        this.cropGuide = this.cutoutObject;

        return;
      }

      this.cropGuide = { ...this.initialCropGuide };
    },

    // Master crop methods
    /**
     * Get aspect ratio used in the crop guide
     * @return {Number} Aspect ratio of current crop guide
     */
    getAspectRatioFromMasterCrop() {
      const {
        masterCrop: {
          bottom, left, top, right
        },
        dimension: { width, height }
      } = this.asset;

      return width * (right - left) / (height * (bottom - top));
    },
    /**
     * Gets aspect ratio from master crop guide
     * @param {Number} masterCropAspectRatio
     * @returns {string}
     */
    getClosestAspectRatio(masterCropAspectRatio) {
      return Object.values(this.ASPECT_RATIO.crops).reduce((accum, item) => {
        const difference = Math.abs(item.value - masterCropAspectRatio);

        if (difference < accum.value) {
          return {
            value: difference,
            id: item.id
          };
        }

        return accum;
      }, { value: Infinity, id: this.ASPECT_RATIO.default.id }).id;
    },
    /**
     * Get default crop guide for specific aspect ratio selected
     * @return {Object} Crop guide configuration object
     */
    getDefaultCropGuide() {
      const coords = {
          top: 0,
          bottom: 1,
          left: 0,
          right: 1
        },
        { width, height } = this.asset.dimension;

      if (this.aspectRatio > (width / height)) {
        const verticalRatio = (width / this.aspectRatio) / (height * 2);

        coords.top = 0.5 - verticalRatio;
        coords.bottom = 0.5 + verticalRatio;
      } else {
        const horizontalRatio = (height * this.aspectRatio) / (width * 2);

        coords.left = 0.5 - horizontalRatio;
        coords.right = 0.5 + horizontalRatio;
      }

      return coords;
    },
    /**
     * Emits event to save the crop guide
     */
    handleSaveMasterCrop() {
      this.$emit('saveMasterCrop', this.cropGuide);
    },
    /**
     * Set fixed aspect ratio to the crop guide
     * @param {String} ratio Ratio to be set
     */
    setAspectRatio(ratio) {
      this.aspectRatio = this.ASPECT_RATIO.crops[ratio].value;
      this.aspectRatioString = ratio;
      this.cropGuide = this.getDefaultCropGuide();
    },
    /**
     * Sets the initial crop guide for master crop
     */
    setMasterCropGuide() {
      const { masterCrop = null } = this.asset;

      this.src = this.originalImageName;

      if (masterCrop) {
        const masterCropAspectRatio = this.getAspectRatioFromMasterCrop();

        this.aspectRatioString = this.getClosestAspectRatio(masterCropAspectRatio);
        this.aspectRatio = this.ASPECT_RATIO.crops[this.aspectRatioString].value;
        this.cropGuide = masterCrop;
      } else {
        this.aspectRatio = this.ASPECT_RATIO.default.value;
        this.aspectRatioString = this.ASPECT_RATIO.default.id;
        this.cropGuide = this.getDefaultCropGuide();
      }
    }
  }
};
</script>
<style lang="scss" scoped>
  @import 'Styles/colors.scss';
  @import 'Styles/fonts.scss';
  @import 'Styles/helpers.scss';

  .master-crop {
    &--aspect-ratio-title {
      @extend .aspect-ratio-title;

      color: $light-gray;
      margin-bottom: 10px;
    }

    &--aspect-ratio-button {
      border: solid 1px $gray;
      border-radius: 0;
      height: 40px;
      margin-left: -1px;
      min-width: unset;
      padding: 0;
      width: 40px;
    }

    &--aspect-ratio-actions-container {
      display: flex;
      justify-content: center;
      margin-bottom: 20px;

      .master-crop--aspect-ratio-button {
        .aspect-ratio {
          position: absolute;
          transform: translateX(-50%) translateY(-50%);
        }

        &.active {
          border-color: $black;
          z-index: 1;

          .aspect-ratio {
            background-color: $black;
          }
        }
      }
    }

    .aspect-ratio {
      background-color: $font-primary;
    }
  }
</style>
