/*
 * Cantata
 *
 * Copyright (c) 2011-2022 Craig Drummond <craig.p.drummond@gmail.com>
 *
 * ----
 *
 * 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 2 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "volumeslider.h"
#include "config.h"
#include "mpd-interface/mpdconnection.h"
#include "mpd-interface/mpdstatus.h"
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
#include "mpd-interface/httpstream.h"
#endif
#include "gui/settings.h"
#include "gui/stdactions.h"
#include "support/action.h"
#include "support/actioncollection.h"
#include "support/utils.h"
#include <QApplication>
#include <QLabel>
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QProxyStyle>
#include <QStyle>
#include <QWheelEvent>

class VolumeSliderProxyStyle : public QProxyStyle {
public:
	VolumeSliderProxyStyle()
		: QProxyStyle()
	{
		setBaseStyle(qApp->style());
	}

	int styleHint(StyleHint stylehint, const QStyleOption* opt, const QWidget* widget, QStyleHintReturn* returnData) const override
	{
		if (SH_Slider_AbsoluteSetButtons == stylehint) {
			return Qt::LeftButton | QProxyStyle::styleHint(stylehint, opt, widget, returnData);
		}
		else {
			return QProxyStyle::styleHint(stylehint, opt, widget, returnData);
		}
	}
};

static int widthStep = 4;
static int constHeightStep = 2;

VolumeSlider::VolumeSlider(bool isMpd, QWidget* p)
	: QSlider(p), isMpdVol(isMpd), isActive(isMpd), lineWidth(0), down(false), muteAction(nullptr), menu(nullptr)
{
	widthStep = 4;
	setRange(0, 100);
	setPageStep(Settings::self()->volumeStep());
	lineWidth = Utils::scaleForDpi(1);

	int w = lineWidth * widthStep * 19;
	int h = lineWidth * constHeightStep * 10;
	setFixedHeight(h + 1);
	setFixedWidth(w);
	setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
	setOrientation(Qt::Horizontal);
	setFocusPolicy(Qt::NoFocus);
	setStyle(new VolumeSliderProxyStyle());
	setStyleSheet(QString("QSlider::groove:horizontal {border: 0px;} "
	                      "QSlider::sub-page:horizontal {border: 0px;} "
	                      "QSlider::handle:horizontal {width: 0px; height:0px; margin:0;}"));
	textCol = Utils::clampColor(palette().color(QPalette::Active, QPalette::Text));
	generatePixmaps();
}

void VolumeSlider::initActions()
{
	if (muteAction) {
		return;
	}
	muteAction = ActionCollection::get()->createAction("mute", tr("Mute"));
	addAction(muteAction);
	connect(muteAction, SIGNAL(triggered()), this, SLOT(muteToggled()));
	connect(StdActions::self()->increaseVolumeAction, SIGNAL(triggered()), this, SLOT(increaseVolume()));
	connect(StdActions::self()->decreaseVolumeAction, SIGNAL(triggered()), this, SLOT(decreaseVolume()));
	if (isMpdVol) {
		connect(MPDStatus::self(), SIGNAL(updated()), this, SLOT(updateStatus()));
		connect(this, SIGNAL(valueChanged(int)), MPDConnection::self(), SLOT(setVolume(int)));
		connect(this, SIGNAL(toggleMute()), MPDConnection::self(), SLOT(toggleMute()));
	}
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
	else {
		connect(this, SIGNAL(valueChanged(int)), HttpStream::self(), SLOT(setVolume(int)));
		connect(this, SIGNAL(toggleMute()), HttpStream::self(), SLOT(toggleMute()));
		connect(HttpStream::self(), SIGNAL(update()), this, SLOT(updateStatus()));
		connect(HttpStream::self(), SIGNAL(isEnabled(bool)), this, SLOT(setEnabled(bool)));
		connect(HttpStream::self(), SIGNAL(isEnabled(bool)), this, SIGNAL(stateChanged()));
	}
#endif
	addAction(StdActions::self()->increaseVolumeAction);
	addAction(StdActions::self()->decreaseVolumeAction);
}

void VolumeSlider::setColor(const QColor& col)
{
	if (col != textCol) {
		textCol = col;
		generatePixmaps();
	}
}

void VolumeSlider::paintEvent(QPaintEvent*)
{
	bool reverse = isRightToLeft();
	QPainter p(this);
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
	bool muted = isMpdVol ? MPDConnection::self()->isMuted() : HttpStream::self()->isMuted();
#else
	bool muted = MPDConnection::self()->isMuted();
#endif
	if (muted || !isEnabled()) {
		p.setOpacity(0.25);
	}

	p.drawPixmap(0, 0, pixmaps[0]);
#if 1
	int steps = (value() / 10.0) + 0.5;
	if (steps > 0) {
		if (steps < 10) {
			int wStep = widthStep * lineWidth;
			p.setClipRect(reverse
			                      ? QRect(width() - ((steps * wStep * 2) - wStep), 0, width(), height())
			                      : QRect(0, 0, (steps * wStep * 2) - wStep, height()));
			p.setClipping(true);
		}
		p.drawPixmap(0, 0, pixmaps[1]);
		if (steps < 10) {
			p.setClipping(false);
		}
	}
#else// Partial filling of each block?
	if (value() > 0) {
		if (value() < 100) {
			int fillWidth = (width() * (0.01 * value())) + 0.5;
			p.setClipRect(reverse
			                      ? QRect(width() - fillWidth, 0, width(), height())
			                      : QRect(0, 0, fillWidth, height()));
			p.setClipping(true);
		}
		p.drawPixmap(0, 0, *(pixmaps[1]));
		if (value() < 100) {
			p.setClipping(false);
		}
	}
#endif

	if (!muted) {
		p.setOpacity(p.opacity() * 0.75);
		p.setPen(textCol);
		QFont f(font());
		f.setPixelSize(qMax(height() / 2.5, 8.0));
		p.setFont(f);
		QRect r = rect();
		bool rtl = isRightToLeft();
		if (rtl) {
			r.setX(widthStep * lineWidth * 12);
		}
		else {
			r.setWidth(widthStep * lineWidth * 7);
		}
		p.drawText(r, Qt::AlignRight, QString("%1%").arg(value()));
	}
}

void VolumeSlider::mousePressEvent(QMouseEvent* ev)
{
	if (Qt::MiddleButton == ev->buttons()) {
		down = true;
	}
	else {
		QSlider::mousePressEvent(ev);
	}
}

void VolumeSlider::mouseReleaseEvent(QMouseEvent* ev)
{
	if (down) {
		down = false;
		muteAction->trigger();
		update();
	}
	else {
		QSlider::mouseReleaseEvent(ev);
	}
}

void VolumeSlider::contextMenuEvent(QContextMenuEvent* ev)
{
	static const char* constValProp = "val";
	if (!menu) {
		menu = new QMenu(this);
		muteMenuAction = menu->addAction(tr("Mute"));
		muteMenuAction->setProperty(constValProp, -1);
		for (int i = 0; i < 11; ++i) {
			menu->addAction(QString("%1%").arg(i * 10))->setProperty(constValProp, i * 10);
		}
	}

#ifdef ENABLE_HTTP_STREAM_PLAYBACK
	bool muted = isMpdVol ? MPDConnection::self()->isMuted() : HttpStream::self()->isMuted();
#else
	bool muted = MPDConnection::self()->isMuted();
#endif

	muteMenuAction->setText(muted ? tr("Unmute") : tr("Mute"));
	QAction* ret = menu->exec(mapToGlobal(ev->pos()));
	if (ret) {
		int val = ret->property(constValProp).toInt();
		if (-1 == val) {
			muteAction->trigger();
		}
		else {
			setValue(val);
		}
	}
}

void VolumeSlider::wheelEvent(QWheelEvent* ev)
{
	int numDegrees = ev->angleDelta().y() / 8;
	int numSteps = numDegrees / 15;
	if (numSteps > 0) {
		for (int i = 0; i < numSteps; ++i) {
			increaseVolume();
		}
	}
	else {
		for (int i = 0; i > numSteps; --i) {
			decreaseVolume();
		}
	}
}

void VolumeSlider::muteToggled()
{
	if (isActive) {
		emit toggleMute();
	}
}

void VolumeSlider::updateStatus()
{
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
	int volume = isMpdVol ? MPDStatus::self()->volume() : HttpStream::self()->volume();
#else
	int volume = MPDStatus::self()->volume();
#endif

	blockSignals(true);
	if (volume < 0) {
		setValue(0);
	}
	else {
		int unmuteVolume = -1;
		if (0 == volume) {
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
			unmuteVolume = isMpdVol ? MPDConnection::self()->unmuteVolume() : HttpStream::self()->unmuteVolume();
#else
			unmuteVolume = MPDConnection::self()->unmuteVolume();
#endif
			if (unmuteVolume > 0) {
				volume = unmuteVolume;
			}
		}
		setToolTip(unmuteVolume > 0 ? tr("Volume %1% (Muted)").arg(volume) : tr("Volume %1%").arg(volume));
		setValue(volume);
	}
	bool wasEnabled = isEnabled();
	setEnabled(volume >= 0);
	update();
	muteAction->setEnabled(isEnabled());
	StdActions::self()->increaseVolumeAction->setEnabled(isEnabled());
	StdActions::self()->decreaseVolumeAction->setEnabled(isEnabled());
	blockSignals(false);
	if (isEnabled() != wasEnabled) {
		emit stateChanged();
	}
}

void VolumeSlider::increaseVolume()
{
	if (isActive) {
		triggerAction(QAbstractSlider::SliderPageStepAdd);
	}
}

void VolumeSlider::decreaseVolume()
{
	if (isActive) {
		triggerAction(QAbstractSlider::SliderPageStepSub);
	}
}

void VolumeSlider::generatePixmaps()
{
	pixmaps[0] = generatePixmap(false);
	pixmaps[1] = generatePixmap(true);
}

QPixmap VolumeSlider::generatePixmap(bool filled)
{
	bool reverse = isRightToLeft();
	QPixmap pix(size());
	pix.fill(Qt::transparent);
	QPainter p(&pix);
	p.setPen(textCol);
	for (int i = 0; i < 10; ++i) {
		int barHeight = (lineWidth * constHeightStep) * (i + 1);
		QRect r(reverse ? pix.width() - (widthStep + (i * lineWidth * widthStep * 2))
		                : i * lineWidth * widthStep * 2,
		        pix.height() - (barHeight + 1), (lineWidth * widthStep) - 1, barHeight);
		if (filled) {
			p.fillRect(r.adjusted(1, 1, 0, 0), textCol);
		}
		else if (lineWidth > 1) {
			p.drawRect(r);
			p.drawRect(r.adjusted(1, 1, -1, -1));
		}
		else {
			p.drawRect(r);
		}
	}
	return pix;
}

#include "moc_volumeslider.cpp"
