/*
    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 "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsTextItem>
#include <QFileDialog>
#include <QMessageBox>
#include <fstream>
#include <stdexcept>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>

using namespace std;

MainWindow::MainWindow(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    scene = new ClutoTreeScene(this->ui);
    //ui->graphicsView->setScene(scene);
    dScene = new DendrogramScene(this->ui);

    ui->graphicsView->setScene(dScene);

    ui->graphicsView->setMouseTracking(true);
    root = NULL;

    ui->statusBar->addWidget(new QLabel("<b>Tree:</b> "));
    treeFile = new QLabel("none");
    ui->statusBar->addWidget(treeFile);
    ui->statusBar->addWidget(new QLabel("<b>Labels:</b> "));
    labelsFile = new QLabel("none");
    ui->statusBar->addWidget(labelsFile);

    connect(scene, SIGNAL(RedrawingNeeded()), this, SLOT(RedrawScene()));
    connect(scene, SIGNAL(CenteringNeeded(int,int)), this, SLOT(CenterScene(int,int)));

    connect(scene, SIGNAL(ShowLabels(Node*)), ui->listWidget, SLOT(clear())); //this may require change in the future
    connect(scene, SIGNAL(ShowLabels(Node*)), this, SLOT(FillLabelList(Node*))); //order of execution connections may be incorrect at some time
    connect(scene, SIGNAL(UpdateSimNeeded(Node*)), this, SLOT(FindMinMaxSimilarities(Node*)));

    currentScale = 1;
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void MainWindow::on_pushButton_clicked()
{
    ReadTreeFile();
    if (!children.empty() && !similarities.empty()) //change to if(ReadTreeFile())
    {
	BuildTree();
	RedrawScene();
    }
}

void MainWindow::on_pushButton_2_clicked()
{
    ReadLabelFile();
    if (root != NULL)
	scene->DrawTree();
}

void MainWindow::on_pushButton_4_clicked()
{
    ReadSimFile();
    if (!children.empty() && !similarities.empty()) //change to if(ReadTreeFile())
    {
	BuildTree();
	RedrawScene();
    }
}


void MainWindow::ReadTreeFile()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open tree file", "/home/wojtek/Mgr/ClutoTrees/");
    string sFileName = fileName.toStdString();
    if (fileName.isEmpty())
	return;

    ifstream in_stream(sFileName.c_str());
    if(!in_stream)
    {
	QMessageBox::critical(this, "Error", "Error reading from file " + fileName);
	return;
    }

    children.clear();
    similarities.clear();
    labels.clear();
    scene->SetLabels(NULL);
    labelsFile->setText("none");

    string line;
    size_t i = 0;
    while (getline(in_stream, line, '\n'))
    {
	vector<string> tokens;
	boost::algorithm::split(tokens, line, boost::algorithm::is_any_of(" "));
	if (tokens.size() != 3)
	{
	    QMessageBox::critical(this, "Error", "Error in tree file \"" + fileName + "\"\nin line: \"" + QString::fromStdString(line) + "\"");
	    return;
	}
	int parent = boost::lexical_cast<int>(tokens[0]);

	//Similarities are read from external file
	//double sim = boost::lexical_cast<double>(tokens[2]);
	//similarities.push_back(sim);

	map<int, vector<size_t> >::iterator iter = children.find(parent);
	if (iter == children.end())
	    children[parent] = vector<size_t>();

	children[parent].push_back(i);
	i++;
    }
    treeFile->setText(fileName);
    nodes = i;

    if (children[-1].size() > 1)
	children[-2].push_back(-1);
}

void MainWindow::ReadSimFile()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open sim file", "/home/wojtek/Mgr/ClutoTrees/");
    string sFileName = fileName.toStdString();
    if (fileName.isEmpty())
	return;

    ifstream in_stream(sFileName.c_str());
    if(!in_stream)
    {
	QMessageBox::critical(this, "Error", "Error reading from file " + fileName);
	return;
    }

    string line;
    size_t i = 0;
    while (getline(in_stream, line, '\n'))
    {
	double sim = boost::lexical_cast<double>(line);
	similarities.push_back(sim);
	i++;
    }

    if (i != this->nodes)
    {
	QMessageBox::critical(this, "Error", "Wrong number of lines in file " + fileName);
	similarities.clear();
	return;
    }
    QMessageBox::information(this, "Success", QString::fromStdString("Similarities have been read"));
}

void MainWindow::ReadLabelFile()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open label file", "/home/wojtek/Mgr/ClutoTrees/", "Text files *.txt (*.txt)");
    string sFileName = fileName.toStdString();
    if (fileName.isEmpty())
	return;

    ifstream in_stream(sFileName.c_str());
    if(!in_stream)
    {
	QMessageBox::critical(this, "Error", "Error reading from file " + fileName);
	return;
    }

    labels.clear();
    string line;
    size_t i = 0;
    while (getline(in_stream, line, '\n'))
    {
	this->labels.push_back(line);
	i++;
    }

    if (i != (this->nodes + 1)/2)
    {
	QMessageBox::critical(this, "Error", "Wrong number of lines in file " + fileName);
	labels.clear();
	return;
    }

    scene->SetLabels(&labels);
    labelsFile->setText(fileName);
    if (root != NULL && ui->checkBox->isChecked())
	RedrawScene();
    QMessageBox::information(this, "Success", QString::fromStdString("Labels have been read"));
}

void MainWindow::BuildTree()
{
    cout << "Building tree" << endl;

    map<int, vector<size_t> >::iterator iter = children.find(-2); //Check if there are more than one root
    if (iter == children.end()) //if there is only one root
	iter = children.find(-1);

    int rootId = iter->second[0];
    double sim = similarities[rootId];

    root = new Node(rootId, 0, sim);

    maxlevel = Insert(root, 0);
    root->SetLevelsAhead(maxlevel);

    scene->PushRoot(root, true);
    scene->SetMaxLevel(maxlevel);

    dScene->SetRoot(root);
    FindMinMaxSimilarities(root);

    cout << "Done: " << maxlevel << " levels" << endl;
}

size_t MainWindow::Insert(Node * node, size_t level)
{
    map<int, vector<size_t> >::iterator iter = children.find(node->GetID());
    size_t maxLevels = 0;

    if (iter != children.end()) //if root has children
    {
	for (size_t i = 0; i < iter->second.size(); ++i)
	{
	    node->AddChild(new Node(iter->second[i], level + 1, similarities[iter->second[i]]));
	    size_t levels = Insert(node->GetChild(node->GetChildrenNo() - 1), level + 1);
	    if (levels > maxLevels)
		maxLevels = levels;
	}

	node->SetLevelsAhead(1 + maxLevels);
	return node->GetLevelsAhead();
    }
    else
	return 0;
}

void MainWindow::RedrawScene()
{
    if (root != NULL)
    {
//	using namespace boost::posix_time;
//	ptime start = microsec_clock::local_time();
//	std::cout << "Drawing tree" << std::endl;

	std::cout << ui->graphicsView->width() << std::endl;
	std::cout << "Selected: " << ui->comboBox_2->currentIndex() << std::endl;
	if (ui->comboBox_2->currentIndex() == 0)
	    dScene->DrawTree(ui->graphicsView->viewport()->width()); //Drawing dendrogram
	else
	    scene->DrawTree(); //Drawing tree

	std::cout << ui->graphicsView->width() << std::endl;
	ui->graphicsView->viewport()->update(ui->graphicsView->viewport()->visibleRegion());

	//ui->graphicsView->updateSceneRect(ui->graphicsView->visibleRegion().boundingRect());

//	ptime stop = microsec_clock::local_time();
//	time_duration lenght = stop - start;
//	std::cout << "Done in " << lenght.total_milliseconds() << " ms" << std::endl;
    }
}

void MainWindow::on_spinBox_valueChanged(int )
{
    RedrawScene();
}

void MainWindow::on_spinBox_2_valueChanged(int )
{
    RedrawScene();
}

void MainWindow::on_spinBox_3_valueChanged(int )
{
    RedrawScene();
}

void MainWindow::on_spinBox_4_editingFinished()
{
    RedrawScene();
}

void MainWindow::on_pushButton_3_clicked()
{
    scene->PopRoot();
}

void MainWindow::CenterScene(int x, int y)
{
    ui->graphicsView->centerOn(x, y);
}

void MainWindow::FillLabelList(Node *node)
{
    if (labels.empty())
	return;
    if (node->HasChildren())
    {
	for (size_t i = 0; i < node->GetChildrenNo(); ++i)
	    FillLabelList(node->GetChild(i));
    }
    else
    {
	std::string label = labels[node->GetID()];
	std::string id = boost::lexical_cast<std::string>(node->GetID());
	QString qstr = QString::fromStdString(id) + ": " + QString::fromUtf8(label.c_str(), label.size());
	QListWidgetItem * item = new QListWidgetItem(qstr);
	item->setToolTip("<p style=\"width: 200px;\">" + qstr + "</p>");
	ui->listWidget->addItem(item);
    }
}

void MainWindow::FindMinMaxSimilarities(Node * node)
{
    if (scene == NULL)
	return;
    double min = 1, max = 0;

    Traverse(node, min, max);

    scene->SetSims(min, max);
    dScene->SetSims(min, max);

    //std::cout << "Min: " << min << " max: " << max << std::endl;
}

void MainWindow::Traverse(Node * node, double & min, double & max)
{
    if (!node->HasChildren())
	return;
    else
    {
	if (node->GetSimilarity() > max)
	    max = node->GetSimilarity();
	if (node->GetSimilarity() < min)
	    min = node->GetSimilarity();
	for (size_t i = 0; i < node->GetChildrenNo(); ++i)
	    Traverse(node->GetChild(i), min, max);
    }
}

void MainWindow::on_checkBox_stateChanged(int )
{
    RedrawScene();
}

void MainWindow::on_comboBox_currentIndexChanged(int index)
{
    double newScale;

    switch (index)
    {
    case 0: newScale = 0.5; break;
    case 1: newScale = 1; break;
    case 2: newScale = 2; break;
    }

    double factor = newScale / currentScale;
    currentScale = newScale;
    this->ui->graphicsView->scale(factor, factor);
}


void MainWindow::on_comboBox_2_currentIndexChanged(int index)
{
    if (index == 0)
	ui->graphicsView->setScene(dScene);
    else
	ui->graphicsView->setScene(scene);
    
    RedrawScene();
}
