#include <FaceClassifier/miscellaneous.h>

#include <iostream>
#include <sstream>
#include <string>
#include <fstream>

//////////////////
// #include <stdio.h>
//////////////////

namespace misc {

void appendFile(const std::string filename, const std::string text) {
	std::ofstream file;
	file.open(filename.c_str(), std::ios_base::app);
	if (file.is_open()) {
		file << text;
	} else {
		std::cout << "Error! File " << filename.c_str() << "couldn't be opened.";
		exit(-1);
	}
	file.close();
}

double round(const double number) { // rounding: (neg. inf.) <- (zero) -> (pos. inf.)
	return (int)((number >= 0) ? (number + 0.5) : (number - 0.5));
}

double roundPrec(double number, const int precision) {
	number = number * pow(10.0, precision);
	number = round(number);
	number = number * pow(10.0, -precision);
	return number;
}

void mtxToString(const std::string name, const CvMat * const mtx) {
	int i, j;
	std::stringstream ss;
	std::cout << "\n[" << name << "]\n\n";
	for (j = 0; j < mtx->height; j++) {
		ss.str("");
		ss << j;
		std::cout << ss.str() << ": ";
		for (i = 0; i < mtx->width; i++) {
			printf("%0.5f ", cvGet2D(mtx, j, i).val[0]);
		}
		std::cout << "\n\n";
	}
}

void imgToString(const std::string name, const IplImage * const img) {
	int i, j;
	std::stringstream ss;
	std::cout << "\n[" << name << "]\n\n";
	for (j = 0; j < img->height; j++) {
		ss.str("");
		ss << j;
		std::cout << ss.str() << ": ";
		for (i = 0; i < img->width; i++) {
			printf("%3.0f ", cvGet2D(img, j, i).val[0]);
		}
		std::cout << "\n\n";
	}
}

void drawRect(IplImage * const img, const CvRect * const rect)
{
	cvRectangle(img, cvPoint(rect->x, rect->y), cvPoint(rect->x + rect->width, rect->y + rect->height),
				CV_RGB(255, 255, 255), 3, 8, 0);
}

IplImage *clipSubImg(const IplImage * const img, const CvRect * const rect)
{
    IplImage *subImg = cvCreateImage(cvSize(rect->width, rect->height), img->depth, img->nChannels);
	cvGetRectSubPix(img, subImg, cvPoint2D32f(rect->x + rect->width * 0.5, rect->y + rect->height * 0.5));
	return subImg;
}

IplImage *cpyImg(const IplImage * const img)
{
	IplImage *cpy = cvCreateImage(cvSize(img->width, img->height), img->depth, img->nChannels);
	cvCopy(img, cpy, 0); // dst's origin is zero (independently from src's origin)
	return cpy;
}

void setOriginTL(IplImage * const img)
{
	if (img->origin != IPL_ORIGIN_TL) {
		cvFlip(img, img, 0);
		img->origin = 0;
	}
}

FaceDetector::FaceDetector()
{
}

FaceDetector::FaceDetector(const std::string cascadeFile)
{
	Load(cascadeFile);
}

void FaceDetector::Load(const std::string cascadeFile)
{
	std::stringstream ss;
	ss.str("");
	ss << "Error! Cascadefile not found: " << cascadeFile.c_str() << ".\n";
	if (!face::utils::FileSystemUtils::exists(cascadeFile)) {
		std::runtime_error(ss.str());
		exit(-1);
	}
	this->cascadeFile = cascadeFile;
	storage = cvCreateMemStorage(0);
	cascade = (CvHaarClassifierCascade*)cvLoad(cascadeFile.c_str(), 0, 0, 0);
}

FaceDetector::~FaceDetector()
{
	cvReleaseHaarClassifierCascade(&cascade);
	cvReleaseMemStorage(&storage);
}


CvSeq *FaceDetector::DetectFaces(const IplImage * const img)
{
	CvSeq *seq;
	float scale = 1.3f;
	// preprocession for facedetection
	IplImage *cpy = cvCreateImage(cvSize(img->width, img->height), img->depth, img->nChannels);
	if (img->origin == IPL_ORIGIN_TL) {
		cvCopy(img, cpy, 0);
	} else {
		cvFlip(img, cpy, 0);
	}
	IplImage *grayImg = cvCreateImage(cvSize(img->width, img->height), 8, 1);
	IplImage *smallImg = cvCreateImage(cvSize(cvRound(img->width / scale), cvRound(img->height / scale)), 8, 1);
	if (img->nChannels == 1) {
		cvCopyImage(img, grayImg);
	}
	else {
		cvCvtColor(img, grayImg, CV_RGB2GRAY);
	}
	cvResize(grayImg, smallImg, CV_INTER_LINEAR);
	cvEqualizeHist(smallImg, smallImg);
	//
	seq = cvHaarDetectObjects(smallImg, cascade, storage,  1.2, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(30, 30));
	CvRect *r;
	// scale back the searched rectangles
	int i;
	for (i = 0; i < (seq ? seq->total : 0); i++) {
		r = (CvRect*)cvGetSeqElem(seq, i);
		r->height = cvRound(r->height * scale);
		r->width = cvRound(r->width  * scale);
		r->x = cvRound(r->x * scale);
		r->y = cvRound(r->y * scale);
	}
	//
	cvReleaseImage(&cpy);
	cvReleaseImage(&grayImg);
	cvReleaseImage(&smallImg);
	return seq;
}

void getLines(const std::string filename, std::list<std::string> * const list)
{
	std::string line;
	std::fstream file;
	file.open(filename.c_str(), std::ios::in);
	if (file.is_open()) {
		while (!file.eof()) {
			std::getline(file, line);
			if (line.compare("") == 0) continue; // ignore empty lines
			list->push_back(line);
		}
		file.close();
	} else {
		std::cout << "Error! Unable to open file: " << filename << ".\n\n";
		exit(-1);
	}
}

}