package com.rnfloatingvideowidget;

import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.view.Display;
import android.widget.Toast;
import android.os.Handler;

import androidx.annotation.Nullable;

import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;

import com.devbrackets.android.exomedia.listener.OnVideoSizeChangedListener;
import com.devbrackets.android.exomedia.ui.widget.VideoView;
import com.devbrackets.android.exomedia.listener.OnErrorListener;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.rnfloatingvideowidget.R;

import java.util.Date;
import java.util.Map;
import java.util.Random;

import android.graphics.Point;

public class FloatingVideoWidgetShowService extends Service {



    private Handler timeoutHandler = new Handler();
    private static ReadableMap playingVideo = null; // The video currently playing
    private static ReadableArray videoPlaylist = null; // List of videos
    private static int index = 0; // Index of playing video in videoPlaylist
    private static ReadableMap initData = null;
    private GestureDetector gestureDetector;
    private int videoWidth = 250; // Default width of floating video player
    private int videoHeight = 180; // Default Height of floating video player

    WindowManager windowManager;
    View floatingWindow, floatingView, playerWrapper, overlayView;
    VideoView videoView;
    ImageButton increaseSize, decreaseSize, playVideo, pauseVideo;
    WindowManager.LayoutParams params;
    ReactContext reactContext = null;


    public FloatingVideoWidgetShowService() {}

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null && intent.getAction() != null) {
            switch (intent.getAction()) {

                case "ACTION_CLOSE_WIDGET": {
                    long seek = videoView.getCurrentPosition();
                    videoView.setKeepScreenOn(false);
                    stopSelf();
                    WritableMap args = new Arguments().createMap();
                    args.putInt("index", index);
                    args.putInt("seek", (int) seek);
                    args.putString("type", "close");
                    sendEvent(reactContext, "onClose", args);
                    onDestroy();
                    break;
                }
                case "ACTION_PLAY": {
                    onResume(floatingWindow);
                    break;
                }
                case "ACTION_PAUSE": {
                    onPause(floatingWindow);
                    break;
                }
                case "ACTION_PREV": {
                    onPrev(floatingWindow);
                    break;
                }
                case "ACTION_NEXT": {
                    onNext(floatingWindow);
                    break;
                }
                case "ACTION_SET_VIDEO": {
                    ReadableMap data = Arguments.fromBundle(intent.getBundleExtra("DATA"));
                    initData = data;
                    playingVideo = data.getMap("video");
                    videoPlaylist = data.getArray("videos");
                    index = data.getInt("index");
                    int Seek = data.getInt("seek");
                    Uri myUri = Uri.parse(playingVideo.getString("url"));
                    videoView.setVideoURI(myUri);
                    videoView.seekTo(Seek);
                    videoView.start();
                    videoView.setKeepScreenOn(true);

                    WritableMap args = Arguments.createMap();
                    args.putString("state", "isOpened");
                    args.putString("url", playingVideo.getString("url"));
                    sendEvent(reactContext, "onOpen", args);
                    break;
                }
            }
        }
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        final ReactInstanceManager reactInstanceManager = getReactNativeHost().getReactInstanceManager();
        ReactContext getReactContext = reactInstanceManager.getCurrentReactContext();
        gestureDetector = new GestureDetector(this, new SingleTapConfirm());
        reactContext = getReactContext;
        floatingWindow = LayoutInflater.from(this).inflate(R.layout.floating_widget_layout, null);

        // Define the layout flag according to android version.

        int LAYOUT_FLAG;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
        }
        // Setting layout params for floating video

        params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT, LAYOUT_FLAG,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);


        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

        assert windowManager != null;
        windowManager.addView(floatingWindow, params);

        // Define all the video view and its components

        floatingView = floatingWindow.findViewById(R.id.Layout_Expended);
        playerWrapper = floatingWindow.findViewById(R.id.view_wrapper);
        overlayView = floatingWindow.findViewById(R.id.overlay_view);
        videoView = (VideoView) floatingWindow.findViewById(R.id.videoView);
        increaseSize = (ImageButton) floatingWindow.findViewById(R.id.increase_size);
        decreaseSize = (ImageButton) floatingWindow.findViewById(R.id.decrease_size);
        playVideo = (ImageButton) floatingWindow.findViewById(R.id.app_video_play);
        pauseVideo = (ImageButton) floatingWindow.findViewById(R.id.app_video_pause);


        // Setting the on error Listener

        videoView.setOnErrorListener(new OnErrorListener() {
            @Override
            public boolean onError(Exception e) {
                long seek = videoView.getCurrentPosition();
                WritableMap args = new Arguments().createMap();
                args.putInt("index", index);
                args.putInt("seek", (int) seek);
                args.putString("url", playingVideo.getString("url"));
                args.putString("type", "error");

                sendEvent(reactContext, "onError", args);

                Toast.makeText(reactContext, "An Error occured, please try again", Toast.LENGTH_LONG).show();
                return false;
            }
        });

        // Changes the video size when new video is loaded

        videoView.setOnVideoSizedChangedListener(new OnVideoSizeChangedListener() {
            @Override
            public void onVideoSizeChanged(int intrinsicWidth, int intrinsicHeight, float pixelWidthHeightRatio) {
                final float scale = reactContext.getResources().getDisplayMetrics().density;

                videoWidth = intrinsicWidth;
                videoHeight = intrinsicHeight;

                RelativeLayout relativeLayout = (RelativeLayout) floatingWindow.findViewById(R.id.view_wrapper);
                double aspectRatio = (double) videoWidth / (double) videoHeight;

                if (videoHeight > videoWidth) {
                    int height = (int) (200 * scale + 0.5f);
                    double width = height * aspectRatio;

                    relativeLayout.getLayoutParams().width = (int) width;
                    relativeLayout.getLayoutParams().height = height;

                } else {
                    int width = (int) (250 * scale + 0.5f);
                    double height = width / aspectRatio;
                    relativeLayout.getLayoutParams().width = width;
                    relativeLayout.getLayoutParams().height = (int) height;

                }


            }
        });

        floatingWindow.findViewById(R.id.app_video_crop).setOnClickListener(new View.OnClickListener() {


            @Override
            public void onClick(View view) {
                long seek = videoView.getCurrentPosition();
                videoView.setKeepScreenOn(false);
                stopSelf();
                WritableMap args = new Arguments().createMap();
                args.putInt("index", index);
                args.putInt("seek", (int) seek);
                args.putString("url", playingVideo.getString("url"));
                args.putString("type", "close");
                sendEvent(reactContext, "onClose", args);
                onDestroy();
            }
        });



        floatingWindow.findViewById(R.id.Layout_Expended).setOnTouchListener(new View.OnTouchListener() {
            int X_Axis, Y_Axis;
            float TouchX, TouchY;

            @Override
            public boolean onTouch(View v, MotionEvent event) {


                if (gestureDetector.onTouchEvent(event)) {

                    if (overlayView.getVisibility() == View.VISIBLE) {

                        overlayView.setVisibility(View.GONE);
                        timeoutHandler.removeCallbacksAndMessages(null);

                    } else {
                        overlayView.setVisibility(View.VISIBLE);

                        timeoutHandler.postDelayed(new Runnable() {
                            public void run() {

                                overlayView.setVisibility(View.GONE);
                            }
                        }, 5000);
                    }

                } else {

                    int touches = event.getPointerCount();

                    if (touches > 1) {
                    }

                    switch (event.getAction()) {

                        case MotionEvent.ACTION_DOWN:

                            X_Axis = params.x;
                            Y_Axis = params.y;
                            TouchX = event.getRawX();
                            TouchY = event.getRawY();
                            return true;

                        case MotionEvent.ACTION_UP:

                            floatingView.setVisibility(View.VISIBLE);
                            return true;

                        case MotionEvent.ACTION_MOVE:

                            params.x = X_Axis + (int) (event.getRawX() - TouchX);
                            params.y = Y_Axis + (int) (event.getRawY() - TouchY);
                            windowManager.updateViewLayout(floatingWindow, params);
                            return true;
                    }

                }

                return false;

            }
        });
    }

    private class SingleTapConfirm extends SimpleOnGestureListener {

        @Override
        public boolean onSingleTapUp(MotionEvent event) {
            return true;
        }
    }


    private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
    }



    public void increaseWindowSize(View view) {
        final float scale = reactContext.getResources().getDisplayMetrics().density;
        RelativeLayout relativeLayout = (RelativeLayout) floatingWindow.findViewById(R.id.view_wrapper);
        Display display = windowManager.getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        int densityX = size.x; // default height width of screen

        double aspectRatio = (double) videoWidth / (double) videoHeight;

        if (videoHeight > videoWidth) {
            int height = (int) (400 * scale + 0.5f);
            double width = height * aspectRatio;

            relativeLayout.getLayoutParams().width = (int) width;
            relativeLayout.getLayoutParams().height = height;

        } else {
            int width = densityX;
            double height = width / aspectRatio;
            relativeLayout.getLayoutParams().width = densityX;
            relativeLayout.getLayoutParams().height = (int) height;

        }
        increaseSize.setVisibility(View.GONE);
        decreaseSize.setVisibility(View.VISIBLE);

    }

    public void returnToApp(View view) {
        long seek = videoView.getCurrentPosition();
        Intent intent = getPackageManager().getLaunchIntentForPackage(reactContext.getPackageName());
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
        videoView.setKeepScreenOn(false);
        stopSelf();
        WritableMap args = new Arguments().createMap();
        args.putInt("index", index);
        args.putInt("seek", (int) seek);
        args.putString("type", "close");
        args.putString("url", playingVideo.getString("url"));

        sendEvent(reactContext, "onClose", args);
        onDestroy();
    }

    public void decreaseWindowSize(View view) {
        final float scale = reactContext.getResources().getDisplayMetrics().density;
        RelativeLayout relativeLayout = (RelativeLayout) floatingWindow.findViewById(R.id.view_wrapper);

        double aspectRatio = (double) videoWidth / (double) videoHeight;

        if (videoHeight > videoWidth) {
            int height = (int) (200 * scale + 0.5f);
            double width = height * aspectRatio;

            relativeLayout.getLayoutParams().width = (int) width;
            relativeLayout.getLayoutParams().height = height;

        } else {
            int width = (int) (250 * scale + 0.5f);
            double height = width / aspectRatio;
            relativeLayout.getLayoutParams().width = width;
            relativeLayout.getLayoutParams().height = (int) height;

        }

        increaseSize.setVisibility(View.VISIBLE);
        decreaseSize.setVisibility(View.GONE);

    }

    public void onPause(View view) {
        long seek = videoView.getCurrentPosition();
        playVideo.setVisibility(ImageButton.VISIBLE);
        pauseVideo.setVisibility(ImageButton.GONE);
        videoView.pause();
        WritableMap args = Arguments.createMap();
        args.putInt("index", index);
        args.putInt("seek", (int) seek);
        args.putString("type", "paused");
        args.putString("url", playingVideo.getString("url"));
        sendEvent(reactContext, "onPause", args);

    }

    public void onResume(View view) {
        long seek = videoView.getCurrentPosition();
        playVideo.setVisibility(ImageButton.GONE);
        pauseVideo.setVisibility(ImageButton.VISIBLE);
        videoView.start();
        WritableMap args = Arguments.createMap();
        args.putInt("index", index);
        args.putInt("seek", (int) seek);
        args.putString("type", "resume");
        args.putString("url", playingVideo.getString("url"));
        sendEvent(reactContext, "onPlay", args);
    }

    public void onNext(View view) {
        int next = index + 1;
        if (index == videoPlaylist.size() - 1) {
            next = 0;
        }
        index = next;

        ReadableMap video = videoPlaylist.getMap(next);
        playingVideo = video;
        Uri myUri = Uri.parse(video.getString("url"));
        videoView.setVideoURI(myUri);
        videoView.seekTo(0);
        videoView.start();
        playVideo.setVisibility(ImageButton.GONE);
        pauseVideo.setVisibility(ImageButton.VISIBLE);
        WritableMap args = Arguments.createMap();
        args.putInt("index", index);

        args.putString("type", "next");
        args.putString("url", playingVideo.getString("url"));
        sendEvent(reactContext, "onNext", args);

    }

    public void onPrev(View view) {
        int next = index - 1;
        if (index == 0) {
            videoView.seekTo(0);
            return;
        }
        index = next;

        ReadableMap video = videoPlaylist.getMap(next);
        playingVideo = video;
        Uri myUri = Uri.parse(video.getString("url"));
        videoView.setVideoURI(myUri);
        videoView.seekTo(0);
        videoView.start();
        playVideo.setVisibility(ImageButton.GONE);
        pauseVideo.setVisibility(ImageButton.VISIBLE);
        WritableMap args = Arguments.createMap();
        args.putInt("index", index);

        args.putString("type", "prev");
        args.putString("url", playingVideo.getString("url"));
        sendEvent(reactContext, "onPrev", args);
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        if (floatingWindow != null)
            windowManager.removeView(floatingWindow);
    }

    protected ReactNativeHost getReactNativeHost() {
        return ((ReactApplication) getApplication()).getReactNativeHost();
    }

}