#include <vector>

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

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <opencv2/imgproc/imgproc.hpp>

#include <algorithm>
#include <functional>
#include <utility>

#include "caffe/util/io.hpp"

namespace caffe {

template <typename Dtype>
void EuclideanLossLayer<Dtype>::Reshape(
  const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>* top) {
  LossLayer<Dtype>::Reshape(bottom, top);
  CHECK_EQ(bottom[0]->channels(), bottom[1]->channels());
  CHECK_EQ(bottom[0]->height(), bottom[1]->height());
  CHECK_EQ(bottom[0]->width(), bottom[1]->width());
  diff_.Reshape(bottom[0]->num(), bottom[0]->channels(),
      bottom[0]->height(), bottom[0]->width());
}

template <typename Dtype>
void EuclideanLossLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    vector<Blob<Dtype>*>* top) {
  int count = bottom[0]->count();
  caffe_sub(
      count,
      bottom[0]->cpu_data(),
      bottom[1]->cpu_data(),
      diff_.mutable_cpu_data());
  Dtype dot = caffe_cpu_dot(count, diff_.cpu_data(), diff_.cpu_data());
  Dtype loss = dot / bottom[0]->num() / Dtype(2);
  (*top)[0]->mutable_cpu_data()[0] = loss;
  //LOG(INFO) << "euclidean_loss:" << loss;
  /*const Dtype* bottom_data1=bottom[0]->cpu_data();
  const Dtype* bottom_data2=bottom[1]->cpu_data();
      	  cv::Mat imgshow1(bottom[0]->height(), bottom[0]->width(), CV_8UC1);
      	cv::Mat imgshow2(bottom[0]->height(), bottom[0]->width(), CV_8UC1);
      	  int offset2=0;
      	  for (int c = 0; c < 1; ++c) {
      	    	for (int h = 0; h < imgshow1.rows; ++h) {
      	    		for (int w = 0; w < imgshow1.cols; ++w) {
      	    			imgshow1.at<uchar>(h, w) = MIN(255, MAX(0, 1*bottom_data1[offset2]));
      	    			imgshow2.at<uchar>(h, w) = MIN(255, MAX(0, 1*bottom_data2[offset2]));
      	    			offset2++;
      	    		}
      	    	}
      	    }
      	  cv::imshow("bottom1", imgshow1);
      	  cv::imshow("bottom2", imgshow2);
      	  cv::waitKey(10);*/

}

template <typename Dtype>
void EuclideanLossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, vector<Blob<Dtype>*>* bottom) {
  for (int i = 0; i < 2; ++i) {
    if (propagate_down[i]) {
      const Dtype sign = (i == 0) ? 1 : -1;
      const Dtype alpha = sign * top[0]->cpu_diff()[0] / (*bottom)[i]->num();
      caffe_cpu_axpby(
          (*bottom)[i]->count(),              // count
          alpha,                              // alpha
          diff_.cpu_data(),                   // a
          Dtype(0),                           // beta
          (*bottom)[i]->mutable_cpu_diff());  // b
    }
  }
}

#ifdef CPU_ONLY
STUB_GPU(EuclideanLossLayer);
#endif

INSTANTIATE_CLASS(EuclideanLossLayer);

}  // namespace caffe
