#include <algorithm>
#include <vector>

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

#include "caffe/filler.hpp"

namespace caffe {

template <typename Dtype>
void GSV2LossLayer<Dtype>::LayerSetUp(
  const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>* top) {
  LossLayer<Dtype>::LayerSetUp(bottom, top);
  static Dtype V1, V2, S;
  static int phase = 0;
  if ( phase == 0 ) {
      do {
          Dtype U1 = (Dtype)rand() / RAND_MAX;
          Dtype U2 = (Dtype)rand() / RAND_MAX;

          V1 = 2 * U1 - 1;
          V2 = 2 * U2 - 1;
          S = V1 * V1 + V2 * V2;
      } while(S >= 1 || S == 0);

      wangB = V1 * sqrt(-2 * log(S) / S);
  } else
      wangB = V2 * sqrt(-2 * log(S) / S);

  phase = 1 - phase;
	//wangB = wangB * wangV + wangE;
	wangB = -1.9;


	LOG(INFO)<<"loss layer inited";

	loss_threshold_ = (this->layer_param_.generalized_similarity_loss_param().margin());
	int dim = bottom[0]->count() / bottom[0]->num();

}

template <typename Dtype>
void GSV2LossLayer<Dtype>::Reshape(
  const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>* top) {
	LossLayer<Dtype>::Reshape(bottom, top);
	difference1_.Reshape(bottom[0]->num(), bottom[0]->channels(), bottom[0]->height(), bottom[0]->width());
	difference2_.Reshape(bottom[0]->num(), bottom[0]->channels(), bottom[0]->height(), bottom[0]->width());
		s_.Reshape(bottom[0]->num(),1,1,1);
		loss_per_triplet=new Dtype[bottom[0]->num()];
}

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

	int count = bottom[0]->count();
	int num = bottom[0]->num();
	int size = count / num;
	//其实size就是dim
	/*const std::vector<std::vector<int> >& triplets_id =
			Caffe::mutable_triplets_id();

	const std::vector<std::vector<std::string> >& triplets =
			Caffe::mutable_triplets();
	const std::map<std::string, int>& name2id = Caffe::mutable_name2id();
	const std::vector<double>& labels=Caffe::mutable_labels();*/
	Blob<Dtype> intermediate_result(1, size, 1, 1);
	Blob<Dtype> intermediate_result2(1,size,1,1);


	Blob<Dtype> intermediate_x1_400(1,((int) size/2),1,1);
	Blob<Dtype> intermediate_x1_800(1,((int) size/2),1,1);
	Blob<Dtype> intermediate_x2_400(1,((int) size/2),1,1);
	Blob<Dtype> intermediate_x2_800(1,((int) size/2),1,1);
	Blob<Dtype> intermediate_res(1,((int) size/2),1,1);




	Blob<Dtype> diff_bottom(bottom[0]->num(), bottom[0]->channels(),
			bottom[0]->height(), bottom[0]->width());
	//LOG(INFO)<<"here";
	//normalize the output


	/*if (to_norm_ == true) {
		for (std::map<std::string, int>::const_iterator iter = name2id.begin();
				iter != name2id.end(); iter++) {
			int size = (*bottom)[0]->channels() * (*bottom)[0]->height()
					* (*bottom)[0]->width();
			Dtype* addr = (*bottom)[0]->mutable_cpu_data()
					+ iter->second * size;
			Dtype s = caffe_cpu_dot(size, addr, addr);
			s_.mutable_cpu_data()[iter->second] = s;
			//printf("/ns=%f",s);
			if (s != 0)
				for (int i = 0; i < size; i++)
					addr[i] = addr[i] / sqrt(s);
		}
	}*/



	// 计算loss
	Dtype total_loss = 0;
	int pos_triplets = 0;
	for (int triplet_i = 0; triplet_i < num; triplet_i++) {

		memset(intermediate_result.mutable_cpu_data(), 0, sizeof(Dtype) * size);

		memset(intermediate_x1_400.mutable_cpu_data(),0,sizeof(Dtype) * ((int) size/2));
		memset(intermediate_x1_800.mutable_cpu_data(),0,sizeof(Dtype) * ((int) size/2));
		memset(intermediate_x2_400.mutable_cpu_data(),0,sizeof(Dtype) * ((int) size/2));
		memset(intermediate_x2_800.mutable_cpu_data(),0,sizeof(Dtype) * ((int) size/2));
		memset(intermediate_res.mutable_cpu_data(),0,sizeof(Dtype) * ((int) size/2));


		//const int o1_index = triplets_id[triplet_i][0];
		//const int o2_index = triplets_id[triplet_i][1];
		//const int o3_index = triplets_id[triplet_i][2];


		const Dtype* x1_addr = bottom[0]->cpu_data() + bottom[0]->offset(triplet_i, 0);
		const Dtype* x1_addr_mid = bottom[0]->cpu_data() + bottom[0]->offset(triplet_i, (int)size/2);

		const Dtype* x2_addr = bottom[1]->cpu_data() + bottom[1]->offset(triplet_i, 0);
		const Dtype* x2_addr_mid = bottom[1]->cpu_data() + bottom[1]->offset(triplet_i, (int)size/2);

		Dtype* val2;
		val2 = new Dtype [((int) size/2)];
		for (int ii=0;ii < ((int) size/2);ii++)
		{
			val2[ii] = 0;
		}
		caffe_sub(((int) size/2),x1_addr,val2,intermediate_x1_400.mutable_cpu_data());
		caffe_sub(((int) size/2),x1_addr_mid,val2,intermediate_x1_800.mutable_cpu_data());
		caffe_sub(((int) size/2),x2_addr,val2,intermediate_x2_400.mutable_cpu_data());
		caffe_sub(((int) size/2),x2_addr_mid,val2,intermediate_x2_800.mutable_cpu_data());
		//caffe_sub(((int) size/2),x3_addr,val2,intermediate_x3_400.mutable_cpu_data());
		//caffe_sub(((int) size/2),x3_addr_mid,val2,intermediate_x3_800.mutable_cpu_data());
		delete[] val2;


         Dtype vall = 0;
		 caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans, 1, 1, ((int) size/2),
                                        (Dtype) (0.5), intermediate_x1_400.mutable_cpu_data(),
                                        intermediate_x1_400.mutable_cpu_data(), (Dtype) 0.,
                                        &vall);
         caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans, 1, 1, ((int) size/2),
         (Dtype) (0.5), intermediate_x2_400.mutable_cpu_data(),
         intermediate_x2_400.mutable_cpu_data(), (Dtype) 1., &vall);

		 caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans, 1, 1, ((int) size/2),
			 (Dtype) (-1.), intermediate_x1_800.mutable_cpu_data(),
			 intermediate_x2_800.mutable_cpu_data(), (Dtype) 1., &vall);

		 //caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans, 1, 1, ((int) size/2),
			 //(Dtype) (1), intermediate_x1_800.mutable_cpu_data(),
			 //intermediate_x3_800.mutable_cpu_data(), (Dtype) 1., &vall);
		 //@@@@
        vall = vall + x1_addr[size - 1] + x2_addr[size - 1] + wangB;
        Dtype wanglr=0.0000;
        wangB = wangB - wanglr * (2*bottom[2]->cpu_data()[triplet_i]-1);

		//loss_per_triplet[triplet_i] = loss_per_triplet[triplet_i] + vall;


		loss_per_triplet[triplet_i] = vall * (2*bottom[2]->cpu_data()[triplet_i]-1);

		total_loss = total_loss + loss_per_triplet[triplet_i];//std::max(1-loss_per_triplet[triplet_i],Dtype(0.0));

		if (loss_per_triplet[triplet_i] > 0)
			pos_triplets++;

	}
	(*top)[0]->mutable_cpu_data()[0] = total_loss;// /num;
	  /*const Dtype * bottom_cpu_data = bottom[0]->cpu_data();
	    LOG(INFO) << this->layer_param_.name() << " " << "bottom:";
	  for (int i=0;i<10;i++) {
	    LOG(INFO) << bottom_cpu_data[i];
	  }*/
}

template <typename Dtype>
void GSV2LossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, vector<Blob<Dtype>*>* bottom) {
  Dtype loss_threshold_ = this->layer_param_.generalized_similarity_loss_param().margin();
  int count = (*bottom)[0]->count();
	memset(difference1_.mutable_cpu_data(), 0, sizeof(Dtype) * count);
	memset(difference2_.mutable_cpu_data(), 0, sizeof(Dtype) * count);
		int size = (*bottom)[0]->channels() * (*bottom)[0]->height()
				* (*bottom)[0]->width();
			int num = (*bottom)[0]->num();
//		LOG(INFO)<<"here size is:"<<size;
			Dtype* myarray;
							myarray = new Dtype [size];
		for (int triplet_i = 0; triplet_i < num; triplet_i++) {
			if (loss_per_triplet[triplet_i] < -1.0) {
				//@@@@@@@@@@WANG
				continue;
			}

			double loss=loss_per_triplet[triplet_i];

			//tanhPartial=a;
			//const int o1_index = triplets_id[triplet_i][0];
			//const int o2_index = triplets_id[triplet_i][1];
			//const int o3_index = triplets_id[triplet_i][2];
			const Dtype* x1_addr = (*bottom)[0]->cpu_data() + (*bottom)[0]->offset(triplet_i, 0);
          //@@@
			const Dtype* x1_addr_mid = (*bottom)[0]->cpu_data() + (*bottom)[0]->offset(triplet_i, (int)size/2);
			const Dtype* x2_addr = (*bottom)[1]->cpu_data() + (*bottom)[1]->offset(triplet_i, 0);
			//@@@
			const Dtype* x2_addr_mid = (*bottom)[1]->cpu_data() + (*bottom)[1]->offset(triplet_i, (int)size/2);
			//const Dtype* x3_addr = (*bottom)[0]->cpu_data() + o3_index * size;
			//@@@
			//const Dtype* x3_addr_mid = (*bottom)[0]->cpu_data() + o3_index * size + ((int) size/2);
			//@@@

				for (int ii =0;ii<size;ii++)
				{
						myarray[ii] = 0;
				}
				Dtype* addr1 = myarray;
				Dtype* addr2 = myarray + ((int) size/2);

			caffe_sub(((int) size/2),x1_addr, addr1, addr1);
			caffe_sub(((int) size/2), addr2, x2_addr_mid, addr2);
			myarray[size - 1] = 1;
			Dtype mylabel = 2*(*bottom)[2]->cpu_data()[triplet_i]-1;
			caffe_cpu_axpby(size, mylabel, myarray, Dtype(1), difference1_.mutable_cpu_data()+difference1_.offset(triplet_i));
			for (int ii =0;ii<size;ii++)
			{
								myarray[ii] = 0;
			}
			caffe_sub(((int) size/2),x2_addr,  addr1, addr1);
			caffe_sub(((int) size/2), addr2, x1_addr_mid, addr2);
			myarray[size - 1] = 1;
			caffe_cpu_axpby(size, mylabel, myarray, Dtype(1), difference2_.mutable_cpu_data()+difference2_.offset(triplet_i));
			//@@@

		}
		caffe_cpu_axpby(count, Dtype(1) / num,
				difference1_.cpu_data(), Dtype(0),
				(*bottom)[0]->mutable_cpu_diff());
		caffe_cpu_axpby(count, Dtype(1) / num,
						difference2_.cpu_data(), Dtype(0),
						(*bottom)[1]->mutable_cpu_diff());
//	caffe_axpby(size, Dtype(0.13), (*bottom)[0]->mutable_cpu_data()+maxIm*size,
//	Dtype(1), (*bottom)[0]->mutable_cpu_diff()+maxIm*size);
//	memcpy(intermediate_result.mutable_cpu_data(),
//			(*bottom)[0]->mutable_cpu_diff(), size * sizeof(Dtype));
//	caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, 1, size, size, (Dtype) 1.,
//			intermediate_result.mutable_cpu_data(), metric_matrix.cpu_data(),
//			(Dtype) 0., (*bottom)[0]->mutable_cpu_diff());
		/*Dtype* bottom_cpu_diff1 = (*bottom)[0]->mutable_cpu_diff();
		for (int i=0;i<count;i++)
		{
			bottom_cpu_diff1[i]=1;
		}
		Dtype* bottom_cpu_diff2 = (*bottom)[1]->mutable_cpu_diff();
		for (int i=0;i<count;i++)
		{
			bottom_cpu_diff2[i]=1;
		}
		  const Dtype* bottom_cpu_diff11 = (*bottom)[0]->cpu_diff();
		  LOG(INFO) << this->layer_param_.name() << " " << "bottom diff:";
		  for (int i=0;i<10;i++)
		  {
		  	LOG(INFO) << bottom_cpu_diff11[i];
		  }*/
  delete[] myarray;


}

#ifdef CPU_ONLY
STUB_GPU(GSV2LossLayer);
#endif

INSTANTIATE_CLASS(GSV2LossLayer);

}  // namespace caffe
