/*
 * Oak Video Editor - Non-Linear Video Editor
 * Copyright (C) 2025 Olive CE Team
 *
 * This program 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 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
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "sliderbase.h"

#include <QDebug>
#include <QEvent>
#include <QMessageBox>

#include "common/qtutils.h"
#include "core.h"
#include "window/mainwindow/mainwindow.h"

namespace olive
{

#define super QStackedWidget

SliderBase::SliderBase(QWidget *parent)
	: super(parent)
	, tristate_(false)
	, format_plural_(false)
{
	// Standard (non-numeric) sliders are not draggable, so we indicate as such
	setCursor(Qt::PointingHandCursor);

	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);

	label_ = new SliderLabel(this);
	addWidget(label_);

	editor_ = new FocusableLineEdit(this);
	addWidget(editor_);

	connect(label_, &SliderLabel::focused, this, &SliderBase::ShowEditor);
	connect(label_, &SliderLabel::RequestReset, this, &SliderBase::ResetValue);
	connect(editor_, &FocusableLineEdit::Confirmed, this,
			&SliderBase::LineEditConfirmed);
	connect(editor_, &FocusableLineEdit::Cancelled, this,
			&SliderBase::LineEditCancelled);
}

void SliderBase::SetAlignment(Qt::Alignment alignment)
{
	label_->setAlignment(alignment);
	editor_->setAlignment(alignment);
}

bool SliderBase::IsTristate() const
{
	return tristate_;
}

void SliderBase::SetTristate()
{
	tristate_ = true;
	UpdateLabel();
}

const QVariant &SliderBase::GetValueInternal() const
{
	return value_;
}

void SliderBase::SetValueInternal(const QVariant &v)
{
	if (!CanSetValue()) {
		return;
	}

	value_ = AdjustValue(v);

	// Disable tristate
	tristate_ = false;

	UpdateLabel();
}

void SliderBase::SetDefaultValue(const QVariant &v)
{
	default_value_ = v;
}

void SliderBase::changeEvent(QEvent *e)
{
	if (e->type() == QEvent::LanguageChange) {
		UpdateLabel();
	}
	super::changeEvent(e);
}

bool SliderBase::GetLabelSubstitution(const QVariant &v, QString *out) const
{
	for (auto it = label_substitutions_.constBegin();
		 it != label_substitutions_.constEnd(); it++) {
		if (it->first == v) {
			*out = it->second;
			return true;
		}
	}

	return false;
}

void SliderBase::UpdateLabel()
{
	QString s;

	if (tristate_) {
		s = tr("---");
	} else if (GetLabelSubstitution(GetValueInternal(), &s)) {
		// String will already be set, just pass through
	} else {
		s = GetFormattedValueToString();
	}

	label_->setText(s);
}

QVariant SliderBase::AdjustValue(const QVariant &value) const
{
	return value;
}

bool SliderBase::CanSetValue() const
{
	return true;
}

void SliderBase::ValueSignalEvent(const QVariant &value)
{
	Q_UNUSED(value)
}

void SliderBase::ShowEditor()
{
	// This was a simple click
	// Load label's text into editor
	editor_->setText(ValueToString(value_));

	// Show editor
	setCurrentWidget(editor_);

	// Select all text in the editor
	editor_->setFocus();
	editor_->selectAll();
}

void SliderBase::LineEditConfirmed()
{
	bool is_valid = true;
	QVariant test_val = StringToValue(editor_->text(), &is_valid);

	// Ensure editor doesn't signal that the focus is lost
	editor_->blockSignals(true);
	label_->blockSignals(true);

	if (is_valid) {
		SetValueInternal(test_val);

		setCurrentWidget(label_);

		ValueSignalEvent(value_);
	} else {
		QMessageBox::critical(
			this, tr("Invalid Value"),
			tr("The entered value is not valid for this field."),
			QMessageBox::Ok);

		// Refocus editor
		editor_->setFocus();
	}

	editor_->blockSignals(false);
	label_->blockSignals(false);
}

void SliderBase::LineEditCancelled()
{
	// Ensure editor doesn't signal that the focus is lost
	editor_->blockSignals(true);
	label_->blockSignals(true);

	// Set widget back to label
	setCurrentWidget(label_);

	editor_->blockSignals(false);
	label_->blockSignals(false);
}

void SliderBase::ResetValue()
{
	if (default_value_.isValid()) {
		SetValueInternal(default_value_);
		ValueSignalEvent(value_);
	}
}

void SliderBase::SetFormat(const QString &s, const bool plural)
{
	custom_format_ = s;
	format_plural_ = plural;
	UpdateLabel();
}

void SliderBase::ClearFormat()
{
	custom_format_.clear();
	UpdateLabel();
}

bool SliderBase::IsFormatPlural() const
{
	return format_plural_;
}

QString SliderBase::GetFormat() const
{
	if (custom_format_.isEmpty()) {
		return QStringLiteral("%1");
	} else {
		return custom_format_;
	}
}

QString SliderBase::GetFormattedValueToString() const
{
	return GetFormattedValueToString(GetValueInternal());
}

QString SliderBase::GetFormattedValueToString(const QVariant &v) const
{
	if (format_plural_) {
		return tr(GetFormat().toUtf8().constData(), nullptr, v.toInt());
	} else {
		return GetFormat().arg(ValueToString(v));
	}
}

}
