/*
 * Copyright (c) 2018 Nam Nguyen, nam@ene.im
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ml.docilealligator.infinityforreddit.videoautoplay;

import static ml.docilealligator.infinityforreddit.videoautoplay.ToroExo.with;
import static ml.docilealligator.infinityforreddit.videoautoplay.ToroUtil.checkNotNull;

import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.ui.PlayerView;

import ml.docilealligator.infinityforreddit.videoautoplay.annotations.RemoveIn;
import ml.docilealligator.infinityforreddit.videoautoplay.helper.ToroPlayerHelper;
import ml.docilealligator.infinityforreddit.videoautoplay.media.PlaybackInfo;
import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
import ml.docilealligator.infinityforreddit.videoautoplay.widget.Container;

/**
 * An implementation of {@link ToroPlayerHelper} where the actual Player is an {@link ExoPlayer}
 * implementation. This is a bridge between ExoPlayer's callback and ToroPlayerHelper behaviors.
 *
 * @author eneim (2018/01/24).
 * @since 3.4.0
 */

public class ExoPlayerViewHelper extends ToroPlayerHelper {

    @NonNull
    private final ExoPlayable playable;
    @UnstableApi
    @NonNull
    private final MyEventListeners listeners;
    private final boolean lazyPrepare;

    // Container is no longer required for constructing new instance.
    @SuppressWarnings("unused")
    @RemoveIn(version = "3.6.0")
    @Deprecated  //
    public ExoPlayerViewHelper(Container container, @NonNull ToroPlayer player, @NonNull Uri uri) {
        this(player, uri);
    }

    public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri) {
        this(player, uri, null);
    }

    @OptIn(markerClass = UnstableApi.class)
    public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri,
                               @Nullable String fileExt) {
        this(player, uri, fileExt, with(player.getPlayerView().getContext()).getDefaultCreator());
    }

    /**
     * Config instance should be kept as global instance.
     */
    @OptIn(markerClass = UnstableApi.class)
    public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri, @Nullable String fileExt,
                               @NonNull Config config) {
        this(player, uri, fileExt,
                with(player.getPlayerView().getContext()).getCreator(checkNotNull(config)));
    }

    public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri, @Nullable String fileExt,
                               @NonNull ExoCreator creator) {
        this(player, new ExoPlayable(creator, uri, fileExt));
    }

    @OptIn(markerClass = UnstableApi.class)
    public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull ExoPlayable playable) {
        super(player);
        //noinspection ConstantConditions
        if (player.getPlayerView() == null || !(player.getPlayerView() instanceof PlayerView)) {
            throw new IllegalArgumentException("Require non-null PlayerView");
        }

        listeners = new MyEventListeners();
        this.playable = playable;
        this.lazyPrepare = true;
    }

    @OptIn(markerClass = UnstableApi.class)
    @Override
    protected void initialize(@NonNull PlaybackInfo playbackInfo) {
        playable.setPlaybackInfo(playbackInfo);
        playable.addEventListener(listeners);
        playable.addErrorListener(super.getErrorListeners());
        playable.addOnVolumeChangeListener(super.getVolumeChangeListeners());
        playable.prepare(!lazyPrepare);
        playable.setPlayerView((PlayerView) player.getPlayerView());
    }

    @OptIn(markerClass = UnstableApi.class)
    @Override
    public void release() {
        super.release();
        playable.setPlayerView(null);
        playable.removeOnVolumeChangeListener(super.getVolumeChangeListeners());
        playable.removeErrorListener(super.getErrorListeners());
        playable.removeEventListener(listeners);
        playable.release();
    }

    @Override
    public void play() {
        playable.play();
    }

    @Override
    public void pause() {
        playable.pause();
    }

    @Override
    public boolean isPlaying() {
        return playable.isPlaying();
    }

    @Override
    public void setVolume(float volume) {
        playable.setVolume(volume);
    }

    @Override
    public float getVolume() {
        return playable.getVolume();
    }

    @Override
    public void setVolumeInfo(@NonNull VolumeInfo volumeInfo) {
        playable.setVolumeInfo(volumeInfo);
    }

    @Override
    @NonNull
    public VolumeInfo getVolumeInfo() {
        return playable.getVolumeInfo();
    }

    @NonNull
    @Override
    public PlaybackInfo getLatestPlaybackInfo() {
        return playable.getPlaybackInfo();
    }

    @Override
    public void setPlaybackInfo(@NonNull PlaybackInfo playbackInfo) {
        this.playable.setPlaybackInfo(playbackInfo);
    }

    @OptIn(markerClass = UnstableApi.class)
    public void addEventListener(@NonNull Playable.EventListener listener) {
        //noinspection ConstantConditions
        if (listener != null) this.listeners.add(listener);
    }

    @OptIn(markerClass = UnstableApi.class)
    public void removeEventListener(Playable.EventListener listener) {
        this.listeners.remove(listener);
    }

    public ExoPlayer getPlayer() {
        return playable.player.getPlayer();
    }

    // A proxy, to also hook into ToroPlayerHelper's state change event.
    @UnstableApi
    private class MyEventListeners extends Playable.EventListeners {

        MyEventListeners() {
        }

        @Override
        public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
            ExoPlayerViewHelper.super.onPlayerStateUpdated(playWhenReady, playbackState); // important
            super.onPlayerStateChanged(playWhenReady, playbackState);
        }

        @Override
        public void onRenderedFirstFrame() {
            super.onRenderedFirstFrame();
            internalListener.onFirstFrameRendered();
            for (ToroPlayer.EventListener listener : ExoPlayerViewHelper.super.getEventListeners()) {
                listener.onFirstFrameRendered();
            }
        }
    }
}