/*
    Copyright (C) 2012 Bartosz Broda, Jacek Bochenek, Łukasz Biłeńkij, 
    Damian Jaworski, Wojciech Mazur, Dominik Piasecki, Maciej Piasecki, 
    Radosław Ramocki
    
    This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.

    This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. 

    See the LICENSE and COPYING files for more details.
*/

#include "clutotreescene.h"
#include "qgraphicsclusteritem.h"
#include <iostream>
#include <cmath>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
#include <QMessageBox>
#include <QGraphicsItem>
#include <QGraphicsSceneEvent>

using namespace std;

ClutoTreeScene::ClutoTreeScene(Ui::MainWindow *ui, QObject *parent) : QGraphicsScene(parent)
{
    this->ui = ui;
    labels = NULL;
    highlighted = NULL;
    skippedLevels = 0;
    this->setItemIndexMethod(QGraphicsScene::NoIndex);
}

void ClutoTreeScene::PushRoot(Node *root, bool clearStack)
{
    if (clearStack)
	while (!roots.empty())
	    roots.pop();
    this->roots.push(root);
    skippedLevels = root->GetLevel();
    highlighted = NULL;
}

void ClutoTreeScene::PopRoot()
{
    if (roots.size() < 2)
    {
	QMessageBox::critical(NULL, "Error", "You are on the top level");
	return;
    }
    this->roots.pop();
    skippedLevels = this->roots.top()->GetLevel();
    highlighted = NULL;

    emit UpdateSimNeeded(this->roots.top());
    emit RedrawingNeeded();
    emit CenteringNeeded(restorePos.x(), restorePos.y());
}

void ClutoTreeScene::SetMaxLevel(size_t maxlevel)
{
    this->maxlevel = maxlevel;
}

void ClutoTreeScene::SetLabels(std::vector<std::string> *labels)
{
    highlighted = NULL;
    this->labels = labels;
}

void ClutoTreeScene::DrawTree()
{
    highlighted = NULL;
    this->clear();

    bubble = new BubbleRect();
    this->addItem(bubble);

    size_t currentMaxLevel = maxlevel - skippedLevels;
    size_t vlength = ui->spinBox_3->value();

    if (roots.top()->GetLevelsAhead() < 7)
	vlength /= (7 - roots.top()->GetLevelsAhead());
    this->setSceneRect(0, 0, currentMaxLevel * 100, 4 * vlength + 50);

    //traverse tree
    if (this->roots.top()->GetID() == -1)
	DrawCluster(2 * vlength, vlength + 0.75 * vlength, this->roots.top());
    else
	DrawCluster(2 * vlength, vlength, this->roots.top());
}

void ClutoTreeScene::DrawCluster(size_t yposition, size_t vlength, Node *node)
{
//    if (node->GetLevel() >= 2)
//    	return;
    size_t hlength = ui->spinBox_2->value();
    size_t xposition = hlength * (node->GetLevel() - skippedLevels);

    if (vlength < (size_t)ui->spinBox->value()) //don't display this node
    {
	QGraphicsTextItem * text = new QGraphicsTextItem(QString::fromStdString("..."));
	text->setPos(xposition, yposition - 13);
	this->addItem(text);
	return;
    }

    //count color according to min/max similarity
    double alfa = (node->GetSimilarity() - minSim) / (maxSim - minSim);
    std::cout << "Sim: " << node->GetSimilarity() << " ALFA: " << alfa << std::endl; //check if it is between 0 and 1

    QColor color = QColor::fromRgb((1-alfa) * 255, 0, 0);
    QGraphicsClusterItem * item;

    //Create graphics item to display
    if (labels != NULL && !node->HasChildren() && ui->checkBox->isChecked())
	item = new QGraphicsClusterItem(xposition, yposition, hlength, vlength, node, color, labels->at(node->GetID()));
    else
	item = new QGraphicsClusterItem(xposition, yposition, hlength, vlength, node, color);

    this->addItem(item);

    if (node->HasChildren())
    {
	size_t topPos = yposition - vlength; //position of first (top) child
	double offset = 2 * vlength / double(node->GetChildrenNo() - 1); //space beetwen children
	size_t childVlength = (1.0/node->GetChildrenNo()) * vlength;

	//Draw all children (recursive)
	for (size_t i = 0; i < node->GetChildrenNo(); ++i)
	    DrawCluster(topPos + round(i * offset), childVlength, node->GetChild(i));
    }
}

void ClutoTreeScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (roots.empty())
	return;

    Unhighlight();
    QList<QGraphicsItem *> list = this->items(event->scenePos());
    if (list.size() > 0)
    {

	QGraphicsClusterItem * elem = dynamic_cast<QGraphicsClusterItem *>(list[0]);
	if (elem == NULL || elem->GetNode()->GetID() == -1)
	    return;

	//Set appropriate text on the bubble rect
	if (elem->GetNode()->HasChildren())
	{
	    this->bubble->SetText("<b>Levels ahead:</b> " + boost::lexical_cast<std::string>(elem->GetNode()->GetLevelsAhead()) \
				  + "<br><b>Sim:</b> " + boost::lexical_cast<std::string>(elem->GetNode()->GetSimilarity()));
	}
	else
	{
	    if (labels == NULL || labels->empty())
		return;
	    std::string label = labels->at(elem->GetNode()->GetID());
	    this->bubble->SetText("<b>Label:</b><br>" + label);
	}
	//set bubble rect position near the mouse pointer
	if (event->scenePos().y() + bubble->boundingRect().height() > this->height() - 10)
	    this->bubble->setPos(event->scenePos().x() + 5, event->scenePos().y() - bubble->boundingRect().height());
	else
	    this->bubble->setPos(event->scenePos().x() + 5, event->scenePos().y() + 5);
	bubble->show();
	elem->SetHighlighted(true);
	highlighted = elem;
    }
}

void ClutoTreeScene::Unhighlight()
{
    bubble->hide();
    if (highlighted != NULL)
	highlighted->SetHighlighted(false);
}

void ClutoTreeScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    if (roots.empty() || event->buttons() != Qt::LeftButton)
	return;

    QList<QGraphicsItem *> list = this->items(event->scenePos());
    if (list.size() > 0)
    {
	QGraphicsClusterItem * elem = dynamic_cast<QGraphicsClusterItem *>(list[0]);
	if (elem == NULL || elem->GetNode() == roots.top())
	    return;

	if (!elem->GetNode()->HasChildren())
	    return;

	//Set new root and display subtree
	restorePos = event->scenePos();
	this->PushRoot(elem->GetNode(), false);
	emit UpdateSimNeeded(elem->GetNode());
	this->DrawTree();

	emit RedrawingNeeded();
	emit CenteringNeeded(0, 2 * ui->spinBox_3->value());
    }
}

void ClutoTreeScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (roots.empty())
	return;

    //Go level up
    if (event->buttons() == Qt::RightButton)
    {
	this->PopRoot();
	return;
    }

    QList<QGraphicsItem *> list = this->items(event->scenePos());
    if (list.size() > 0)
    {
	QGraphicsClusterItem * elem = dynamic_cast<QGraphicsClusterItem *>(list[0]);

	//read labels in subtree with elem->GetNode() as a root
	if (elem != NULL)
	    emit ShowLabels(elem->GetNode());
    }
}

void ClutoTreeScene::SetSims(double min, double max)
{
    minSim = min;
    maxSim = max;
}
