/*
 This file is part of Telegram Desktop,
 the official desktop version of Telegram messaging app, see https://telegram.org

 Telegram Desktop 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.

 It 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.

 Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 */
#include "stdafx.h"
#include "ui/widgets/tooltip.h"

#include "application.h"
#include "styles/style_widgets.h"

namespace Ui {

Tooltip *TooltipInstance = nullptr;

const style::Tooltip *AbstractTooltipShower::tooltipSt() const {
	return &st::defaultTooltip;
}

AbstractTooltipShower::~AbstractTooltipShower() {
	if (TooltipInstance && TooltipInstance->_shower == this) {
		TooltipInstance->_shower = 0;
	}
}

Tooltip::Tooltip() : TWidget(nullptr) {
	TooltipInstance = this;

	setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint);
	setAttribute(Qt::WA_NoSystemBackground, true);
	setAttribute(Qt::WA_TranslucentBackground, true);

	_showTimer.setSingleShot(true);
	connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow()));

	connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}

void Tooltip::onShow() {
	if (_shower) {
		auto text = QString();
		if (auto window = App::wnd()) {
			window->updateIsActive(0);
			if (window->isActive()) {
				text = _shower->tooltipText();
			}
		}
		if (text.isEmpty()) {
			Hide();
		} else {
			TooltipInstance->popup(_shower->tooltipPos(), text, _shower->tooltipSt());
		}
	}
}

void Tooltip::onWndActiveChanged() {
	if (!App::wnd() || !App::wnd()->windowHandle() || !App::wnd()->windowHandle()->isActive()) {
		Tooltip::Hide();
	}
}

bool Tooltip::eventFilter(QObject *o, QEvent *e) {
	if (e->type() == QEvent::Leave) {
		_hideByLeaveTimer.start(10);
	} else if (e->type() == QEvent::Enter) {
		_hideByLeaveTimer.stop();
	} else if (e->type() == QEvent::MouseMove) {
		if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) {
			Hide();
		}
	}
	return TWidget::eventFilter(o, e);
}

void Tooltip::onHideByLeave() {
	Hide();
}

Tooltip::~Tooltip() {
	if (TooltipInstance == this) {
		TooltipInstance = 0;
	}
}

void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) {
	if (!_hideByLeaveTimer.isSingleShot()) {
		_hideByLeaveTimer.setSingleShot(true);
		connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave()));

		Sandbox::installEventFilter(this);
	}

	_point = m;
	_st = st;
	_text = Text(_st->textStyle, text, _textPlainOptions, _st->widthMax, true);

	_useTransparency = Platform::TransparentWindowsSupported(_point);
	setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);

	int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right();
	int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom();

	// count tooltip size
	QSize s(addw + _text.maxWidth(), addh + _text.minHeight());
	if (s.width() > _st->widthMax) {
		s.setWidth(addw + _text.countWidth(_st->widthMax - addw));
		s.setHeight(addh + _text.countHeight(s.width() - addw));
	}
	int32 maxh = addh + (_st->linesMax * _st->textStyle.font->height);
	if (s.height() > maxh) {
		s.setHeight(maxh);
	}

	// count tooltip position
	QPoint p(m + _st->shift);
	if (rtl()) {
		p.setX(m.x() - s.width() - _st->shift.x());
	}
	if (s.width() < 2 * _st->shift.x()) {
		p.setX(m.x() - (s.width() / 2));
	}

	// adjust tooltip position
	QRect r(QApplication::desktop()->screenGeometry(m));
	if (r.x() + r.width() - _st->skip < p.x() + s.width() && p.x() + s.width() > m.x()) {
		p.setX(qMax(r.x() + r.width() - int32(_st->skip) - s.width(), m.x() - s.width()));
	}
	if (r.x() + _st->skip > p.x() && p.x() < m.x()) {
		p.setX(qMin(m.x(), r.x() + int32(_st->skip)));
	}
	if (r.y() + r.height() - _st->skip < p.y() + s.height()) {
		p.setY(m.y() - s.height() - _st->skip);
	}
	if (r.y() > p.x()) {
		p.setY(qMin(m.y() + _st->shift.y(), r.y() + r.height() - s.height()));
	}

	setGeometry(QRect(p, s));

	_hideByLeaveTimer.stop();
	show();
}

void Tooltip::paintEvent(QPaintEvent *e) {
	Painter p(this);

#ifdef OS_MAC_OLD
	p.setCompositionMode(QPainter::CompositionMode_Source);
	p.fillRect(e->rect(), Qt::transparent);
	p.setCompositionMode(QPainter::CompositionMode_SourceOver);
#endif // OS_MAC_OLD

	if (_useTransparency) {
		p.setPen(_st->textBorder);
		p.setBrush(_st->textBg);
		PainterHighQualityEnabler hq(p);
		p.drawRoundedRect(QRectF(0.5, 0.5, width() - 1., height() - 1.), st::buttonRadius, st::buttonRadius);
	} else {
		p.fillRect(rect(), _st->textBg);

		p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder);
		p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder);
		p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
		p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
	}
	int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textStyle.font->height);

	p.setPen(_st->textFg);
	_text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines);
}

void Tooltip::hideEvent(QHideEvent *e) {
	if (TooltipInstance == this) {
		Hide();
	}
}

void Tooltip::Show(int32 delay, const AbstractTooltipShower *shower) {
	if (!TooltipInstance) {
		new Tooltip();
	}
	TooltipInstance->_shower = shower;
	if (delay >= 0) {
		TooltipInstance->_showTimer.start(delay);
	} else {
		TooltipInstance->onShow();
	}
}

void Tooltip::Hide() {
	if (auto instance = TooltipInstance) {
		TooltipInstance = nullptr;
		instance->_showTimer.stop();
		instance->_hideByLeaveTimer.stop();
		instance->hide();
		instance->deleteLater();
	}
}

} // namespace Ui