/*
 * grid.h
 *
 *  Created on: Jul 5, 2014
 *      Author: Daniel Wesierski
 */

#ifndef GRID_H_
#define GRID_H_

#include <cmath>
#include "configuration.h"

class Grid {
public:

	Grid()
	: x(0), y(0), stepPlaneX(0), stepPlaneY(0), imWidth(0), imHeight(0),
	  stepXNum(0), stepYNum(0), bboxes(0),
	  planeRes(0), scaleRes(0), planeSize(0) { };

	Grid(cv::Rect bbox, unsigned int imageWidth, unsigned int imageHeight, Config& conf)
	: x(bbox.x), y(bbox.y), stepPlaneX(0), stepPlaneY(0),
	  imWidth(imageWidth), imHeight(imageHeight), stepXNum(0), stepYNum(0),
	  planeRes(conf.gridResolution), scaleRes(conf.stepScales), scaleNum(conf.scaleNum), planeSize(conf.gridSz) {

		if (scaleNum > std::min(imWidth,imHeight)) {
			scaleNum = std::min(imWidth,imHeight);
			if (scaleNum%2==0) 
				scaleNum = std::abs(scaleNum)-1;
		}

		if (planeSize > std::min(imWidth,imHeight))
			planeSize = std::min(imWidth,imHeight);

		update_grid(bbox);
	}

	void update_grid(cv::Rect bbox) { // with check of boundaries

		stepPlaneX = std::max(1, int(planeRes*bbox.width));
		stepPlaneY = std::max(1, int(planeRes*bbox.height));
		int stepScaleX = std::max(1, int(scaleRes*bbox.width/2.0));
		int stepScaleY = std::max(1, int(scaleRes*bbox.height/2.0));

		// X-axis
		int scaleOffX = stepScaleX*std::min(1, int((scaleNum-1)/2));

		// Left side
		int stepXNumLeft = int(std::min(bbox.x - scaleOffX, int(planeSize*bbox.width/2.0 + scaleOffX))/stepPlaneX);
		x = bbox.x - stepXNumLeft*stepPlaneX;                        // init point of grid but still without scales
		int stepScaleXNumLeft = int(std::min(int(x), int((scaleNum-1)/2)*stepScaleX)/stepScaleX);

		// Right side
		int stepXNumRight = int(std::min(int(imWidth-bbox.width-bbox.x - scaleOffX), int(planeSize*bbox.width/2.0 + scaleOffX))/stepPlaneX);
		int xR = bbox.x + stepXNumRight*stepPlaneX;
		int stepScaleXNumRight = int(std::min(int(imWidth-bbox.width-xR), int((scaleNum-1)/2)*stepScaleX)/stepScaleX);

		// Y-axis
		int scaleOffY = stepScaleY*std::min(1, int((scaleNum-1)/2));

		// Up
		int stepYNumUp = int(std::min(bbox.y - scaleOffY, int(planeSize*bbox.height/2.0 + scaleOffY))/stepPlaneY);
		y = bbox.y - stepYNumUp*stepPlaneY;                        // init point of grid but still without scales
		int stepScaleYNumUp = int(std::min(int(y), int((scaleNum-1)/2)*stepScaleY)/stepScaleY);

		// Down
		int stepYNumDown = int(std::min(int(imHeight-bbox.height-bbox.y - scaleOffY), int(planeSize*bbox.height/2.0 + scaleOffY))/stepPlaneY);
		int yR = bbox.y + stepYNumDown*stepPlaneY;
		int stepScaleYNumDown = int(std::min(int(imHeight-bbox.height-yR), int((scaleNum-1)/2)*stepScaleY)/stepScaleY);

		stepXNum = stepXNumLeft + stepXNumRight + 1;
		stepYNum = stepYNumUp + stepYNumDown + 1;

		// "Scale-axis"
		int scalesNumUp = std::min(std::min(stepScaleXNumLeft,stepScaleXNumRight),std::min(stepScaleYNumUp,stepScaleYNumDown));
		int scalesNumDown = std::min(int((scaleNum-1)/2), std::min(int(bbox.width/2/stepScaleX),int(bbox.height/2/stepScaleY)));

		double scaleNumCurr = scalesNumUp + scalesNumDown + 1; 

		x = x - scalesNumUp*stepScaleX;
		y = y - scalesNumUp*stepScaleY;
		int boxMaxX = bbox.width + 2*scalesNumUp*stepScaleX;
		int boxMaxY = bbox.height + 2*scalesNumUp*stepScaleY;

		bboxes.clear();
		scaleF.clear();
		for (unsigned int i=0; i!=scaleNumCurr; ++i) {
			cv::Rect roi;
			roi.x = i*stepScaleX;
			roi.y = i*stepScaleY;
			roi.width = boxMaxX - 2*i*stepScaleX;
			roi.height = boxMaxY - 2*i*stepScaleY;
			bboxes.push_back(roi);
			scaleF.push_back(double(roi.width)/bbox.width);
		}
	}

	unsigned int x,y;
	int stepPlaneX, stepPlaneY;
	unsigned int stepXNum, stepYNum;
	std::vector<cv::Rect> bboxes;
	std::vector<double> scaleF;
	unsigned int scaleNum;
private:
	unsigned int imWidth, imHeight;
	double planeRes;
	double scaleRes;
	double planeSize;
};


#endif /* GRID_H_ */
