#include <vector>

#include "caffe/filler.hpp"
#include "caffe/layer.hpp"
#include "caffe/vision_layers.hpp"

namespace caffe {

template <typename Dtype>
void DummyDataLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      vector<Blob<Dtype>*>* top) {
  const int num_top = top->size();
  const DummyDataParameter& param = this->layer_param_.dummy_data_param();
  const int num_data_filler = param.data_filler_size();
  CHECK(num_data_filler == 0 || num_data_filler == 1 ||
        num_data_filler == num_top)
      << "Number of data fillers must be 0, 1 or equal to the number of tops: "
      << num_top << "; you specified " << num_data_filler << " data fillers.";
  CHECK(param.num_size() == 1 || param.num_size() == num_top)
      << "Must specify either a single (1) 'num' or one for each top blob "
      << "(" << num_top << "); you specified " << param.num_size() << ".";
  CHECK(param.channels_size() == 1 || param.channels_size() == num_top)
      << "Must specify either a single (1) 'channels' or one for each top blob "
      << "(" << num_top << "); you specified " << param.channels_size() << ".";
  CHECK(param.height_size() == 1 || param.height_size() == num_top)
      << "Must specify either a single (1) 'height' or one for each top blob "
      << "(" << num_top << "); you specified " << param.height_size() << ".";
  CHECK(param.width_size() == 1 || param.width_size() == num_top)
      << "Must specify either a single (1) 'width' or one for each top blob "
      << "(" << num_top << "); you specified " << param.width_size() << ".";
  // refill_[i] tells Forward i whether or not to actually refill top Blob i.
  // If refill_[i] is false, Forward does nothing for Blob i. We use this to
  // avoid wastefully refilling "constant" Blobs in every forward pass.
  // We first fill refill_ in with the INVERSE of its final values.
  // The first time we run Forward from the LayerSetUp method, we'll fill only
  // Blobs for which refill_ is normally false.  These Blobs will never be
  // filled again.
  refill_.clear();
  fillers_.clear();
  if (num_data_filler <= 1) {
    FillerParameter filler_param;
    if (num_data_filler == 0) {
      filler_param.set_type("constant");
      filler_param.set_value(0);
    } else {
      filler_param.CopyFrom(param.data_filler(0));
    }
    // Refill on each iteration iff not using a constant filler,
    // but use the inverse of this rule for the first run.
    refill_.resize(1);
    refill_[0] = (strcmp(filler_param.type().c_str(), "constant") == 0);
    fillers_.resize(1);
    fillers_[0].reset(GetFiller<Dtype>(filler_param));
  } else {
    refill_.resize(num_top);
    fillers_.resize(num_top);
    for (int i = 0; i < num_top; ++i) {
      fillers_[i].reset(GetFiller<Dtype>(param.data_filler(i)));
      // Refill on each iteration iff not using a constant filler,
      // but use the inverse of this rule for the first run.
      refill_[i] =
          (strcmp(param.data_filler(i).type().c_str(), "constant") == 0);
    }
  }
  for (int i = 0; i < num_top; ++i) {
    const int num = (param.num_size() == 1) ? param.num(0) : param.num(i);
    const int channels =
        (param.channels_size() == 1) ? param.channels(0) : param.channels(i);
    const int height =
        (param.height_size() == 1) ? param.height(0) : param.height(i);
    const int width =
        (param.width_size() == 1) ? param.width(0) : param.width(i);
    (*top)[i]->Reshape(num, channels, height, width);
  }
  // Run Forward once, with refill_ inverted, to fill the constant Blobs.
  this->Forward(bottom, top);
  // Invert the inverted refill_ values to refill the desired (non-constant)
  // Blobs in every usual forward pass.
  for (int i = 0; i < refill_.size(); ++i) {
    refill_[i] = !refill_[i];
  }
}

template <typename Dtype>
void DummyDataLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
      vector<Blob<Dtype>*>* top) {
  for (int i = 0; i < top->size(); ++i) {
    const int filler_id = (fillers_.size() > 1) ? i : 0;
    if (refill_[filler_id]) {
      fillers_[filler_id]->Fill((*top)[i]);
    }
  }
}

INSTANTIATE_CLASS(DummyDataLayer);

}  // namespace caffe
