/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            renderdialog.cc
 *
 *  Thu Sep 20 17:32:11 CEST 2018
 *  Copyright 2018 Bent Bisballe Nyeng
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *  This file is part of DrumGizmo.
 *
 *  DrumGizmo is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  DrumGizmo 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
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with DrumGizmo; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include "renderdialog.h"

#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QFileDialog>
#include <QLabel>
#include <QAbstractItemModel>
#include <QHeaderView>
#include <QStyledItemDelegate>
#include <QStyleOptionProgressBar>
#include <QApplication>

#include "project.h"

class ProgressDelegate
	: public QStyledItemDelegate
{
//	Q_OBJECT

public:
	ProgressDelegate(Project& project, QObject *parent)
		: QStyledItemDelegate(parent)
		, project(project)
	{
	}

	void paint(QPainter *painter,
	           const QStyleOptionViewItem &option,
	           const QModelIndex &index ) const override
	{
		if(/*index.row() == current_row &&*/
		   index.column() == 2 &&
		   index.parent() == QModelIndex())
		{
			QStyleOptionProgressBar progressBarOption;
			progressBarOption.state = QStyle::State_Enabled;
			progressBarOption.direction = QApplication::layoutDirection();
			progressBarOption.rect = option.rect;
			progressBarOption.fontMetrics = QApplication::fontMetrics();
			progressBarOption.minimum = 0;
			progressBarOption.textAlignment = Qt::AlignCenter;
			progressBarOption.textVisible = true;

			if(index.row() == current_row)
			{
				// Current task
				progressBarOption.maximum = progress_max;
				progressBarOption.progress = progress;
			}
			else if(index.row() < current_row)
			{
				// Finished task
				progressBarOption.maximum = 100;
				progressBarOption.progress = 100;
			}
			else
			{
				// Waiting task
				progressBarOption.maximum = 100;
				progressBarOption.progress = 0;
			}
			QApplication::style()->drawControl(QStyle::CE_ProgressBar,
			                                   &progressBarOption, painter);
		}
		else
		{
			QStyledItemDelegate::paint(painter, option, index);
		}
	}

	void setActiveTask(int current_instrument_id)
	{
		auto instrument_ids = project.getInstrumentList();
		current_row = 0;
		for(auto instrument_id : instrument_ids)
		{
			if(current_instrument_id == instrument_id)
			{
				return;
			}
			current_row++;
		}

		// current_row will be one above the row count here
	}

	void setProgressMaximum(int progress_max)
	{
		this->progress_max = progress_max;
	}

	void setProgress(int progress)
	{
		this->progress = progress;
	}

private:
	Project& project;
	int progress{0};
	int progress_max{100};
	int current_row{-1};
};

class RenderDataModel
	: public QAbstractItemModel
{
public:
	RenderDataModel(Project& project)
		: project(project)
	{
	}

	QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
	{
		if(!hasIndex(row, column, parent))
		{
			return QModelIndex();
		}

		if(!parent.isValid())
		{
			auto instrument_ids = project.getInstrumentList();
			if(row < instrument_ids.size())
			{
				return createIndex(row, column, (void*)42);
			}
			else
			{
				return QModelIndex(); // row is out of bounds.
			}
		}

		return QModelIndex();
	}

	QModelIndex parent(const QModelIndex &index) const override
	{
		return QModelIndex(); // no parent
	}

	int rowCount(const QModelIndex &parent = QModelIndex()) const override
	{
		if(parent.column() > 0) // only return row count on first column
		{
			return 0;
		}

		if(!parent.isValid())
		{
			auto instrument_ids = project.getInstrumentList();
			return instrument_ids.size();
		}

		return 0; // no children
	}

	int columnCount(const QModelIndex &parent = QModelIndex()) const override
	{
		return 3; // icon, text, progressbar
	}

	QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
	{
		if(!index.isValid())
		{
			return QVariant();
		}

		if(role == Qt::DecorationRole)
		{
			if(index.column() == 0)
			{
				if(index.row() < current_row)
				{
					return QPixmap(":/icons/task_done.png");
				}
				else if(index.row() == current_row)
				{
					if(error)
					{
						return QPixmap(":/icons/task_error.png");
					}
					else
					{
						return QPixmap(":/icons/task_running.png");
					}
				}
				else
				{
					return QPixmap(":/icons/task_waiting.png");
				}
			}
		}

		if(role == Qt::DisplayRole)
		{
			auto instrument_ids = project.getInstrumentList();
			auto instrument_id = instrument_ids.begin() + index.row();
			const auto& instrument = project.getInstrument(*instrument_id);
			switch(index.column())
			{
			case 1: return instrument.getInstrumentName();
			default: return QVariant();
			}
		}

		return QVariant();
	}
	void setError()
	{
		error = true;
	}

	QVariant headerData(int section, Qt::Orientation orientation, int role) const override
	{
		return QVariant();
	}

	void refresh()
	{
		beginResetModel();
		endResetModel();
	}

	void setActiveTask(int current_instrument_id)
	{
		auto instrument_ids = project.getInstrumentList();
		current_row = 0;
		for(auto instrument_id : instrument_ids)
		{
			if(current_instrument_id == instrument_id)
			{
				return;
			}
			current_row++;
		}

		// current_row will be one above the row count here
	}

private:
	Project& project;
	int current_row{-1};
	bool error{false};
};

RenderDialog::RenderDialog(QWidget* parent, Project& project)
	: QDialog(parent)
	, project(project)
	, renderer(project)
{
	setWindowTitle(tr("Export drumkit"));
	model = new RenderDataModel(project);

	auto vl = new QVBoxLayout();
	setLayout(vl);

	layout()->addWidget(new QLabel(tr("Export path:")));

	{
		auto hl = new QHBoxLayout();
		export_path = new QLineEdit(this);
		export_path->setText(project.getExportPath());
		connect(export_path, SIGNAL(textEdited(const QString&)),
		        this, SLOT(exportPathChanged(const QString&)));
		auto btn = new QPushButton(tr("..."));
		btn->setMaximumWidth(32);
		connect(btn, SIGNAL(clicked()), this, SLOT(chooseExportPath()));
		hl->addWidget(export_path);
		hl->addWidget(btn);
		vl->addLayout(hl);
	}

	export_btn = new QPushButton(this);
	export_btn->setText(tr("Export"));
	connect(export_btn, SIGNAL(clicked()), this, SLOT(render()));
	layout()->addWidget(export_btn);

	bar = new QProgressBar(this);
	connect(&renderer, SIGNAL(progressStart(int)),
	        bar, SLOT(setMaximum(int)));
	layout()->addWidget(bar);

	connect(&renderer, SIGNAL(progressRenderStart(int)),
	        this, SLOT(progressRenderStart(int)));
	connect(&renderer, SIGNAL(progressRenderTask(int)),
	        this, SLOT(progressRenderTask(int)));

	tasks = new QTreeView(this);
	tasks->setModel(model);
	tasks->setHeaderHidden(true);
	tasks->setRootIsDecorated(false);
	tasks->header()->resizeSection(0, 24);
	delegate = new ProgressDelegate(project, this);
	tasks->setItemDelegate(delegate);

	connect(&renderer, SIGNAL(progressStart(int)),
	        this, SLOT(progressStart(int)));
	connect(&renderer, SIGNAL(progressFinished(int)),
	        this, SLOT(progressFinished(int)));
	layout()->addWidget(tasks);

	connect(&renderer, SIGNAL(progressTask(QString)),
	        this, SLOT(progressTask(QString)));
	connect(&renderer, SIGNAL(progressRenderFinished(int)),
	        this, SLOT(progressRenderFinished(int)));
}

void RenderDialog::render()
{
	bar->setValue(1);
	renderer.render();
}

void RenderDialog::progressStart(int total)
{
	export_btn->setEnabled(false);
}

void RenderDialog::progressTask(QString text)
{
	//tasks->addItem(text);
}

void RenderDialog::progressRenderStart(int tasktotal)
{
	auto task = bar->value();

	auto instrument_ids = project.getInstrumentList();
	auto instrument_id = instrument_ids.begin() + (task - 1);
	delegate->setActiveTask(*instrument_id);
	model->setActiveTask(*instrument_id);

	delegate->setProgressMaximum(tasktotal);
	model->refresh();
}

void RenderDialog::progressRenderTask(int subtask)
{
	delegate->setProgress(subtask);
	model->refresh();
}

void RenderDialog::progressRenderFinished(int success)
{
	auto task = bar->value();

	// Search past the last instriment id to mark all tasks as finished
	auto instrument_ids = project.getInstrumentList();
	auto instrument_id = instrument_ids.begin() + task;
	delegate->setActiveTask(*instrument_id); // note this id is invalid
	model->setActiveTask(*instrument_id); // note this id is invalid

	bar->setValue(task + 1);
	if(success != 0)
	{
		model->setError();
	}
	model->refresh();
}

void RenderDialog::progressFinished(int success)
{
	if(success != 0)
	{
		model->setError();
	}
	model->refresh();

	export_btn->setEnabled(true);
}

void RenderDialog::chooseExportPath()
{
	QString path =
		QFileDialog::getExistingDirectory(
			this, tr("Choose export directory."),
			project.getExportPath(), QFileDialog::ShowDirsOnly);

	if(path != "")
	{
		export_path->setText(path);
		project.setExportPath(path);
	}
}

void RenderDialog::exportPathChanged(const QString& path)
{
	project.setExportPath(path);
}
