/*
 * main.cpp
 *
 *  Created on: 27.4.2011
 *      Author: Pavel Svoboda
 */
#include "read_dataset.hpp"

using namespace ppd;

Object::Object():
		file_name(""),
		flag(-1)
{}

Object::Object(const std::string& file_name):
file_name(file_name),
flag(1)
{}

Object::Object(const std::string& file_name, const int flag):
		file_name(file_name),
		flag(flag)

{}

std::string Object::get_file_name() const
{
	return this->file_name;
}

void Object::set_file_name(const std::string& file_name)
{
	this->file_name = std::string(file_name);
}

int Object::get_flag(void) const
{
	return this->flag;
}

void Object::set_flag(const int flag)
{
	this->flag = flag;
}

BoundedObject::BoundedObject(const std::string& file_name, const int flag, const cv::Rect_<int>& bounding_box):
		Object(file_name, flag),
		bounding_box(bounding_box)
{}

void BoundedObject::set_bounding_box(const cv::Rect_<int>& bounding_box)
{
	this->bounding_box = cv::Rect_<int>(bounding_box);
}

cv::Rect_<int> BoundedObject::get_bounding_box() const
{
	return this->bounding_box;
}

cv::Mat DataSetReader::get_image(const unsigned int i) const
{
	if(i < static_cast<unsigned int>(this->dataset_indexes[this->flag].size()))
	{
		cv::Mat img = cv::imread(this->path_image + this->objects[this->dataset_indexes[this->flag][i]]->get_file_name(),-1);
//		if(img.data == NULL)
//		{
//			#pragma omp critical(std_err)
//			{
//				std::cerr << "Can not read data: " << this->path_image + this->image_list[i].get_file_name() << std::endl;
//			}
//			return cv::Mat();
//		}
		return img;
	}
	else
	{
		throw new Exception(std::string("Indexing of DataSetReader object out of possible interval!"));
	}
}

const Object* DataSetReader::get(const unsigned int i) const
{
	if( i < this->dataset_indexes[this->flag].size())
		return this->objects[this->dataset_indexes[this->flag][i]];
	return new Object();
}

int DataSetReader::get_image_flag(const unsigned int i) const
{
	if(i <= static_cast<unsigned int>(this->image_list.size()))
	{
		return this->image_list[i].get_flag();
	}
	else
	{
		throw new Exception(std::string("Indexing of DataSetReader object out of possible interval!"));
	}
}

DataSetReader::DataSetReader( void ):
		read(true),
		flag(DataSetReader::ALL_OBJECTS)
{}

void DataSetReader::set_class(const std::string &object_class)
{
	this->object_class = object_class;
}

void DataSetReader::set_paths(const std::string& data_list_file_name, const std::string &path_sets, const std::string &path_annotations, const std::string &path_image, const std::string &path_save_image)
{
	this->data_list_file_name = data_list_file_name;
	this->path_sets = path_sets;
	this->path_annotations = path_annotations;
	this->path_image = path_image;
}

void DataSetReader::set_properties(const SettingsReader& settingsReader)
{
	this->object_class = settingsReader.get_class();
	this->path_sets = settingsReader.get_path_img_sets();
	this->data_list_file_name = settingsReader.get_data_list_file();
	this->path_annotations = settingsReader.get_path_ann();
	this->path_image = settingsReader.get_path_img();
}

//Reads data file to know, which images contain the wanted class
void DataSetReader::load_data_list(const std::string file_name)
{
		std::string name;
//Flag if the image contains Potted Plant, -1/1
		int is_object_class = 0;
		std::ifstream file(file_name.c_str(), std::ios::in);
		if (!file.is_open())
			throw new Exception(std::string("Missing file list : " + file_name));

		while (file.good())
		{
			file >> name >> is_object_class;
//			is_object_class = 1 - sufficient visibility | 0 - inadequate visibility | -1 - not present in the image
//			ToDo changed because bad dataset
//			if (is_object_class >= 1)
//			{
				this->data_list.push_back(Object(name, is_object_class));
//			}
		}
		this->nImages = static_cast<unsigned int>(this->data_list.size());
}

//Parse xml annotation file - extracts bounding boxes
void DataSetReader::read_annotation_file(const std::string &file_name, std::string& image_name, std::vector<cv::Rect_<int> > &bounding_boxes)
{
	image_name.clear();
	std::string object_class_string, object_bounding_box_string, coord_string;
	try
	{
		ticpp::Document an_file(file_name);
		an_file.LoadFile();
		//Todo Potential unhandled exception

		ticpp::Node *image_name_node =(an_file.FirstChildElement())->FirstChild("filename");
		std::string tmp;
		(image_name_node->ToElement())->GetText(&tmp);
		image_name = tmp;

		ticpp::Iterator<ticpp::Element> object_node("object");
		for (object_node = object_node.begin(an_file.FirstChildElement()); object_node != object_node.end(); object_node++)
		{
			object_node->FirstChildElement()->GetText(&object_class_string);
			if (object_class_string.compare(this->object_class) == 0)
			{
				ticpp::Iterator<ticpp::Node> pp_node;
				for (pp_node = pp_node.begin(object_node.Get()); pp_node != pp_node.end(); pp_node++)
				{
					pp_node->GetValue(&object_bounding_box_string);
					if (object_bounding_box_string.compare("bndbox") == 0)
					{
						int min_x, min_y, max_x, max_y;
						ticpp::Iterator<ticpp::Element> bb_element;
						for(bb_element = bb_element.begin(pp_node.Get()); bb_element != bb_element.end(); bb_element++)
						{
							bb_element->GetValue(&coord_string);
							if(coord_string.compare("xmin") == 0)
							{
								bb_element->GetText(&min_x);
								min_x = (min_x < 0) ? 0 : min_x;
							}
							else if(coord_string.compare("ymin") == 0)
							{
								bb_element->GetText(&min_y);
								min_y = (min_y < 0) ? 0 : min_y;
							}
							else if(coord_string.compare("xmax") == 0)
								bb_element->GetText(&max_x);
							else if(coord_string.compare("ymax") == 0)
								bb_element->GetText(&max_y);
						}
						bounding_boxes.push_back( cv::Rect_<int>(min_x, min_y, max_x - min_x, max_y - min_y));
//						std::cout << "min X: " << min_x << " max X: " << max_x << " | min Y: " << min_y << " max Y: " << max_y << std::endl;
					}
				}
			}
		}
	} catch (ticpp::Exception &e)
	{
		std::cerr << e.what() << std::endl;
	}
}

void DataSetReader::initialize()
{
	try
	{
		int index = 0;
		std::vector<int> bg_objects;
		std::vector<int> fg_objects;
		std::vector<int> all_objects;
		std::string image_name = "";

		this->load_data_list(this->path_sets.append(this->data_list_file_name));

		for(unsigned int i = 0; i < static_cast<unsigned int>(this->data_list.size()); i++)
		{
			//Read the annotation file
			this->read_annotation_file( this->path_annotations + data_list[i].get_file_name() + std::string(".xml"), image_name, this->bounding_boxes);
			if(image_name.empty())
			{
				image_name = this->data_list[i].get_file_name()+".jpg";
			}
			//Save all image-info-objects from annotation file
			this->image_list.push_back(Object(image_name, this->data_list[i].get_flag()));

			//ToDo taking care about 0 objects (occlusion etc)
			if(this->data_list[i].get_flag() >= 0)
			{
				if(this->bounding_boxes.size() == 0)
				{
					this->objects.push_back(new Object(image_name, this->data_list[i].get_flag() ));
					all_objects.push_back(index);
					index++;
				}
				else
				{
					for(unsigned int j = 0; j < static_cast<unsigned int>(this->bounding_boxes.size()); j++)
					{
						this->objects.push_back(new BoundedObject(image_name, this->data_list[i].get_flag() ,this->bounding_boxes[j]));
						fg_objects.push_back(index);
						all_objects.push_back(index);
						index++;
					}
				}
			}
			else
			{
				this->objects.push_back(new Object(image_name, this->data_list[i].get_flag() ));
				bg_objects.push_back(index);
				all_objects.push_back(index);
				index++;
			}
			this->bounding_boxes.clear();
		}

		this->dataset_indexes.push_back(bg_objects);
		this->dataset_indexes.push_back(fg_objects);
		this->dataset_indexes.push_back(all_objects);
	}
	catch(Exception *e)
	{
		std::cout << e->get() << std::endl;
		delete e;
		this->read = false;
		throw new Exception("Reading of dataset properties failed.");
	}

}

const unsigned int DataSetReader::get_number_of_front_objects() const
{
	return static_cast<unsigned int>(this->dataset_indexes[this->FG_OBJECTS].size());
}

cv::Mat DataSetReader::operator [](const unsigned int i) const
{
	cv::Mat tmp;

	if(i <= static_cast<unsigned int>(this->dataset_indexes[this->flag].size()))
	{
		cv::Mat img = cv::imread(this->path_image + this->objects[this->dataset_indexes[this->flag][i]]->get_file_name(), -1);
//		if(img.data == NULL)
//		{
//			#pragma omp critical(std_err)
//			{
//				std::cerr << "Can not read data: " << this->path_image + this->objects[this->dataset_indexes[this->flag][i]]->get_file_name() << std::endl;
//			}
//			return cv::Mat();
//		}
//		if(this->objects[this->dataset_indexes[this->flag][i]]->get_flag() == -1)
//			tmp = img;
//		else if(this->objects[this->dataset_indexes[this->flag][i]]->get_flag() == 0 || this->objects[this->dataset_indexes[this->flag][i]]->get_flag() == 1)
//		{
//			cv::Rect_<int> tmp_bb = static_cast<BoundedObject*>(this->objects[this->dataset_indexes[this->flag][i]])->get_bounding_box();
//			//Check the coordinates if its in the allowed range
//			if ((tmp_bb.width + tmp_bb.x) > img.cols)
//				tmp_bb.width = img.cols - tmp_bb.x;
//			if ((tmp_bb.height + tmp_bb.y) > img.rows)
//				tmp_bb.height = img.rows - tmp_bb.y;
//			tmp = cv::Mat(img, tmp_bb);
//		}
		tmp = img;
	}
	else
	{
		throw new Exception(std::string("Indexing of DataSetReader object out of possible interval!"));
	}

	return tmp;
}

std::string DataSetReader::get_file_name(const unsigned int i) const
{
	if(i < static_cast<unsigned int>(this->dataset_indexes[this->flag].size()))
	{
		return this->objects[this->dataset_indexes[this->flag][i]]->get_file_name();
	}
	else
		return std::string();
}

unsigned int DataSetReader::get_number_of_bg_objects() const
{
	return static_cast<unsigned int>(this->dataset_indexes[this->BG_OBJECTS].size());
}

unsigned int DataSetReader::get_number_of_objects(void) const
{
	return static_cast<unsigned int>(this->dataset_indexes[this->flag].size());
}

int DataSetReader::get_class(const unsigned int i) const
{
	int tmp = 0;

	if(i <= static_cast<unsigned int>(this->dataset_indexes[this->flag].size()))
	{
		tmp = this->objects[this->dataset_indexes[this->flag][i]]->get_flag();
	}
	return tmp;
}

void DataSetReader::work_fg()
{
	this->flag = this->FG_OBJECTS;
}

void DataSetReader::work_bg()
{
	this->flag = this->BG_OBJECTS;
}

void DataSetReader::work_all()
{
	this->flag = this->ALL_OBJECTS;
}

DataSetReader::~DataSetReader(void)
{
	for(unsigned int i = 0; i < static_cast<unsigned int>(this->objects.size()); i++ )
	{
		delete this->objects[i];
	}

}
