<!--
 Assetmanager todo:
  - Tonen van maten in overzicht en detail
  - Bestandsnaam (titel in overzicht) kunnen aanpassen
  - Opschonen van assets (alleen assets die nergens gebruikt worden)
  - Lijst van pagina's waar de asset gebruikt wordt
  - Updaten van afbeeldingen in een asset
  - In overzicht, sorteren op created/updated date
  - Delete-button enkel enabled zetten als asset nergens gebruikt wordt
  - Zoeken op naam (nice to have)
  - Aantal KB's in detail scherm
-->
<template>
  <form @submit.prevent="saveAsset" :class="{ 'form-in-modal': isInModal }">
    <div :class="{ 'modal-body': isInModal }">
      <div class="form-group row">
        <div class="col-sm-8">
          <fieldset>
            <field :label="maxUploadSize != null ? `Bestand (max. ${maxUploadSize} MB)` : 'Bestand'" v-if="isNew">
              <input type="file" @change="selectFile($event.target.files[0])" class="form-control-file" />
              <div class="figure-caption">
                Gebruik voor afbeeldingen bij voorkeur het JPG-bestandsformaat, tenzij PNG noodzakelijk is voor transparantie in de
                afbeelding.
              </div>
            </field>

            <field
              info="De titel wordt gebruikt in het overzicht, aan de hand van de titel kun je het bestand later terugvinden."
              label="Titel"
            >
              <input type="text" class="form-control" v-model="asset.title" :readonly="isReadOnly" />
              <validator :test="asset.title.length <= titleMaxLength"
                >De titel mag maximaal 128 tekens bevatten. Je hebt er {{ asset.title.length - titleMaxLength }} teveel.</validator
              >
            </field>

            <field label="Tags">
              <relation-picker
                module="cmsTag"
                id="tags"
                v-model="asset.tagIds"
                :source="tags"
                display-field="title"
                :multi="true"
                :required="false"
                :readonly="isReadOnly"
              ></relation-picker>
            </field>

            <field label="Bestandstype" v-if="asset.blobstoreUrl">
              <input type="text" class="form-control" v-model="asset.contentType" readonly />
            </field>

            <field label="Afmetingen" v-if="asset.width">
              <input type="text" class="form-control" v-model="imageDimensions" readonly />
            </field>

            <field label="Download URL" v-if="!isImage && asset.sourceUrl">
              <input type="text" class="form-control" v-model="asset.sourceUrl" readonly />
            </field>
          </fieldset>
        </div>
        <div class="col-sm-4">
          <loader>
            <div class="preview-wrapper" v-if="imageUrl">
              <img ref="img" :src="imageUrl" @load="updatePreviewUrl" crossorigin="anonymous" v-if="imageUrl != asset.blobstoreUrl" />
              <img :src="imageUrl" v-if="imageUrl == asset.blobstoreUrl" />
            </div>
          </loader>
        </div>
      </div>

      <div class="editor container image-editor" v-if="!isImage && !isNew">
        <div class="card">
          <div class="card-body">
            <div class="form-group pt-2 pb-2" v-show="!isImage">
              <h4>Bestandsnamen</h4>
              <p>Naam van het bestand als deze gedownload wordt.</p>

              <field
                info=""
                v-for="filenameTranslation in asset.filenameTranslations"
                :label="getLanguageDisplayName(filenameTranslation.language)"
                :key="filenameTranslation.language"
              >
                <input type="text" class="form-control" v-model="filenameTranslation.filename" />
              </field>
            </div>
          </div>
        </div>
      </div>

      <div class="editor container image-editor" v-if="isImage && canSubmit">
        <div class="card">
          <div class="card-body">
            <div class="form-group pt-2 pb-2" v-show="isImage">
              <h4>Alt teksten</h4>
              <p>
                Geef een korte samenvatting van wat er te zien is op de afbeelding (max. 140 tekens). Zo weten zoekmachines, blinden &amp;
                slechtzienden ook wat er op een afbeelding staat.
                <a href="https://www.emerce.nl/achtergrond/waarom-afbeeldingen-altijd-alt-tekst-moeten-hebben" target="_blank"
                  >Meer uitleg over alt teksten</a
                >
              </p>
              <field
                info=""
                v-for="altTranslation in asset.altTranslations"
                :label="getLanguageDisplayName(altTranslation.language)"
                :key="altTranslation.language"
              >
                <input :readonly="isReadOnly" type="text" class="form-control" v-model="altTranslation.alt" />
              </field>
            </div>
            <asset-detail-center-point v-if="!isReadOnly" :imageUrl="imageUrl" :asset="asset" v-model="asset.centerPoint" />
            <asset-detail-variants :asset="asset" v-if="imageUrl && !isNew && !isReadOnly" />
          </div>
        </div>
      </div>
    </div>

    <div v-if="isInModal" class="modal-footer">
      <button slot="right" @click.prevent="cancel" class="btn btn-secondary-outline">Annuleren</button>
      <button slot="right" type="submit" class="btn btn-primary mr-2" :disabled="!permissions.canUpdate || !canSubmit">
        <span v-if="fetchActionRunning" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
        <span v-else class="icon-floppy-disk"></span>
        Opslaan
      </button>
    </div>

    <bottom-toolbar v-else-if="!isReadOnly">
      <button @click.prevent="remove" class="btn btn-danger mr-2" :disabled="!permissions.canDelete" v-if="!isNew">
        <span class="icon-cross"></span>
        Verwijderen
      </button>
      <button slot="right" @click.prevent="cancel" class="btn btn-secondary-outline">Annuleren</button>
      <button slot="right" type="submit" class="btn btn-primary mr-2" :disabled="!permissions.canUpdate || !canSubmit">
        <span v-if="fetchActionRunning" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
        <span v-else class="icon-floppy-disk"></span>
        Opslaan
      </button>
    </bottom-toolbar>

    <bottom-toolbar v-else>
      <button slot="right" @click.prevent="cancel" class="btn btn-secondary-outline">Terug naar overzicht</button>
    </bottom-toolbar>

    <confirm-dialog ref="dialog"></confirm-dialog>
  </form>
</template>

<style lang="scss" scoped>
.preview-wrapper {
  background: rgba(204, 204, 204, 0.3)
    repeating-linear-gradient(45deg, transparent, transparent 5px, rgba(255, 255, 255, 0.2) 5px, rgba(255, 255, 255, 0.2) 10px);
  display: block; // override flex from bootstrap
  text-align: center;
}

img {
  max-width: 100%;
  max-height: 250px;
}

.form-controls {
  display: flex;
  justify-content: space-between;
  width: 100%;

  button:first-child {
    margin-left: auto;
  }
}

.figure-caption {
  margin-top: 5px;
}

.form-row-url {
  display: flex;
  justify-content: space-between;

  &-input {
    flex: 1 1 auto;
    margin-right: 10px;
  }

  &-button {
    width: 100px;
    display: flex;
    justify-content: flex-end;
    align-items: center;

    input {
      margin-right: 5px;
    }

    label {
      margin-bottom: 0;
    }
  }
}

.image-editor {
  margin-bottom: 10px;
}

.form-in-modal {
  display: flex;
  flex-direction: column;
  height: 70vh;
}

.modal-body {
  overflow-y: auto;
}
</style>

<script>
import { PermissionTypes } from '../stores/types/PermissionStoreModel';

import StackBlur from 'stackblur-canvas/src/stackblur.js';

import { mapActions, mapGetters, mapState } from 'vuex';
import { compress } from '../tools/image-preview.js';
import AssetApi from '../api/asset-api';
import { ValueSyncMixin } from '../shared/value-sync-mixin.js';

import assetDetailVariants from './asset-detail-variants.vue';
import assetDetailCenterPoint from './asset-detail-center-point.vue';
import relationPicker from '../shared/form/relation-picker.vue';
import bottomToolbar from '../shared/form/bottom-toolbar.vue';
import confirmDialog from '../shared/form/confirm-dialog.vue';
import field from '../shared/form/field.vue';
import loader from '../shared/loader.vue';
import * as assetUtils from '../shared/asset-utils';
import { getCmsSettings } from '../tools/settings';
import Validator from '../shared/form/validator.vue';
import ValidatorGroupBase from '../tools/validator-group-base.vue';

const TITLE_MAX_LENGTH = 128;

export default {
  mixins: [ValueSyncMixin],
  extends: ValidatorGroupBase,
  name: 'asset-detail',
  props: {
    value: {
      type: String,
      required: false
    },
    assetProp: {},

    // used for asset-picker
    isInModal: {
      type: Boolean,
      required: false
    }
  },
  data() {
    return {
      fetchActionRunning: false,
      selectedFile: null,
      selectedFileDataUrl: null,
      imageUrl: null,
      isLoading: false,
      asset: this.assetProp || {
        title: '',
        alt: '',
        altTranslations: [],
        centerPoint: null,
        blobstoreUrl: '',
        previewData: '',
        tagIds: this.items || [],
        variants: getCmsSettings().defaultImageCrops || []
      },
      titleMaxLength: TITLE_MAX_LENGTH
    };
  },
  mounted() {
    // TODO: improve this solution
    if (!this.assetProp) {
      this.asset.altTranslations = this.languageInfos.map((l) => ({ language: l.language, alt: '' }));
    }

    this.setPreviewUrl();
  },
  methods: {
    ...mapActions('assets', ['createAsset', 'deleteAsset', 'updateAsset']),
    async saveAsset() {
      try {
        this.fetchActionRunning = true;

        const asset = this.isNew
          ? await this.createAsset(this.createFormDataObject(this.asset, this.selectedFile))
          : await this.updateAsset(this.asset);

        this.valueCopy = asset.id;
        this.$store.dispatch('alerts/addSuccess', 'De asset is opgeslagen');
      } catch (err) {
        this.$store.dispatch('alerts/addCmsError', err);
      } finally {
        this.fetchActionRunning = false;
      }
    },
    createFormDataObject(asset, fileData) {
      const data = new FormData();
      data.append('file', fileData);
      data.append('title', asset.title);
      data.append('alt', asset.alt);
      data.append('previewData', asset.previewData);
      data.append('centerPointX', asset.centerPoint && asset.centerPoint.x);
      data.append('centerPointY', asset.centerPoint && asset.centerPoint.y);

      if (asset.width && asset.height) {
        data.append('width', asset.width);
        data.append('height', asset.height);
      }

      for (let i = 0; i < asset.altTranslations.length; i++) {
        data.append(`altTranslations[${i}].language`, asset.altTranslations[i].language);
        data.append(`altTranslations[${i}].alt`, asset.altTranslations[i].alt);
      }

      if (this.isImage) {
        for (let i = 0; i < asset.variants.length; i++) {
          let variant = asset.variants[i];
          data.append(`variants[${i}].id`, variant.id);
          data.append(`variants[${i}].fixedAspectRatio`, variant.fixedAspectRatio);
          data.append(`variants[${i}].name`, variant.name);
          data.append(`variants[${i}].crop.X`, variant.crop.x);
          data.append(`variants[${i}].crop.Y`, variant.crop.y);
          data.append(`variants[${i}].crop.width`, variant.crop.width);
          data.append(`variants[${i}].crop.height`, variant.crop.height);
          data.append(`variants[${i}].isUnTouched`, variant.isUnTouched);
          data.append(`variants[${i}].isDefaultVariant`, variant.isDefaultVariant);
        }
      }

      for (let i = 0; i < asset.tagIds.length; i++) {
        data.append(`tagIds[${i}]`, asset.tagIds[i]);
      }

      return data;
    },
    selectFile(file) {
      this.selectedFile = file;
      if (this.selectedFile) {
        var reader = new FileReader();
        reader.onloadstart = () => {
          this.isLoading = true;
        };
        reader.onloadend = () => {
          this.isLoading = false;
        };
        reader.onload = (e) => {
          this.selectedFileDataUrl = e.target.result;
        };
        reader.readAsDataURL(this.selectedFile);

        // fill asset Title with filename of uploaded asset
        if (this.asset.title === '') {
          this.asset.title = file.name.replace(/\.[^.]*$/, ''); // regex removes file extension
        }
      }
    },
    async remove() {
      try {
        await this.$refs.dialog.confirm('Asset verwijderen', 'Weet je zeker dat je deze asset wilt verwijderen?');

        try {
          await this.deleteAsset(this.asset.id);

          this.valueCopy = null;
          this.$store.dispatch('alerts/addSuccess', 'De asset is verwijderd');
        } catch (error) {
          if (error.isReferenceError) {
            this.$emit('deleteError', error);
          }

          this.$store.dispatch('alerts/addCmsError', error);

          throw error;
        }
      } catch (error) {
        // intentionally empty for removing console error when dismissing dialog
      }
    },
    cancel() {
      if (this.isInModal) {
        this.$emit('cancel');
      } else {
        this.$router.push({ name: 'asset-overview' });
      }
    },
    updatePreviewUrl() {
      const cnv = document.createElement('canvas');
      const ctx = cnv.getContext('2d');

      // add width/height for new images only
      if (this.isNew) {
        const width = this.$refs.img.naturalWidth;
        const height = this.$refs.img.naturalHeight;
        this.asset.width = width;
        this.asset.height = height;

        this.asset.centerPoint = {
          x: Math.round(width / 2),
          y: Math.round(height / 2)
        };

        this.asset.variants = this.asset.variants.map((v) => ({
          ...v,
          crop: assetUtils.getDefaultCropDataForAspectRatio(width, height, v.fixedAspectRatio)
        }));
      }

      const maxSize = 32;
      const ratio = this.$refs.img.naturalWidth / this.$refs.img.naturalHeight;
      const width = (cnv.width = Math.min(maxSize, Math.round(maxSize * ratio)));
      const height = (cnv.height = Math.min(maxSize, Math.round(maxSize / ratio)));

      ctx.drawImage(this.$refs.img, 0, 0, width, height);
      StackBlur.canvasRGBA(cnv, 0, 0, width, height, 5);

      this.asset.previewData = compress(cnv);
    },
    getLanguageDisplayName(language) {
      const info = this.languageInfos.find((l) => l.language === language);
      return info && info.displayName;
    },
    changeVariantsToNewCenterPoint() {
      this.asset.variants = this.asset.variants.map((v) => {
        if (!v.isUnTouched) {
          return v;
        }
        const { width, height } = this.asset;
        return {
          ...v,
          crop: assetUtils.getCropForAspectRatioForCenterPoint(width, height, v.fixedAspectRatio, this.asset.centerPoint)
        };
      });
    },
    async setPreviewUrl() {
      this.imageUrl =
        this.selectedFileDataUrl && this.isImage ? this.selectedFileDataUrl : await AssetApi.getPreviewUrlForAsset(this.asset);
    }
  },
  computed: {
    ...mapState('translations', ['languageInfos']),
    ...mapState('cmsTag', ['items']),
    ...mapGetters('permissions', ['getPermissions']),
    permissions() {
      return this.getPermissions('CmsAsset');
    },
    isNew() {
      return !this.asset.id;
    },
    isReadOnly() {
      return PermissionTypes[String(this.asset.userEntityPermission)] === PermissionTypes.Read;
    },
    isImage() {
      return this.asset.contentType
        ? this.asset.contentType.startsWith('image/')
        : this.selectedFile
        ? this.selectedFile.type.startsWith('image/')
        : false;
    },
    canSubmit() {
      return (this.asset.blobstoreUrl || this.selectedFile) && this.asset.title && this.isValid && !this.fetchActionRunning;
    },
    imageDimensions() {
      return this.asset.width + ' x ' + this.asset.height;
    },
    maxUploadSize() {
      return getCmsSettings().maxUploadSize != null ? getCmsSettings().maxUploadSize / 1000000 : null;
    },
    tags() {
      return this.items || [];
    }
  },
  provide() {
    return {
      // Assets are non-translatable but relation-picker expects this function to be injected from the parent object
      getCurrentLanguage() {}
    };
  },
  components: {
    Validator,
    bottomToolbar,
    confirmDialog,
    field,
    loader,
    relationPicker,
    assetDetailVariants,
    assetDetailCenterPoint
  },
  watch: {
    'asset.centerPoint'() {
      this.changeVariantsToNewCenterPoint();
    },
    selectedFileDataUrl() {
      this.setPreviewUrl();
    }
  }
};
</script>
