#include <jni.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <vector>


extern "C" {

JNIEXPORT void JNICALL Java_com_app3_pano_PanoActivity_FindFastFeatures(JNIEnv*, jobject, jlong addrGray, jlong addrRgba)
{
	cv::Mat& mGr  = *(cv::Mat*)addrGray;
	cv::Mat& mRgb = *(cv::Mat*)addrRgba;
	std::vector<cv::KeyPoint> v;

	cv::FastFeatureDetector detector(50);
	detector.detect(mGr, v);
	for( unsigned int i = 0; i < v.size(); i++ )
	{
		const cv::KeyPoint& kp = v[i];
		cv::circle(mRgb, cv::Point(kp.pt.x, kp.pt.y), 10, cv::Scalar(255,0,0,255));
	}
}

JNIEXPORT void JNICALL Java_com_app3_pano_PanoActivity_FindHarrisCorners(JNIEnv*, jobject, jlong addrGray, jlong addrRgba)
{
	cv::Mat& mGr  = *(cv::Mat*)addrGray;
	cv::Mat& mRgb = *(cv::Mat*)addrRgba;
	cv::Mat dst_norm;
	cv::Mat dst = cv::Mat::zeros(mGr.size(),CV_32FC1);
	int blockSize = 2;
	int apertureSize = 3;
	double k = 0.04;
	float threshold=150;

	cv::cornerHarris( mGr, dst, blockSize, apertureSize, k, cv::BORDER_DEFAULT );
	cv::normalize( dst, dst_norm, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat() );

	for( unsigned int i = 0; i < dst_norm.rows; i++ )
	{
		float * row=dst_norm.ptr<float>(i);
		for(int j=0;j<dst_norm.cols;j++)
		{
			if(row[j]>=threshold)
			{
				cv::circle(mRgb, cv::Point(j, i), 10, cv::Scalar(255,0,0,255));
			}
		}
	}
}
JNIEXPORT void JNICALL Java_com_app3_pano_PanoActivity_FindORBFeatures(JNIEnv*, jobject, jlong addrRgba, jint featuresNumber)
{
	cv::Mat& mRgb = *(cv::Mat*)addrRgba;
	cv::Mat grayImg;
	std::vector<cv::KeyPoint> v;
	cv::cvtColor(mRgb,grayImg,cv::COLOR_RGBA2GRAY);
	cv::OrbFeatureDetector detector(featuresNumber);
	detector.detect(grayImg, v);
	cv::drawKeypoints(grayImg,v,mRgb,cv::Scalar::all(-1),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
}
JNIEXPORT void JNICALL Java_com_app3_pano_PanoActivity_FindMatches(JNIEnv*, jobject, jlong objectAddress, jlong sceneAddress,jint detectorID, jint descriptorID,jlong matchingResult)
{
	cv::Mat& object  = *(cv::Mat*)objectAddress;
	cv::Mat& scene = *(cv::Mat*)sceneAddress;
	cv::Mat& result = *(cv::Mat*)matchingResult;

	cv::Mat grayObject;
	cv::Mat grayScene;
	cv::cvtColor(object,grayObject,cv::COLOR_RGBA2GRAY);
	cv::cvtColor(scene,grayScene,cv::COLOR_RGBA2GRAY);

	std::vector<cv::KeyPoint> objectKeyPoints;
	std::vector<cv::KeyPoint> sceneKeyPoints;

	cv::Mat objectDescriptor;
	cv::Mat scenceDescriptor;

	if(detectorID==1)//FAST
	{
		cv::FastFeatureDetector detector(50);
		detector.detect(grayObject, objectKeyPoints);
		detector.detect(grayScene, sceneKeyPoints);
	}
	else if(detectorID==5)//ORB
	{
		cv::OrbFeatureDetector detector;
		detector.detect(grayObject, objectKeyPoints);
		detector.detect(grayScene, sceneKeyPoints);
	}

	if(descriptorID==3)//ORB
	{
		cv::OrbDescriptorExtractor descriptor;
		descriptor.compute(grayObject,objectKeyPoints,objectDescriptor);
		descriptor.compute(grayScene,sceneKeyPoints,scenceDescriptor);
	}
	else if(descriptorID==4)//BRIEF
	{
		cv::BriefDescriptorExtractor descriptor;
		descriptor.compute(grayObject,objectKeyPoints,objectDescriptor);
		descriptor.compute(grayScene,sceneKeyPoints,scenceDescriptor);
	}
	else if(descriptorID==5)//BRISK
	{
		cv::BRISK descriptor;
		descriptor.compute(grayObject,objectKeyPoints,objectDescriptor);
		descriptor.compute(grayScene,sceneKeyPoints,scenceDescriptor);
	}
	else if(descriptorID==6)//FREAK
	{
		cv::FREAK descriptor;
		descriptor.compute(grayObject,objectKeyPoints,objectDescriptor);
		descriptor.compute(grayScene,sceneKeyPoints,scenceDescriptor);
	}

	cv::BFMatcher matcher(cv::NORM_HAMMING);
	std::vector< cv::DMatch > matches;
	matcher.match( objectDescriptor, scenceDescriptor, matches);

	double min_dist = 100;
	for( int i = 0; i < objectDescriptor.rows; i++ )
	{
		double dist = matches[i].distance;
		if( dist < min_dist ) min_dist = dist;
	}
	std::vector< cv::DMatch > good_matches;
	for( int i = 0; i < objectDescriptor.rows; i++ )
	{
		if( matches[i].distance <= 3*min_dist )
		{
			good_matches.push_back( matches[i]);
		}
	}
	drawMatches( grayObject, objectKeyPoints, grayScene, sceneKeyPoints,good_matches, result, cv::Scalar::all(-1), cv::Scalar::all(-1),
			std::vector<char>(),
			cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS+cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

}
}
