import React, {
  useState,
  useRef,
  useEffect,
  forwardRef,
  useImperativeHandle,
} from 'react';
import shaka from 'shaka-player';

const FORMAT_HTML5 = 'HTML5';
const FORMAT_HLS = 'HLS';
const FORMAT_DASH = 'DASH';

/**
 * react-native-video components for react-native-web
 */
const Video = forwardRef((props, ref) => {
  const {
    source,
    poster,
    paused,
    autoplay,
    controls,
    muted,
    repeat,
    inline,
    volume,
    rate,
    onLoad,
    onLoadStart,
    onReadyForDisplay,
    onPlaybackRateChange,
    onProgress,
    onSeek,
    onEnd,
    onError,
    onExitFullscreen,
  } = props;

  // State
  const [format, setFormat] = useState(FORMAT_HTML5);
  const [shakaPlayer, setShakaPlayer] = useState(null);

  // Video ref
  const video = useRef(null);

  useEffect(() => {
    //console.log('Video.useEffect([])');
    // Bind listeners
    bindListeners();
    return () => {
      // Destroy shaka palyer
      if (shakaPlayer) {
        shakaPlayer.destroy();
      }
      // Unbind listeners
      unbindListeners();
    };
  }, []);

  useEffect(() => {
    if (source) {
      // Get file extension from source
      const extension = source.split(/[#?]/)[0].split('.').pop().trim();
      // Get format
      let format = FORMAT_HTML5;
      if (extension === 'm3u8') {
        format = FORMAT_HLS;
      } else if (extension === 'mpd') {
        format = FORMAT_DASH;
        setShakaPlayer(new shaka.Player(video.current));
      }
      setFormat(format);
    }
  }, [source]);

  useEffect(() => {
    //console.log('Video.useEffect([shakaPlayer, source])');
    if (shakaPlayer && source) {
      shakaPlayer.load(source);
    }
  }, [shakaPlayer, source]);

  useEffect(() => {
    //console.log("Video.useEffect([paused])", paused);
    // Toggle play / pause from parent
    if (paused === false) {
      play();
    } else if (paused === true) {
      pause();
    }
  }, [paused]);

  useEffect(() => {
    console.log('rate: ', rate);
    if (rate) {
      if (rate === 0) {
        pause();
      } else {
        play();
        // TODO: handle rate < 1
      }
    }
  }, [rate]);

  useEffect(() => {
    if (volume >= 0 && volume <= 1) {
      video.current.volume = volume;
    }
  }, [volume]);

  // Private methods

  function play() {
    //console.log('Video.play()');
    if (video.current.paused) {
      if (video.current.currentTime > 0 || !autoplay) {
        // Handle old exception
        let playPromise = null;
        try {
          playPromise = video.current.play();
        } catch (error) {
          onError(error);
        }
        // Handle promise
        if (playPromise !== undefined) {
          playPromise
            .then(() => {
              // playback started
            })
            .catch((error) => {
              onError(error);
            });
        }
      }
    }
  }

  function pause() {
    //console.log('Video.pause()');
    if (!video.current.paused) {
      video.current.pause();
    }
  }

  // react-native-video API (public methods)

  useImperativeHandle(ref, () => ({
    /**
     * react-native-video seek() method requires seconds
     *
     * @param seconds
     */
    seek: (seconds) => {
      //console.log('Video.seek(' + seconds + ')');
      if (seconds) {
        if (seconds >= 0 && seconds < video.current.duration) {
          video.current.currentTime = seconds;
        }
      }
    },

    presentFullscrenPlayer: () => {
      //console.log('Video.presentFullscreenPlayer()');
      if (video) {
        if (video.current.requestFullscreen) {
          video.current.requestFullscreen();
        }
        // Deprecated
        else if (video.current.enterFullscreen) {
          video.current.enterFullscreen();
        } else if (video.current.webkitEnterFullscreen) {
          video.current.webkitEnterFullscreen();
        }
      }
    },

    dismissFullscreenPlayer: () => {
      //console.log('Video.dismissFullscreenPlayer()');
      if (document.exitFullscreen) {
        document.exitFullscreen();
      }
    },
  }));

  // react-native-video callback proxy

  /**
   * loadedmetadata => onLoad
   */
  function onVideoLoadedMetadata() {
    //console.log('Video.onVideoLoadedMetadata()');
    if (onLoad && video) {
      onLoad({
        currentPosition: 0,
        duration: video.current.duration,
        // TODO: naturalSize, audioTracks, textTracks
      });
    }
  }

  /**
   * loadstart => onLoadStart
   */
  function onVideoLoadStart() {
    if (source) {
      //console.log('Video.onVideoLoadStart()');
      if (onLoadStart && video) {
        onLoadStart({
          isNetwork: true,
          type: '',
          uri: source.uri,
        });
      }
    }
  }

  /**
   * waiting => onLoadStart
   */
  function onVideoWaiting() {
    //console.log('Video.onVideoWaiting()');
    if (onLoadStart && video) {
      onLoadStart({
        isNetwork: true,
        type: '',
        uri: source.uri,
      });
    }
  }

  /**
   * canplaythrough => onReadyForDisplay
   */
  function onVideoCanPlayThrough() {
    //console.log('Video.onVideoCanPlayThrough()');
    if (video) {
      if (onReadyForDisplay) {
        onReadyForDisplay();
      }
    }
  }

  /**
   * play => onPlaybackRateChange
   */
  function onVideoPlay() {
    //console.log('Video.onVideoPlay()');
    if (onPlaybackRateChange) {
      onPlaybackRateChange({playbackRate: 1});
    }
  }

  /**
   * pause => onPlaybackRateChange
   */
  function onVideoPause() {
    //console.log('Video.onVideoPause()');
    if (onPlaybackRateChange) {
      onPlaybackRateChange({playbackRate: 0});
    }
  }

  /**
   * ratechange => onPlaybackRateChange
   */
  function onVideoRateChange() {
    //console.log('Video.onVideoRateChange()');
    if (onPlaybackRateChange && video) {
      onPlaybackRateChange({
        playbackRate: video.current.playbackRate,
      });
    }
  }

  /**
   * timeupdate => onProgress
   */
  function onVideoTimeUpdate() {
    //console.log('Video.onVideoTimeUpdate()');
    if (onProgress && video) {
      onProgress({
        seekableDuration: video.current.duration,
        playbableDuration: video.current.duration,
        currentTime: video.current.currentTime,
      });
    }
  }

  /**
   * seeked => onSeek
   */
  function onVideoSeeked() {
    //console.log('Video.onVideoSeeked()');
    if (onSeek && video) {
      onSeek({
        currentTime: video.current.currentTime,
        seekTime: video.current.currentTime,
      });
    }
  }

  /**
   * ended => onEnd
   */
  function onVideoEnded() {
    //console.log('Video.onVideoEnded()');
    if (onEnd) {
      onEnd();
    }
  }

  /**
   * error => onError
   */
  function onVideoError() {
    if (source) {
      //console.log('Video.onVideoError()');
      let error = {};
      // TODO: return same errors as react-native-video
      if (onError) {
        onError(error);
      }
    }
  }

  /**
   * Get exit fullscreen event for webkit
   */
  function onVideoEndFullscreen() {
    //console.log('Video.onVideoEndFullscreen()');
    if (onExitFullscreen) {
      onExitFullscreen();
    }
  }

  /**
   * get exit fullscreen event for firefox
   */
  function onVideoFullscreenChange(e) {
    //console.log('Video.onVideoEndFullscreen()');
    if (document.fullscreenElement) {
      // enter fullscreen
    } else if (onExitFullscreen) {
      onExitFullscreen();
    }
  }

  // Listeners

  function bindListeners() {
    //console.log('Video.bindListeners()');
    if (video && video.current) {
      // Unsupported native listeners
      video.current.addEventListener(
        'webkitendfullscreen',
        onVideoEndFullscreen,
      );
    }
    // Listeners on document
    document.addEventListener('fullscreenchange', onVideoFullscreenChange);
  }

  function unbindListeners() {
    //console.log('Video.unbindListeners()');
    if (video && video.current) {
      // Unsupported native listeners
      video.current.removeEventListener(
        'webkitendfullscreen',
        onVideoEndFullscreen,
      );
      // Listeners on document
      document.removeEventListener('fullscreenchange', onVideoFullscreenChange);
    }
  }

  // Optional params
  let controlsProp = controls ? {controls: 'controls'} : {};
  let autoPlayProp = autoplay ? {autoplay: 'autoplay'} : {};
  let mutedProp = muted ? {muted: 'muted'} : {};
  let repeatProp = repeat ? {loop: 'loop'} : {};
  let playsInlineProp = inline ? {playsInline: 'playsInline'} : {};

  // Build <video> element
  return (
    <video
      className="video"
      ref={video}
      src={source.uri || source}
      poster={poster}
      {...controlsProp}
      {...autoPlayProp}
      {...mutedProp}
      {...repeatProp}
      {...playsInlineProp}
      onLoadedMetadata={onVideoLoadedMetadata}
      onLoadStart={onVideoLoadStart}
      onWaiting={onVideoWaiting}
      onCanPlayThrough={onVideoCanPlayThrough}
      onPlay={onVideoPlay}
      onPause={onVideoPause}
      onRateChange={onVideoRateChange}
      onSeeked={onVideoSeeked}
      onTimeUpdate={onVideoTimeUpdate}
      onEnded={onVideoEnded}
      onError={onVideoError}
      style={{width: '100%', height: '100%'}}
    />
  );
});

export default Video;