<template>
  <img v-if="isBrowser()" ref="imageRef" />
  <img v-else :src="getSsrSrc()" />
</template>

<script lang="ts">
/**
 * @mixin VueSDK
 * @description The Cloudinary Vue SDK contains components like \<AdvancedImage\> to easily render your media assets from Cloudinary.
 * The SDK also comes with support for optional JS plugins that make the components smart, with features like lazy loading, placeholder, accessibility & responsiveness.
 *
 * @example
 * <caption>
 *  Please note that the order of the plugins is important. See {@link https://cloudinary.com/documentation/sdks/js/frontend-frameworks/index.html#plugin-order|Plugin Order} for more details.
 * </caption>
 * // AdvancedImage
 * <template>
 *   <div>
 *     <AdvancedImage :cldImg="cldImg" :plugins="plugins" />
 *   </div>
 * </template>
 *
 * <script lang="ts">
 * import { defineComponent } from 'vue';
 * import { CloudinaryImage } from '@cloudinary/url-gen/assets/CloudinaryImage';
 * import { AdvancedImage, responsive } from '@cloudinary/vue';
 *
 * export default defineComponent({
 *   name: 'App',
 *   components: {
 *     AdvancedImage,
 * },
 *   setup(props) {
 *     const cldImg = new CloudinaryImage(
 *       'sample',
 *       { cloudName: 'demo' },
 *       { analytics: false }
 *   );
 *
 *     const plugins = [responsive({ steps: 100 })];
 *
 *     return {
 *       cldImg,
 *       plugins,
 *   };
 *  },
 * });
 * </ script>
 * 
 * @example
 * <caption>
 *  Using `AdvancedVideo` custom defined resources.
 * </caption>
 * 
 * // AdvancedVideo
 * <template>
 *   <AdvancedVideo :cldVid="cldVid" :sources="sources" controls width="600" />
 * </template>
 *
 * <script lang="ts">
 * import { defineComponent } from "vue";
 * import { auto } from "@cloudinary/url-gen/qualifiers/videoCodec";
 * import { videoCodec } from "@cloudinary/url-gen/actions/transcode";
 * import { AdvancedVideo } from "../dist";
 * import { CloudinaryVideo } from "@cloudinary/url-gen/assets/CloudinaryVideo";
 *
 * export default defineComponent({
 *   name: "App",
 *   components: {
 *     AdvancedVideo,
 *  },
 *   setup(props) {
 *     const cldVid = new CloudinaryVideo(
 *       "dog",
 *       { cloudName: "demo" },
 *       { analytics: false }
 *     );
 *
 *     const sources = [
 *       {
 *         type: "mp4",
 *         transcode: videoCodec(auto()),
 *       },
 *       {
 *         type: "webm",
 *         transcode: videoCodec(auto()),
 *       },
 *     ];
 *
 *     return {
 *       cldVid,
 *       sources,
 *     };
 *   },
 * });
 * </ script>
 */

/**
 * @memberOf VueSDK
 * @module AdvancedImage
 * @description The Cloudinary image component.
 * @vue-prop {CloudinaryImage} cldImg Generated by @cloudinary/url-gen
 * @vue-prop {Plugins} plugins Advanced image component plugins accessibility(), responsive(), lazyload(), placeholder()
 */
</script>

<script setup lang="ts">
import { ref, onMounted, onUpdated, onUnmounted } from "vue";
import { CloudinaryImage } from "@cloudinary/url-gen/assets/CloudinaryImage";
import {
  HtmlImageLayer,
  isBrowser,
  serverSideSrc,
  cancelCurrentlyRunningPlugins,
  Plugins,
} from "@cloudinary/html";
import { SDKAnalyticsConstants } from "../internal/SDKAnalyticsConstants";

interface ImgProps {
  cldImg: CloudinaryImage;
  plugins?: Plugins;

  [x: string]: any;
}

// Disabled linting due to [@vue/compiler-sfc] `defineProps` is a compiler macro and no longer needs to be imported.
// eslint-disable-next-line no-undef
const props = defineProps<ImgProps>();

const imageRef = ref(null);
let htmlLayerInstance;

const getSsrSrc = () => serverSideSrc(props.plugins, props.cldImg, SDKAnalyticsConstants);

/**
 * On mount, creates a new HTMLLayer instance and initializes with ref to img element,
 * user generated cloudinaryImage and the plugins to be used.
 */
onMounted(() => {
  htmlLayerInstance = new HtmlImageLayer(
    imageRef.value,
    props.cldImg,
    props.plugins,
    SDKAnalyticsConstants
  );
});

/**
 * On update, we cancel running plugins and update image instance with the state of user
 * cloudinaryImage and the state of plugins.
 */
onUpdated(() => {
  cancelCurrentlyRunningPlugins(htmlLayerInstance.htmlPluginState);
  // call html layer to update the dom again with plugins and reset toBeCanceled
  htmlLayerInstance.update(props.cldImg, props.plugins, SDKAnalyticsConstants);
});

/**
 * On unmount, we cancel the currently running plugins.
 */
onUnmounted(() => {
  // Safely cancel running events on unmount.
  cancelCurrentlyRunningPlugins(htmlLayerInstance.htmlPluginState);
});
</script>