/** 
* @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();

    m_iImageWidth = -1;
    m_iImageHeight = -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") {
                if( !this->extractSectionImage( &element, aFilename) )
                    return false;
            }
            // path to the segmented image
            if (element.tagName() == "segmented") {
                if( !this->extractSectionSegmented(&element) )
                    return false;
            }
            // image description
            else if (element.tagName() == "description") {
                if( !this->extractSectionDescription(&element) )
                    return false;
            }
            // tags
            else if (element.tagName() == "tags") {
                if( !this->extractSectionTags(&element) )
                    return false;
            }
            // legend
            else if (element.tagName() == "legend") {
                extractSectionLegend(&element);
            }
            // objects
            else if (element.tagName() == "objects") {
                if( !this->extractSectionObjects(&element) )
                    return false;
            }
            // image size
            else if (element.tagName() == "image_size") {
                if( !this->extractImageSize(&element) )
                    return false;
            }
            else if (element.tagName() == "pure_data") {
                if( !this->extractSectionPureData(&element) )
                    return false;
            }
        }
        rootNode = rootNode.nextSibling();
    }

    #endif //NICE_USELIB_QT4_XML

    return true;
}

#ifdef NICE_USELIB_QT4_XML

bool ImageInfo::extractSectionImage(QDomElement *element , const std::string &p_sImageInfoFilename)
{
    QString 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(p_sImageInfoFilename.c_str()) ).absoluteDir().absolutePath();

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

        string_buffer = qwe;
    }

    image_path_ = string_buffer.toStdString();
    return true;
}
bool ImageInfo::extractSectionSegmented(QDomElement *element )
{
    QString string_buffer = element->text();
    if ( !string_buffer.isEmpty() ) {
        QByteArray array = string_buffer.toAscii();
        segmented_image_path_ = string(array.data());
    }
    return true;
}
bool ImageInfo::extractSectionDescription(QDomElement *element )
{
    QString string_buffer = element->text();
    if ( !string_buffer.isEmpty()) {
        QByteArray array = string_buffer.toAscii();
        image_description_ = string(array.data());
    }
    return true;
}
bool ImageInfo::extractSectionTags(QDomElement *element )
{
    QString string_buffer = element->text();
    if ( !string_buffer.isEmpty())
    {
        QByteArray array = string_buffer.toAscii();
        //TODO: make parsing into the string list
        tags_ = string(array.data());
    }
    return true;
}

bool ImageInfo::extractSectionObjects(QDomElement *element )
{
    QDomNode subNode = element->firstChild();
    QDomElement subElement;

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

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

        if (subElement.tagName() == "bbox") {
            if ( !this->extractObjectRectangle(&subElement) )
                return false;
        }
        if (subElement.tagName() == "poly") {
           if ( !this->extractObjectPolygon(&subElement) )
               return false;
        }

        subNode = subNode.nextSibling();
    }
    return true;
}
bool ImageInfo::extractObjectPolygon(QDomElement *element )
{
    QString string_buffer = element->attribute("id");
    bool ok = 1;
    int id = string_buffer.toInt(&ok, 10);

    if (!ok) {
        cout << "loadImageInfo: "
                "poly id format is corrupted\n";
        return false;
    }

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

    string_buffer = element->text();
    Polygon poly;
    bool bValid = polyFromData(&string_buffer, poly);
    if( !bValid )
        return false;

    poly.setID(id);
    poly.unique_id_ = uniqueObjectId;

    polys_.push_back(poly);

    return true;
}
bool ImageInfo::extractObjectRectangle(QDomElement *element )
{
    QString string_buffer = element->attribute("id");
    bool ok = 1;
    int id = string_buffer.toInt(&ok, 10);

    if (!ok)
    {
        cout << "loadImageInfo: "
                "poly id format is corrupted\n";
        return false;
    }

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

    string_buffer = element->text();
    BoundingBox bbox;
    bool bValid = BBoxFromData(&string_buffer, id, bbox);
    if( !bValid )
        return false;

    bbox.unique_id_ = uniqueObjectId;
    bboxes_.push_back(bbox);

    return true;
}

bool ImageInfo::extractImageSize(QDomElement *element )
{
    QString string_buffer = element->text();
    if (string_buffer.isEmpty()) {
        cout << "loadImageInfo: "
                "image size format is corrupted\n";
        return false;
    }

    QStringList coordsList = string_buffer.split(";", QString::SkipEmptyParts);

    try
    {
        if( coordsList.size() == 2)
        {
            bool ok = false;
            this->m_iImageWidth = QVariant(coordsList[0]).toInt( &ok );
            if( !ok ) return false;
            this->m_iImageHeight = QVariant(coordsList[1]).toInt( &ok );
            if( !ok ) return false;

            return true;
        }
    } catch(std::exception &e)
    {
        std::cout << "exception: image size format is corrupted" << e.what() << std::endl;
        return false;
    }

    return false;
}
bool ImageInfo::extractSectionPureData(QDomElement *element )
{
    if( m_iImageHeight < 0 || m_iImageWidth < 0)
        return false;

    QString string_buffer = element->text();
    labeled_image_ = imageTFromData(m_iImageWidth, m_iImageHeight, &string_buffer);

    return true;
}


//! A member loading legend from xml node
/*!
 * \param[in] anElement a pointer to the object containing all the legend
 */
void
ImageInfo::extractSectionLegend(QDomElement *anElement)
{
	if (!anElement) {
		return;
	}
	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_;
}

/*
 * 
 */
