#include <jni.h>

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include "opencv2/calib3d.hpp"

#include <GLES2/gl2.h>

#include "common.hpp"
using namespace cv;

Mat homography;

extern "C" jbyteArray
Java_com_example_myapplication_MyGLSurfaceView_processFrame(JNIEnv *env, jobject /* this */,
                                                                      jint texIn, jint texOut,
                                                                      jint w, jint h,
                                                                      jboolean frontFacing) {
    static UMat m, flipped_m;

    //LOGD("Processing on CPU");
    int64_t t;
    m.create(h, w, CV_8UC4);

    // read
    t = getTimeMs();
    // expecting FBO to be bound, read pixels to mat
    glReadPixels(0, 0, m.cols, m.rows, GL_RGBA, GL_UNSIGNED_BYTE, m.getMat(ACCESS_WRITE).data);
    //LOGD("glReadPixels() costs %d ms", getTimeInterval(t));

    t = getTimeMs();
    // Check if we should flip image due to frontFacing
    // I don't think this should be required, but I can't find
    // a way to get the OpenCV Android SDK to do this properly
    // (also, time taken to flip image is negligible)
    if(frontFacing){
        flip(m, m, 1);
    }
    //LOGD("flip() costs %d ms", getTimeInterval(t));

    // modify
    t = getTimeMs();
    cvtColor(m, m, COLOR_BGRA2GRAY);
    int width = m.size().width;
    int height = m.size().height;

    blur( m, m, Size(3,3) );
    Canny( m, m, 40, 40*3, 3 );

    cvtColor(m, m, COLOR_GRAY2BGRA);
    //LOGD("Laplacian() costs %d ms", getTimeInterval(t));

    UMat rotate_m;
    m.copyTo(rotate_m);
    cv::rotate(m, rotate_m, ROTATE_90_COUNTERCLOCKWISE);
    resize(rotate_m,rotate_m, m.size(), INTER_LINEAR);

    // write back
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texOut);
    t = getTimeMs();
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rotate_m.cols, rotate_m.rows, GL_RGBA, GL_UNSIGNED_BYTE, rotate_m.getMat(ACCESS_READ).data);
    //LOGD("glTexSubImage2D() costs %d ms", getTimeInterval(t));


    Mat mat = rotate_m.getMat(ACCESS_READ);
    if(frontFacing) flip(mat, mat, 0);

    if(!homography.empty()) {
        // Perspective transform
        warpPerspective(mat, mat, homography, mat.size(), INTER_LINEAR, BORDER_CONSTANT);
    }

    auto& vec2 = mat.data;
    int matSize = mat.cols * mat.rows * mat.channels();
    jbyteArray jArr2 = env->NewByteArray(matSize);
    env->SetByteArrayRegion(jArr2, 0, matSize, reinterpret_cast<const jbyte *>(vec2));
    return jArr2;
}
