package co.pixelbeard.theanfieldwrap.videoPlayer;

import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.view.ContextThemeWrapper;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.mediarouter.app.MediaRouteButton;

import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.framework.CastContext;
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
import com.google.android.gms.common.images.WebImage;
import com.google.gson.Gson;

import org.json.JSONException;
import org.json.JSONObject;
import org.parceler.Parcels;

import butterknife.BindView;
import butterknife.ButterKnife;
import co.pixelbeard.theanfieldwrap.R;
import co.pixelbeard.theanfieldwrap.data.Video;
import co.pixelbeard.theanfieldwrap.data.source.DataRepository;
import co.pixelbeard.theanfieldwrap.data.source.local.LocalRepository;
import co.pixelbeard.theanfieldwrap.data.source.remote.RemoteRepository;
import co.pixelbeard.theanfieldwrap.utils.ActivityExtraTags;
import co.pixelbeard.theanfieldwrap.utils.AnalyticsConstants;
import co.pixelbeard.theanfieldwrap.utils.BaseActivity;
import co.pixelbeard.theanfieldwrap.utils.ConnectionUtils;
import co.pixelbeard.theanfieldwrap.utils.DateUtils;
import co.pixelbeard.theanfieldwrap.utils.TawController;
import io.realm.Realm;
import pl.droidsonroids.casty.Casty;
import pl.droidsonroids.casty.ExpandedControlsActivity;
import pl.droidsonroids.casty.MediaData;

public class VideoPlayerActivity extends BaseActivity implements VideoPlayerContract.View {

    private static final String TAG = VideoPlayerActivity.class.getSimpleName();

    @BindView(R.id.video_view)
    PlayerView playerView;

    @BindView(R.id.ll_video_player_toolbar)
    LinearLayout llVideoPlayerToolBar;

    @BindView(R.id.ll_back)
    LinearLayout llBack;

    RemoteMediaClient remoteMediaClient;

    private VideoPlayerContract.Presenter mPresenter;

    private Video mVideo;
    private SimpleExoPlayer player;
    private long playbackPosition;
    private int currentWindow;
    private boolean playWhenReady = true;
    private boolean isCasting = false;
    private Casty casty;

    private boolean serviceBound = false;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            serviceBound = true;
            VideoPlayerService.LocalBinder binder = (VideoPlayerService.LocalBinder) service;
            player = binder.getService().exoPlayer;
            if (!isCasting) {
                playerView.setPlayer(player);
                playerView.setControllerVisibilityListener(visibility -> {
                    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                        llVideoPlayerToolBar.setVisibility(visibility);
                    }
                });
            } else {
                player.setPlayWhenReady(false);
                unbindService(this);
                stopService(new Intent(VideoPlayerActivity.this, VideoPlayerService.class));
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            serviceBound = false;
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_player);

        ButterKnife.bind(this);
        mVideo = Parcels.unwrap(getIntent().getParcelableExtra(ActivityExtraTags.PARCEL_VIDEO));
        Log.d("CUSTOM", new Gson().toJson(mVideo));

        new VideoPlayerPresenter(this, new DataRepository(new LocalRepository(Realm.getDefaultInstance()), new RemoteRepository()));
        casty = Casty.create(this);

        setupCasty();
        setClickListeners();
        setUpMediaRouteButton();
    }

    @Override
    public void onStart() {
        super.onStart();
        if (Util.SDK_INT > 23) {
            Log.d("IS CASTING ON START", "" + isCasting);
            if (!isCasting && !casty.getPlayer().isPlaying() && !casty.getPlayer().isBuffering() && !casty.getPlayer().isPaused()) {
                playerView.setVisibility(View.VISIBLE);
                initializePlayer();
            } else {
                releasePlayer();
                finish();
            }
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d("IS CASTING ON RESUME", "" + isCasting);

        TawController.setCurrentPage(TAG);
        if ((Util.SDK_INT <= 23 || player == null)) {
            if (!isCasting && !casty.getPlayer().isPlaying() && !casty.getPlayer().isBuffering() && !casty.getPlayer().isPaused()) {
                playerView.setVisibility(View.VISIBLE);
                initializePlayer();
            } else {
                releasePlayer();
                finish();
            }
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mVideo.getType().equals("youtube")) {
            if (Util.SDK_INT <= 23) {
                releasePlayer();
            }
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mVideo.getType().equals("youtube")) {
            if (player != null) {
                mPresenter.recordAnalytic(AnalyticsConstants.STOPPED_VIDEO, mVideo.getPostId(), DateUtils.getTimeStampString((int) player.getCurrentPosition()));
            }

            if (Util.SDK_INT > 23) {
                releasePlayer();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        try {
            unbindService(serviceConnection);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }

    private void setClickListeners() {
        llBack.setOnClickListener(v -> onBackPressed());
    }

    private void setUpMediaRouteButton() {
        MediaRouteButton mediaRouteButton = findViewById(R.id.media_route_button);

        casty.setUpMediaRouteButton(mediaRouteButton);

        Context castContext = new ContextThemeWrapper(this, R.style.Theme_MediaRouter);

        Drawable drawable = null;
        TypedArray a = castContext.obtainStyledAttributes(null, R.styleable.MediaRouteButton, R.attr.mediaRouteButtonStyle, 0);
        drawable = a.getDrawable(R.styleable.MediaRouteButton_externalRouteEnabledDrawable);
        a.recycle();

        DrawableCompat.setTint(drawable, Color.WHITE);
        mediaRouteButton.setRemoteIndicatorDrawable(drawable);
    }

    private void setupCasty() {
        casty.setOnConnectChangeListener(new Casty.OnConnectChangeListener() {
            @Override
            public void onConnected() {
                Log.d("CHROMECAST", "ONCONNECTED");
                isCasting = casty.getPlayer().isPlaying() || casty.getPlayer().isPaused() || casty.getPlayer().isBuffering();

                MediaInfo mediaInfo;
                JSONObject jsonObject;
                String currentCastId = null;

                remoteMediaClient = CastContext.getSharedInstance(VideoPlayerActivity.this).getSessionManager().getCurrentCastSession().getRemoteMediaClient();

                if (remoteMediaClient != null) {
                    mediaInfo = remoteMediaClient.getMediaInfo();
                    if (mediaInfo != null) {
                        jsonObject = mediaInfo.getCustomData();
                        try {
                            currentCastId = jsonObject.getString("id");
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                }


                if (!isCasting) {
                    if (player != null) {
                        playbackPosition = player.getCurrentPosition();
                    } else {
                        playbackPosition = 0;
                    }

                    initializeChromeCast(playbackPosition);
                } else if (currentCastId != null && !currentCastId.equals(mVideo.getPostId())) {
                    playbackPosition = 0;
                    initializeChromeCast(playbackPosition);
                } else {
                    if (player != null) {
                        player.stop();
                    }
                    playerView.setVisibility(View.GONE);
                    Intent intent = new Intent(VideoPlayerActivity.this, ExpandedControlsActivity.class);
                    startActivity(intent);
                }
                isCasting = casty.getPlayer().isPlaying() || casty.getPlayer().isPaused() || casty.getPlayer().isBuffering();
            }

            @Override
            public void onDisconnected() {
                isCasting = false;
                finish();
            }
        });

        casty.setOnCastSessionUpdatedListener(castSession -> {
            isCasting = casty.getPlayer().isPlaying() || casty.getPlayer().isPaused() || casty.getPlayer().isBuffering();
            if (castSession != null) {
                remoteMediaClient = castSession.getRemoteMediaClient();
                if (remoteMediaClient != null) {
                    remoteMediaClient.addProgressListener((progressMs, durationMs) -> {
                        playbackPosition = progressMs;
                    }, 1000);
                }
            }
        });
    }

    private void initializePlayer() {


        if (mVideo.getType().equals("youtube")) {
            player = ExoPlayerFactory.newSimpleInstance(this);

            playerView.setPlayer(player);

            player.setPlayWhenReady(playWhenReady);
            player.seekTo(currentWindow, playbackPosition);

            Uri uri = Uri.parse(mVideo.getVideoUrl());
            MediaSource mediaSource = buildMediaSource(uri);
            player.prepare(mediaSource, true, false);

            playerView.setControllerVisibilityListener(visibility -> {
                if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    llVideoPlayerToolBar.setVisibility(visibility);
                }
            });
        } else {


            if (!serviceBound && !isCasting) {
                try {
                    Intent playerIntent = new Intent(this, VideoPlayerService.class);
                    playerIntent.putExtra("VIDEO", Parcels.wrap(mVideo));
                    ContextCompat.startForegroundService(this, playerIntent);
                    Log.d("BIND SERVICE: ", "" + System.currentTimeMillis());
                    bindService(playerIntent, serviceConnection, Context.BIND_IMPORTANT);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private MediaSource buildMediaSource(Uri uri) {
        return buildMediaSource(uri, uri.getLastPathSegment());
    }

    private MediaSource buildMediaSource(Uri uri, @Nullable String overrideExtension) {
        DefaultHttpDataSourceFactory dataSourceFactory = new DefaultHttpDataSourceFactory("The-Anfield-Wrap");
        @C.ContentType int type = Util.inferContentType(uri, overrideExtension);
        switch (type) {
            case C.TYPE_DASH:
                return new DashMediaSource.Factory(dataSourceFactory)
                        .createMediaSource(uri);
            case C.TYPE_SS:
                return new SsMediaSource.Factory(dataSourceFactory)
                        .createMediaSource(uri);
            case C.TYPE_HLS:
                return new HlsMediaSource.Factory(dataSourceFactory)
                        .createMediaSource(uri);
            case C.TYPE_OTHER:
                return new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(uri);
            default: {
                throw new IllegalStateException("Unsupported type: " + type);
            }
        }
    }


    private void initializeChromeCast(long playbackPosition) {
        MediaMetadata mediaMetadata = new MediaMetadata(MediaData.MEDIA_TYPE_MOVIE);
        mediaMetadata.putString(MediaMetadata.KEY_TITLE, mVideo.getTitle());
        mediaMetadata.putString(MediaMetadata.KEY_SUBTITLE, "The Anfield Wrap");
        mediaMetadata.addImage(new WebImage(Uri.parse(mVideo.getLargeImage())));

        JSONObject object = new JSONObject();
        try {
            object.put("id", mVideo.getPostId());
        } catch (JSONException e) {
            e.printStackTrace();
        }

        MediaInfo info = new MediaInfo.Builder(mVideo.getVideoUrl())
                .setStreamType(MediaData.STREAM_TYPE_BUFFERED)
                .setContentType("videos/mp4")
                .setStreamDuration(-1L)
                .setCustomData(object)
                .setMetadata(mediaMetadata)
                .build();

        casty.getPlayer().loadMediaAndPlay(info, true, playbackPosition);
        releasePlayer();
        try {
            if (serviceBound) {
                unbindService(serviceConnection);
                stopService(new Intent(this, VideoPlayerService.class));
            }

        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
            showSystemUi();
        } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            hideSystemUi();
        }
    }

    @SuppressLint("InlinedApi")
    private void hideSystemUi() {
        playerView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

        if (!playerView.isControllerVisible()) {
            llVideoPlayerToolBar.setVisibility(View.GONE);
        }
    }

    private void showSystemUi() {
        playerView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
        llVideoPlayerToolBar.setVisibility(View.VISIBLE);
    }

    private void releasePlayer() {
        if (player != null) {
            playbackPosition = player.getCurrentPosition();
            currentWindow = player.getCurrentWindowIndex();
            playWhenReady = player.getPlayWhenReady();
            player.release();
            player = null;
        }
    }

    @Override
    public void setPresenter(VideoPlayerContract.Presenter presenter) {
        if (presenter != null) {
            this.mPresenter = presenter;
        } else {
            throw new RuntimeException("Presenter is null");
        }
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();

        releasePlayer();
        try {
            unbindService(serviceConnection);
            stopService(new Intent(this, VideoPlayerService.class));

        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void goBack() {
        onBackPressed();
    }

    @Override
    public void showLoader(String text) {
        //Not needed
    }

    @Override
    public void hideLoader() {
        //Not needed
    }

    @Override
    public void onUnknownError(String error) {
        //Not needed
    }

    @Override
    public void onTimeout() {
        //Not needed
    }

    @Override
    public void onNetworkError() {
        //Not needed
    }

    @Override
    public void onConnectionError() {
        //Not needed
    }

    @Override
    public void logoutUser() {
        //Not needed
    }

    @Override
    public boolean checkConnection() {
        return ConnectionUtils.userHasConnection(this);
    }

}
