import React from 'react';
import { CloudinaryImage } from '@cloudinary/url-gen/assets/CloudinaryImage';

import {
  HtmlImageLayer,
  Plugins,
  isBrowser,
  serverSideSrc,
  cancelCurrentlyRunningPlugins
} from '@cloudinary/html'
import { SDKAnalyticsConstants } from './internal/SDKAnalyticsConstants';

interface ImgProps {
  cldImg: CloudinaryImage;
  plugins?: Plugins;
  [x: string]: any;
}

/**
 * @mixin ReactSDK
 * @description The Cloudinary React 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>
 * // Example
 * import {CloudinaryImage} from "@cloudinary/url-gen/assets/CloudinaryImage";
 * import {
 *  AdvancedImage,
 *  accessibility,
 *  responsive,
 *  lazyload,
 *  placeholder
 * } from '@cloudinary/react';
 *
 * const App = () => {
 *
 * const myCld = new Cloudinary({ cloudName: 'demo'});
 * let img = myCld().image('sample');
 *
 *   return (
 *      <div>
 *         <div style={{height: "1000px"}}/>
 *         <AdvancedImage
 *            cldImg={img}
 *            plugins={[lazyload(), responsive(100), placeholder()]}
 *         />
 *      </div>
 *   )
 * };
 *
 *
 *
 *
 *
 */

/**
 * @memberOf ReactSDK
 * @type {Component}
 * @description The Cloudinary image component.
 * @prop {CloudinaryImage} cldImg Generated by @cloudinary/url-gen
 * @prop {Plugins} plugins Advanced image component plugins accessibility(), responsive(), lazyload(), placeholder()
 */
class AdvancedImage extends React.Component <ImgProps> {
  imageRef: React.RefObject<HTMLImageElement>;
  htmlLayerInstance: HtmlImageLayer;

  constructor(props: ImgProps) {
    super(props);
    this.imageRef = React.createRef();
  }

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

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

  /**
   * On unmount, we cancel the currently running plugins, and destroy the html layer instance
   */
  componentWillUnmount() {
    // Safely cancel running events on unmount.
    cancelCurrentlyRunningPlugins(this.htmlLayerInstance.htmlPluginState);
    this.htmlLayerInstance.unmount();
  }

  render() {
    const {
      cldImg,
      plugins,
      ...otherProps // Assume any other props are for the base element
    } = this.props;
    if (isBrowser()) { // On client side render
      return <img suppressHydrationWarning {...otherProps} ref={this.imageRef} />
    } else { // on server side render
      const src = serverSideSrc(
        this.props.plugins,
        this.props.cldImg,
        SDKAnalyticsConstants
      );
      return <img {...otherProps} src={src} />
    }
  }
}

export { AdvancedImage };