/** 
* @file ImageInfo.cpp
* @brief localization info + image filename + ?
* @author Erik Rodner
* @date 04/16/2008

*/
#include "core/image/ImageT.h"
#include "core/vector/VectorT.h"
#include "core/vector/MatrixT.h"

#include <iostream>

#include "vislearning/cbaselib/ImageInfo.h"

/* Qt */
#ifdef NICE_USELIB_QT4_XML

#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QString>
#include <QByteArray>
#include <QDomDocument>
#include <QDomNode>
#include <QDomElement>
#include <QPoint>
#include <QStringList>
#include <QVariant>

#endif //NICE_USELIB_QT4_XML

using namespace OBJREC;

using namespace std;
using namespace NICE;

ImageInfo::~ImageInfo()
{
    //if ( lr != NULL )
	//delete lr;
}

//! A member loading labeled image from formatted xml file.
/*!
 * \param[in] filename a std::string containing a path to the file
 * we need to load data from
 *
 * \see loadLegendFromNode(QDomElement *)
 * \see BBoxFromData(QString *aBBoxData, int *ID)
 * \see polyFromData(QString *aPolyData, int *labelID)
 */
bool
ImageInfo::loadImageInfo(const string &aFilename)
{
	#ifdef NICE_USELIB_QT4_XML

	QString filename(aFilename.data());
	QDomDocument doc("Image Labeler");
	QFile file(filename);
	if (!file.open(QIODevice::ReadOnly)) {
		cout << "loadImageInfo:Can not open such file\n";
		return false;
		/* NOTREACHED */
	}

	QString errMsg;
	if (!doc.setContent(&file, &errMsg)) {
		QByteArray array = errMsg.toAscii();
		cout << array.data();
		//showWarning(errMsg);
		file.close();
		return false;
		/* NOTREACHED */
	}

	file.close();

	/* getting all info */
	QDomElement elements = doc.documentElement();
	QDomNode rootNode = elements.firstChild();
	QString string_buffer;
	int width = -1;
	int height = -1;

//	cout << "\nlet the parsing begin!\n";
	while(!rootNode.isNull()) {
		QDomElement element = rootNode.toElement();
		if(!element.isNull()) {
			/* path to the image */
			if (element.tagName() == "image") {

                string_buffer = element.text();

                if (string_buffer.isEmpty())
                {
					cout << "loadImageInfo:The file with data"
							" doesn't contain path to the image\n";
					return false;
				}
                if( QFileInfo(string_buffer).isRelative() )
                {
                    QString asd = QFileInfo( QString(aFilename.c_str()) ).absoluteDir().absolutePath();

                    QString qwe = QFileInfo( asd + "/" + string_buffer ).absoluteFilePath();

                    string_buffer = qwe;
                }

                image_path_ = string_buffer.toStdString();
			}
			/* path to the segmented image */
			if (element.tagName() == "segmented") {
				string_buffer = element.text();
				if ( !string_buffer.isEmpty() ) {
					QByteArray array = string_buffer.toAscii();
					segmented_image_path_ = string(array.data());
				}
			}
			/* image description */
			else if (element.tagName() == "description") {
				string_buffer = element.text();
				if ( !string_buffer.isEmpty()) {
					QByteArray array = string_buffer.toAscii();
					image_description_ = string(array.data());
				}
			}
			/* tags */
			else if (element.tagName() == "tags") {
				string_buffer = element.text();
                if ( !string_buffer.isEmpty()) {
                    QByteArray array = string_buffer.toAscii();
                    //TODO: make parsing into the string list
                    tags_ = string(array.data());
                }
			}
			/* legend */
			else if (element.tagName() == "legend") {
				loadLegendFromElement(&element);
			}
			/* objects */
			else if (element.tagName() == "objects") {
				QDomNode subNode = element.firstChild();
				QDomElement subElement;

				while(!subNode.isNull()) {
					subElement = subNode.toElement();

					if (subElement.isNull() || subElement.text().isEmpty()) {
						subNode = subNode.nextSibling();
						continue;
					}

					string_buffer = subElement.attribute("id");
					bool ok = 1;
					int id = string_buffer.toInt(&ok, 10);

					if (!ok) {
						cout << "loadImageInfo: "
								"poly id format is corrupted\n";
						subNode = subNode.nextSibling();
						continue;
					}

                    // try reading a unique object/bounding box id, which identifies
                    // this object against all others (not a label)
                    string_buffer = subElement.attribute("uniqueObjectId");
                    ok = 1;
                    int uniqueObjectId = string_buffer.toInt(&ok, 10);
                    if(!ok)
                        uniqueObjectId = -1;


                    string_buffer = subElement.text();
					if (subElement.tagName() == "bbox") {
                        BoundingBox bbox;
                        bool bValid = BBoxFromData(&string_buffer, id, bbox);
                        if( bValid )
                        {
                            bbox.unique_id_ = uniqueObjectId;

                            bboxes_.push_back(bbox);
                        }
					}
					if (subElement.tagName() == "poly") {
                        Polygon poly;
                        bool bValid = polyFromData(&string_buffer, poly);
                        if(bValid)
                        {
                            poly.setID(id);
                            poly.unique_id_ = uniqueObjectId;

                            polys_.push_back(poly);
                        }

					}



					subNode = subNode.nextSibling();
				}
			}
			/* image size */
			else if (element.tagName() == "image_size") {
				string_buffer = element.text();
				if (string_buffer.isEmpty()) {
					cout << "loadImageInfo: "
							"image size format is corrupted\n";
					return false;
					/* NOTREACHED */
				}

				QString buffer;
				int size = string_buffer.size();
				bool ok = 0;
				for (int i = 0; i < size; i++) {
					/* ";" is a separator */
					if (';' != string_buffer.at(i))
						continue;

					buffer = string_buffer.mid(0, i);

					width = buffer.toInt(&ok, 10);
					if (!ok) {
						cout <<
							"loadImageInfo: "
							"image size format is corrupted\n";
						return false;
						/* NOTREACHED */
					}

					buffer = string_buffer.mid(i + 1, size - (i + 1));

					height = buffer.toInt(&ok, 10);

					if (!ok) {
						cout <<
							"loadImageInfo: "
							"image size format is corrupted";
						return false;
						/* NOTREACHED */
					}
					break;
				}
			}
			else if (element.tagName() == "pure_data") {
				string_buffer = element.text();
				labeled_image_ = imageTFromData(width, height, &string_buffer);
			}
		}
		rootNode = rootNode.nextSibling();
	}

	#endif //NICE_USELIB_QT4_XML

	return true;
}

#ifdef NICE_USELIB_QT4_XML

//! A member loading legend from xml node
/*!
 * \param[in] anElement a pointer to the object containing all the legend
 */
void
ImageInfo::loadLegendFromElement(QDomElement *anElement)
{
	if (!anElement) {
		return;
		/* NOTREACHED */
	}
	QDomNode subNode = anElement->firstChild();
	QDomElement subElement;

	while(!subNode.isNull()) {
		subElement = subNode.toElement();

		if (!subElement.isNull() && !subElement.text().isEmpty())
			loadCategoryInfo(&subElement);

		subNode = subNode.nextSibling();
	}
}

//! Loads one category info(label) from xml QDomElement
/*!
 * \param[in] anElement an object containing category info data
 */
bool
ImageInfo::loadCategoryInfo(QDomElement *anElement)
{
	QString string_buffer;
	int id = -1;
	bool isMain;
	uint color = 0xff000000;
	/* id attribute */
	string_buffer = anElement->attribute("id");
	bool ok = 0;
	id = string_buffer.toInt(&ok, 10);

	if (!ok) {
		cout <<
			"loadCategoryInfo: "
			"label id format is corrupted\n";
		return false;
		/* NOTREACHED */
	}

	/* isMain attribute */
	string_buffer = anElement->attribute("isMain");
	isMain = string_buffer.toInt(&ok, 2);

	if (!ok) {
		cout <<
			"loadCategoryInfo: "
			"label isMain flag format is corrupted\n";
		return false;
		/* NOTREACHED */
	}

	/* color attribute */
	string_buffer = anElement->attribute("color");
	color = string_buffer.toUInt(&ok, 16);

	if (!ok) {
		cout <<
			"loadCategoryInfo: "
			"label color format is corrupted\n";
		return false;
		/* NOTREACHED */
	}

	/* converting label name from QString to std::string*/
	string_buffer = anElement->text();
	QByteArray array = string_buffer.toAscii();
	std::string labelName(array.data());

	CategoryInfo label;
	label.setID(id);
	label.setCategoryName(labelName);
	label.setColor(color);
	labels_.push_back(label);

  return true;
}

//! A protected member parsing string data and returning a BoundingBox from it
/*!
 * format is x;y;w;h where w - width and h - height
 */
bool ImageInfo::BBoxFromData( QString *aBBoxData, int &id, BoundingBox &p_bbox )
{

    p_bbox.setID(id);

    QStringList coordsList = aBBoxData->split(";", QString::SkipEmptyParts);

    try
    {
        if( coordsList.size() == 4)
        {
            int x = QVariant(coordsList[0]).toInt();
            int y = QVariant(coordsList[1]).toInt();
            int w = QVariant(coordsList[2]).toInt();
            int h = QVariant(coordsList[3]).toInt();

            p_bbox.setTopLeft(x,y);
            p_bbox.setWidth( w );
            p_bbox.setHeight( h );
        }
        else
        {
            std::cout <<
                "BBoxFromData: "
                "bbox format is corrupted\n";

            return false;
        }
    }
    catch(std::exception &e)
    {
        std::cout << "BBoxFromData: exception:" << e.what() << std::endl;
        return false;
    }

    if ( !p_bbox.isValid() )
    {
        std::cout << "BBoxFromData not valid:"<< aBBoxData->toStdString() << std::endl;
        p_bbox.setTopLeft(0, 0);
        p_bbox.setBottomRight(0, 0);
        return false;
    }

    return true;
}

//! A protected member parsing string data and returning a Polygon from it
/*!
 * format is x0;y0;x1;y1;...
 */
bool ImageInfo::polyFromData( QString *aPolyData, Polygon &p_Poly)
{
    QStringList coordsList = aPolyData->split(";", QString::SkipEmptyParts);
    try
    {
        if( coordsList.size() % 2 == 0)
        {
            for( int i = 0; i < coordsList.size(); i += 2)
            {
                p_Poly.push( QVariant(coordsList[i]).toInt(),
                              QVariant(coordsList[i+1]).toInt() );
            }
        }
        else
        {
            std::cout << "polyFromData: not valid (coordinates not multiple of two)" << std::endl;
            return false;
        }
    }
    catch(std::exception &e)
    {
        std::cout << "polyFromData: exception:" << e.what() << std::endl;
        return false;
    }
    return true;
}

//!
ImageT< unsigned int >
ImageInfo::imageTFromData(
	const int &aWidth,
	const int &aHeight,
	QString *aPureData
)
{
	int startPos = 0;
	QString buffer = 0;
	ImageT< unsigned int > image(aWidth, aHeight);
	int y = 0;
	int x = 0;
	bool ok = 0;
	for (int i = 0; i < aPureData->size(); i++) {
		if ('\n' == aPureData->at(i)) {
			y++;
			x = 0;
			startPos = i + 1;
			continue;
		}
		/* ";" is a separator */
		if (';' != aPureData->at(i))
			continue;

		buffer = aPureData->mid(startPos, i - startPos);

		int pixel = buffer.toInt(&ok, 10);
		if (!ok) {
			cout <<
				"imageTFromData: "
				"pure data format is corrupted\n";
			image = 0;
			return image;
			/* NOTREACHED */
		}
		image.setPixel(x, y, pixel);
		x++;

		startPos = i + 1;
	}

	return image;
}

#endif //NICE_USELIB_QT4_XML

//! returns pointer to labels_ list
const std::list< CategoryInfo > *
ImageInfo::labels() const
{
	return &labels_;
}

//! returns pointer to bboxes_ list
const std::list< BoundingBox > *
ImageInfo::bboxes() const
{
	return &bboxes_;
}

//! returns pointer to polys_ list
const std::list< Polygon > *
ImageInfo::polys() const
{
	return &polys_;
}

//! returns ImageT object labeled_image_
ImageT< unsigned int >
ImageInfo::labeledImage() const
{
	return labeled_image_;
}

//! returns tags
std::string
ImageInfo::tags() const
{
	return tags_;
}

//! returns path to the original image
std::string
ImageInfo::imagePath() const
{
	return image_path_;
}

//! returns string with the image description
std::string
ImageInfo::imageDescription() const
{
	return image_description_;
}

//! returns path to the image segmented by ImageLabeler tool
std::string
ImageInfo::segmentedImagePath() const
{
	return segmented_image_path_;
}

/*
 * 
 */