// Copyright 2014 BVLC and contributors.

#include <algorithm>
#include <cmath>
#include <cfloat>
#include <vector>

#include "caffe/layer.hpp"
#include "caffe/vision_layers.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/io.hpp"

using std::max;

namespace caffe {

template <typename Dtype>
void DistanceAccuracyLayer<Dtype>::LayerSetUp(
  const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>* top) {
	LOG(INFO) << "bottom.size()=="<<bottom.size();
	LOG(INFO) << "top->size()=="<<top->size();
  CHECK_EQ(bottom.size(), 2) << "Distance Accuracy Layer takes three blobs as input.";
  CHECK_EQ(top->size(), 1) << "Distance Accuracy Layer takes 1 output.";
  CHECK_EQ(bottom[0]->num(), bottom[1]->num())
      << "The data and label should have the same number.";
  CHECK_EQ(bottom[1]->channels(), 1);
  CHECK_EQ(bottom[1]->height(), 1);
  CHECK_EQ(bottom[1]->width(), 1);
  (*top)[0]->Reshape(1, 2, 1, 1);
}

template <typename Dtype>
void DistanceAccuracyLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
      vector<Blob<Dtype>*>* top) {

}

template <typename Dtype>
void DistanceAccuracyLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    vector<Blob<Dtype>*>* top) {
	int count = bottom[0]->count();
	const Dtype* label = bottom[1]->cpu_data();
	const Dtype* dist_data = bottom[0]->cpu_data();
	int num = bottom[0]->num();
	Dtype accuracy = 0;
	Dtype mindist=dist_data[0];
	Dtype maxdist=dist_data[0];
	Dtype acculist[1000];
	Dtype loss=0;
	std::ofstream file;
	  file.open("./feature.txt");
	for (int i=0;i<num;i++){
		//LOG(INFO) << "label=" << label[i] << ", dist=" << dist_data[i];
		file << dist_data[i] << "\t" << label[i] << "\n";
		if (dist_data[i]<mindist){
			mindist=dist_data[i];
		}
		if (dist_data[i]>maxdist){
			maxdist=dist_data[i];
		}
		if (dist_data[i]<=0 && label[i]==1)
		{
			++accuracy;
		}
		if (dist_data[i]>0 && label[i]==-1)
		{
			++accuracy;
		}
		Dtype dloss=0;
		  if (dist_data[i]*label[i]>20)
		  {
			  dloss=dist_data[i]*label[i];
		  }else{
			  dloss=log(1+exp(dist_data[i]*label[i]));
		  }
		  loss=loss+dloss;
	}
	loss = loss / Dtype(6);
	/*for (int i=0;i<5;i++)
	{
		LOG(INFO) << "dist_data[" << i << "]=" << dist_data[i] << ", label[" << i << "]=" << label[i];
	}
	for (int i=300;i<305;i++)
	{
		LOG(INFO) << "dist_data[" << i << "]=" << dist_data[i] << ", label[" << i << "]=" << label[i];
	}*/
	//for (int i=0;i<20;i++) {
	//	LOG(INFO) << "label[" << i << "]=" << label[i] << ", dist_data[" << i << "]=" << dist_data[i];
	//}
	LOG(INFO) << "max dist = " << maxdist << ", min dist = " << mindist;
	Dtype maxrate=0;
	int maxidx;
	for (int j=0;j<1000;j++)
	{
		Dtype threshold = mindist + j*(maxdist-mindist) / 1000;
		accuracy = 0;
		for (int i=0;i<num;i++){
			if (dist_data[i]<=threshold && label[i]==1)
			{
				++accuracy;
			}
			if (dist_data[i]>threshold && label[i]==-1)
			{
				++accuracy;
			}
		}
		acculist[j]=accuracy/num;
		if (acculist[j]>maxrate)
		{
			maxrate=acculist[j];
			maxidx=j;
		}
	}
   LOG(INFO) << "test loss=" << loss << ", Correct:" << accuracy << ", Num:" << num << ", Accuracy: " << accuracy/num << ", maxrate:" << maxrate << ", maxidx:" << maxidx;
  (*top)[0]->mutable_cpu_data()[0] = maxrate;
}

INSTANTIATE_CLASS(DistanceAccuracyLayer);

}  // namespace caffe
