#include "gui/widgets/text.h"
#include "core/session.h"
#include "core/strings.h"

#include <QDebug>
#include <QFrame>
#include <QLabel>
#include <QPainter>
#include <QScrollArea>
#include <QTextBlock>
#include <QVBoxLayout>

namespace TextWidgets {

/* Generic header meant to be used in conjunction with Section<T> */

Header::Header(QWidget *parent) : QWidget(parent), title_(new QLabel), separator_(new QFrame)
{
	QVBoxLayout *layout = new QVBoxLayout(this);
	setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);

	title_->setTextFormat(Qt::PlainText);

	{
		QFont font = title_->font();
		font.setWeight(QFont::Bold);
		title_->setFont(font);
	}

	separator_->setFrameShape(QFrame::HLine);
	separator_->setFrameShadow(QFrame::Sunken);
	separator_->setFixedHeight(2);

	layout->addWidget(title_.data());
	layout->addWidget(separator_.data());
	layout->setSpacing(0);
	layout->setContentsMargins(0, 0, 0, 0);
}

void Header::SetText(const std::string &text)
{
	title_->setText(Strings::ToQString(text));
	updateGeometry();
}

Label::Label(QWidget *parent) : QLabel(parent)
{
}
Label::Label(const QString &string, QWidget *parent) : QLabel(string, parent)
{
}

void Label::SetElidingMode(bool elide)
{
	elide_ = elide;
	update();
}

void Label::paintEvent(QPaintEvent *event)
{
	if (elide_) {
		/* bruh */
		if (wordWrap()) {
			QFrame::paintEvent(event);

			const QString content = text();

			QPainter painter(this);
			QFontMetrics fontMetrics = painter.fontMetrics();

			bool didElide = false;
			int lineSpacing = fontMetrics.lineSpacing();
			int y = 0;

			QTextLayout textLayout(content, painter.font());
			textLayout.beginLayout();
			for (;;) {
				QTextLine line = textLayout.createLine();

				if (!line.isValid())
					break;

				line.setLineWidth(width());
				int nextLineY = y + lineSpacing;

				if (height() >= nextLineY + lineSpacing) {
					line.draw(&painter, QPoint(0, y));
					y = nextLineY;
				} else {
					QString lastLine = content.mid(line.textStart());
					QString elidedLastLine = fontMetrics.elidedText(lastLine, Qt::ElideRight, width());
					painter.drawText(QPoint(0, y + fontMetrics.ascent()), elidedLastLine);
					line = textLayout.createLine();
					didElide = line.isValid();
					break;
				}
			}
			textLayout.endLayout();
		} else {
			QString backup_text = QLabel::text();

			QFontMetrics metric(fontMetrics());
			QString elided_text = metric.elidedText(backup_text, Qt::ElideRight, width());

			QLabel::setText(elided_text);
			QLabel::paintEvent(event);
			QLabel::setText(backup_text);
		}
	} else {
		/* QLabel can handle everything... */
		QLabel::paintEvent(event);
	}
}

/* ---------------------------------------------------------------------------------- */
/* "Paragraph" widgets, as in widgets meant to hold a bunch of text. */

Paragraph::Paragraph(QWidget *parent) : QWidget(parent), label_(new QLabel)
{
	QVBoxLayout *layout = new QVBoxLayout(this);
	layout->setSpacing(0);
	layout->setContentsMargins(0, 0, 0, 0);

	label_->setTextInteractionFlags(Qt::TextBrowserInteraction);

	/* defaults */
	SetWordWrap(true);
	SetSelectable(true);

	layout->addWidget(label_.data());
}

void Paragraph::SetText(const std::string &text)
{
	label_->setText(Strings::ToQString(text));
}

void Paragraph::SetSelectable(bool enable)
{
	label_->setAttribute(Qt::WidgetAttribute::WA_TransparentForMouseEvents, !enable);
	label_->setCursor(enable ? Qt::IBeamCursor : Qt::ArrowCursor);
}

void Paragraph::SetWordWrap(bool enable)
{
	label_->setWordWrap(enable);
}

/* LabelledParagraph implementation */

LabelledParagraph::LabelledParagraph(QWidget *parent)
    : QWidget(parent), contents_(new QWidget), contents_layout_(new QGridLayout)
{
	QHBoxLayout *ly = new QHBoxLayout(this);

	contents_layout_->setVerticalSpacing(1);
	contents_layout_->setHorizontalSpacing(20);
	contents_layout_->setContentsMargins(0, 0, 0, 0);
	contents_layout_->setColumnStretch(1, 0);

	contents_->setLayout(contents_layout_.data());

	ly->addWidget(contents_.data());
	ly->setContentsMargins(0, 0, 0, 0);
}

LabelledParagraph::~LabelledParagraph()
{
	data_.clear();
}

void LabelledParagraph::Clear(void)
{
	for (auto &[label, data] : data_) {
		contents_layout_->removeWidget(label.data());
		contents_layout_->removeWidget(data.data());
	}

	data_.clear();
}

void LabelledParagraph::SetData(const std::vector<std::pair<std::string, std::string>> &data)
{
	Clear();

	data_.reserve(data.size());
	for (std::size_t i = 0; i < data.size(); i++) {
		QSharedPointer<Label> first(new Label);
		QSharedPointer<Label> second(new Label);

		first->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);

		first->setText(Strings::ToQString(data[i].first));
		second->setText(Strings::ToQString(data[i].second));

		data_.push_back({first, second});

		contents_layout_->addWidget(first.data(), i, 0);
		contents_layout_->addWidget(second.data(), i, 1);
	}
}

void LabelledParagraph::SetStyle(int style)
{
	const QString style_sheet = (style & LabelledParagraph::BoldedLabels) ? "font-weight: bold;" : "";
	for (auto &[label, data] : data_)
		label->setStyleSheet(style_sheet);

	if (style & LabelledParagraph::ElidedData) {
		for (auto &[label, data] : data_) {
			data->setWordWrap(false);
			data->SetElidingMode(true);
		}
	}
}

} // namespace TextWidgets
