package com.videonext;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.view.View;
import android.content.Context;
import android.util.Log;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.LinearLayout;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.events.RCTEventEmitter;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MediaPlayer extends View {
    static {
        System.loadLibrary("VNMediaClient-jni");
    }

    private final int IDLE = 0;
    private final int PLAY_FROM_SERVER = 1;
    private final int PLAY_FROM_BUFFER = 2;
    private final int STOPPED = 3;
    private final int OPENING = 4;
    private final int BUFFERING = 5;
    private final int OPENED = 6;

    public enum States {
        IDLE("IDLE"),
        PLAY_FROM_SERVER("PLAY_FROM_SERVER"),
        PLAY_FROM_BUFFER("PLAY_FROM_BUFFER"),
		STOPPED("STOPPED"),
        OPENING("OPENING"),
        OPENED("OPENED"),
        BUFFERING("BUFFERING");

        private final String mName;

        States(final String name) {
            mName = name;
        }

        @Override
        public String toString() {
            return mName;
        }
    }

    public enum ScaleMode {FillStretch, FillProportional, OriginalSize}

    private long playerId;
    private String uri;
    private int[] imgBuf = null;
    private int outputWidth;
    private int outputHeight;
    private int imageWidth;
    private int imageHeight;
    private Lock imageMutex = new ReentrantLock(true);

    private ReactContext reactContext;
    public MediaPlayer(ReactContext ctx) {
        super(ctx);
        reactContext = ctx;
    }


    public void setSource(ReadableMap source) {
        uri      = source.getString("uri");
        outputWidth    = source.getInt("width");
        outputHeight   = source.getInt("height");

        Log.i("RNVnPlayer", "outputWidth: " + outputWidth + " outputHeight: " + outputHeight);

        this.layout(0, 0, outputWidth, outputHeight);

    }


    public void setFullscreen(boolean isenabled) {
        Log.i("RNVnPlayer", "fullscreen: " + isenabled);

        //this.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);

        //this.getSupportActionBar().hide();//if you need hidden actionbar also
        //this.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT));


        /* if (isenabled) {
             Activity host = (Activity) this.getContext();
             host.requestWindowFeature(Window.FEATURE_NO_TITLE);
             host.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
             host.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

         } else {
//             Log.i("RNVnPlayer", "width: " + outputWidth + " height: " + outputHeight);

  //           this.layout(0, 0, width, height);
         }

    */
    }

    public void play() {
        playerId = nativeOpen(uri);

        if (playerId == 0) {
            // Oops. something went wrong..
        }
    }

    public void stop() {
        if (playerId != 0) {
            nativeStop(playerId);
            playerId = 0;
        }
    }

    public void pause() {
        if (playerId != 0)
            nativePause(playerId);
    }

    public void jump(int timestamp) {
        if (playerId != 0)
            nativeJump(playerId, timestamp);
    }

    public int[] allocImageBuf(int width, int height) {
        this.imageWidth = width;
        this.imageHeight = height;

        imgBuf = new int[width * height];
        return imgBuf;
    }

    public void lockImageBuffer()
    {
        imageMutex.lock();
    }

    public void unlockImageBuffer()
    {
        imageMutex.unlock();
    }


    public void onStateChanged(int state, int resultCode, String resultString) {
        if(state == OPENED)
            nativePlay(playerId);

        WritableMap args = Arguments.createMap();
        args.putString("state", stateToString(state));

        if (state == STOPPED)
            args.putString("error", resultString != null ? resultString : "");

        this.sendEvent("onStateChanged", args);
    }

    public void onNewVideoFrame(/*int frame[],*/
                                 /*int width, int height,*/
                                 /*byte objsData[],*/
                                 int timeSec,
                                 int timeUSec) {
        this.postInvalidate();
        //Log.i("RNVnPlayer","onNewVideoFrame");
        WritableMap args = Arguments.createMap();
        args.putInt("width", imageWidth);
        args.putInt("height", imageHeight);
        long pts = (long)timeSec * 1000 + (timeUSec/1000);
        args.putString("pts", String.valueOf(pts));

        this.sendEvent("onNewFrame", args);
    }


    private Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {

        int width = bm.getWidth();
        int height = bm.getHeight();

        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        // create a matrix for the manipulation
        Matrix matrix = new Matrix();

        // resize the bit map
        matrix.postScale(scaleWidth, scaleHeight);

        // recreate the new Bitmap
        Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);

        return resizedBitmap;
    }


    public void	onDraw(Canvas canvas) {
        if (imgBuf == null)
            return;

        //Log.i("RNVnPlayer","onDraw imageWidth: " + imageWidth + " imageHeight: " + imageHeight
         //       + " outputWidth: " + outputWidth + " outputHeight: " + outputHeight);
        if (imageWidth == 0 || imageHeight == 0) {
            Log.i("RNVnPlayer", "invalid image size");
            return;
        }
        
        this.layout(0, 0, outputWidth, outputHeight);

        if (canvas.getWidth() == 0 || canvas.getHeight() == 0) {
            Log.i("RNVnPlayer", "invalid canvas size");
            return;
        }

        Bitmap origImg = Bitmap.createBitmap(imgBuf, 0, imageWidth, imageWidth, imageHeight, Bitmap.Config.RGB_565);

        if (imgBuf == null) {
            Log.i("RNVnPlayer", "NULLLLLLLLLLL");
            return;
        }

        //Log.i("RNVnPlayer","Canvas size: " + canvas.getWidth() + "x" + canvas.getHeight());
        //canvas.drawBitmap(origImg, null, new Rect(0, 0, outputWidth, outputHeight), null);


       // Bitmap newBitmap = resizeImage(origImg, canvas.getWidth(), canvas.getHeight(), ScaleMode.FillProportional);
       // canvas.drawBitmap(newBitmap, null, new Rect(0, 0, canvas.getWidth(), canvas.getHeight()), null);

        
        /// follow aspect ratio
        Matrix m = new Matrix();
        m.setRectToRect(new RectF(0, 0, origImg.getWidth(), origImg.getHeight()), new RectF(0, 0, canvas.getWidth() , canvas.getHeight()),
                Matrix.ScaleToFit.CENTER);
        Bitmap scaledBitmap = Bitmap.createBitmap(origImg, 0, 0, origImg.getWidth(), origImg.getHeight(), m, true);
        //canvas.drawBitmap(scaledBitmap, null, new Rect(0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight()), null);
        canvas.drawBitmap(scaledBitmap,
                (canvas.getWidth() - scaledBitmap.getWidth()) / 2,
                (canvas.getHeight() - scaledBitmap.getHeight()) / 2, null);

    }

    private Bitmap resizeImage(Bitmap bm, int maxWidth, int maxHeight) {
        return resizeImage(bm, maxWidth, maxHeight, ScaleMode.FillStretch);
    }

    public void paint(Canvas canvas, int headerHeight, int screenWidth, int screenHeight) {
        if (imgBuf == null)
            return;

        /*Log.i("RNVnPlayer","paint");
        Bitmap origImg = Bitmap.createBitmap(imgBuf, 0, width, width, height, Bitmap.Config.RGB_565);
        Bitmap newBitmap = resizeImage(origImg, canvas.getHeight() - headerHeight, canvas.getWidth(), ScaleMode.FillProportional);

        canvas.drawBitmap(newBitmap, (screenWidth - newBitmap.getWidth()) / 2, 0, null);
        */
    }

    private Bitmap resizeImage(Bitmap bm, int maxWidth, int maxHeight, ScaleMode mode) {
        int newImgHeight = bm.getHeight();
        int newImgWidth = bm.getWidth();

        switch (mode) {
            case FillStretch:
                newImgHeight = maxHeight;
                newImgWidth = maxWidth;
                break;
            case FillProportional:
                float ratio = Math.min(
                        (float) maxWidth / bm.getWidth(),
                        (float) maxHeight / bm.getHeight()
                );
                newImgWidth = Math.round(ratio * bm.getWidth());
                newImgHeight = Math.round(ratio * bm.getHeight());
                break;
            case OriginalSize:
                // use defaults of original image size => no resizing
            default:
                // use defaults of original image size => no resizing
        }
        return getResizedBitmap(bm, newImgHeight, newImgWidth);
    }


    private void removeAllChildViews(ViewGroup viewGroup) {
        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            View child = viewGroup.getChildAt(i);
            if (child instanceof ViewGroup) {
                if (child instanceof AdapterView) {
                    viewGroup.removeView(child);
                    return;
                }
                removeAllChildViews(((ViewGroup) child));
            } else {
                viewGroup.removeView(child);
            }
        }
    }


    private String stateToString(int state) {
        String state_str;
        switch (state)
        {
            case IDLE:
                state_str="IDLE";
                break;
            case PLAY_FROM_SERVER:
                state_str="PLAY_FROM_SERVER";
                break;
            case PLAY_FROM_BUFFER:
                state_str="PLAY_FROM_BUFFER";
                break;
            case STOPPED:
                state_str="STOPPED";
                break;
            case OPENING:
                state_str="OPENING";
                break;
            case OPENED:
                state_str="OPENED";
                break;
            case BUFFERING:
                state_str="BUFFERING";
                break;
            default:
                state_str="UNKNOWN";
        }

        return state_str;
    }

    private void sendEvent(String eventName,
                           @Nullable WritableMap params) {

        reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
            getId(),
            eventName,
            params
        );        
    }

    private native long nativeOpen(String url);
    private native void nativePlay(long playerId);
    private native void nativeStop(long playerId);
    private native void nativePause(long playerId);
    private native void nativeJump(long playerId, int timestamp);
    private native void nativeResume(long playerId, int direction);
}
