/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/history_view_top_bar_widget.h"

#include <rpl/combine.h>
#include <rpl/combine_previous.h>
#include "history/history.h"
#include "boxes/add_contact_box.h"
#include "boxes/confirm_box.h"
#include "info/info_memento.h"
#include "info/info_controller.h"
#include "storage/storage_shared_media.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "main/main_session.h"
#include "lang/lang_keys.h"
#include "core/shortcuts.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/effects/radial_animation.h"
#include "ui/special_buttons.h"
#include "ui/unread_badge.h"
#include "ui/ui_utility.h"
#include "window/window_session_controller.h"
#include "window/window_peer_menu.h"
#include "calls/calls_instance.h"
#include "data/data_peer_values.h"
#include "data/data_folder.h"
#include "data/data_session.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "base/unixtime.h"
#include "support/support_helper.h"
#include "observer_peer.h"
#include "apiwrap.h"
#include "facades.h"
#include "styles/style_window.h"
#include "styles/style_dialogs.h"
#include "styles/style_history.h"
#include "styles/style_info.h"

namespace HistoryView {

TopBarWidget::TopBarWidget(
	QWidget *parent,
	not_null<Window::SessionController*> controller)
: RpWidget(parent)
, _controller(controller)
, _clear(this, tr::lng_selected_clear(), st::topBarClearButton)
, _forward(this, tr::lng_selected_forward(), st::defaultActiveButton)
, _sendNow(this, tr::lng_selected_send_now(), st::defaultActiveButton)
, _delete(this, tr::lng_selected_delete(), st::defaultActiveButton)
, _back(this, st::historyTopBarBack)
, _call(this, st::topBarCall)
, _search(this, st::topBarSearch)
, _infoToggle(this, st::topBarInfo)
, _menuToggle(this, st::topBarMenuToggle)
, _titlePeerText(st::windowMinWidth / 3)
, _onlineUpdater([=] { updateOnlineDisplay(); }) {
	subscribe(Lang::Current().updated(), [=] { refreshLang(); });
	setAttribute(Qt::WA_OpaquePaintEvent);

	_forward->setClickedCallback([=] { _forwardSelection.fire({}); });
	_forward->setWidthChangedCallback([=] { updateControlsGeometry(); });
	_sendNow->setClickedCallback([=] { _sendNowSelection.fire({}); });
	_sendNow->setWidthChangedCallback([=] { updateControlsGeometry(); });
	_delete->setClickedCallback([=] { _deleteSelection.fire({}); });
	_delete->setWidthChangedCallback([=] { updateControlsGeometry(); });
	_clear->setClickedCallback([=] { _clearSelection.fire({}); });
	_call->setClickedCallback([=] { onCall(); });
	_search->setClickedCallback([=] { onSearch(); });
	_menuToggle->setClickedCallback([=] { showMenu(); });
	_infoToggle->setClickedCallback([=] { toggleInfoSection(); });
	_back->addClickHandler([=] { backClicked(); });

	rpl::combine(
		_controller->activeChatValue(),
		_controller->searchInChat.value()
	) | rpl::combine_previous(
		std::make_tuple(Dialogs::Key(), Dialogs::Key())
	) | rpl::map([](
			const std::tuple<Dialogs::Key, Dialogs::Key> &previous,
			const std::tuple<Dialogs::Key, Dialogs::Key> &current) {
		const auto &active = std::get<0>(current);
		const auto &search = std::get<1>(current);
		const auto activeChanged = (active != std::get<0>(previous));
		const auto searchInChat = search && (active == search);
		return std::make_tuple(searchInChat, activeChanged);
	}) | rpl::start_with_next([=](
			bool searchInActiveChat,
			bool activeChanged) {
		auto animated = activeChanged
			? anim::type::instant
			: anim::type::normal;
		_search->setForceRippled(searchInActiveChat, animated);
	}, lifetime());

	subscribe(Adaptive::Changed(), [=] { updateAdaptiveLayout(); });
	refreshUnreadBadge();
	{
		using AnimationUpdate = Data::Session::SendActionAnimationUpdate;
		session().data().sendActionAnimationUpdated(
		) | rpl::filter([=](const AnimationUpdate &update) {
			return (update.history == _activeChat.history());
		}) | rpl::start_with_next([=] {
			update();
		}, lifetime());
	}

	using UpdateFlag = Notify::PeerUpdate::Flag;
	auto flags = UpdateFlag::UserHasCalls
		| UpdateFlag::UserOnlineChanged
		| UpdateFlag::MembersChanged
		| UpdateFlag::UserSupportInfoChanged;
	subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(flags, [=](const Notify::PeerUpdate &update) {
		if (update.flags & UpdateFlag::UserHasCalls) {
			if (update.peer->isUser()) {
				updateControlsVisibility();
			}
		} else {
			updateOnlineDisplay();
		}
	}));
	subscribe(Global::RefPhoneCallsEnabledChanged(), [this] {
		updateControlsVisibility();
	});

	rpl::combine(
		session().settings().thirdSectionInfoEnabledValue(),
		session().settings().tabbedReplacedWithInfoValue()
	) | rpl::start_with_next(
		[this] { updateInfoToggleActive(); },
		lifetime());

	rpl::single(rpl::empty_value()) | rpl::then(
		base::ObservableViewer(Global::RefConnectionTypeChanged())
	) | rpl::start_with_next(
		[=] { updateConnectingState(); },
		lifetime());

	setCursor(style::cur_pointer);
	updateControlsVisibility();
}

TopBarWidget::~TopBarWidget() = default;

Main::Session &TopBarWidget::session() const {
	return _controller->session();
}

void TopBarWidget::updateConnectingState() {
	const auto mtp = MTP::dcstate();
	if (mtp == MTP::ConnectedState) {
		if (_connecting) {
			_connecting = nullptr;
			update();
		}
	} else if (!_connecting) {
		_connecting = std::make_unique<Ui::InfiniteRadialAnimation>(
			[=] { connectingAnimationCallback(); },
			st::topBarConnectingAnimation);
		_connecting->start();
		update();
	}
}

void TopBarWidget::connectingAnimationCallback() {
	if (!anim::Disabled()) {
		update();
	}
}

void TopBarWidget::refreshLang() {
	InvokeQueued(this, [this] { updateControlsGeometry(); });
}

void TopBarWidget::onSearch() {
	if (_activeChat) {
		App::main()->searchInChat(_activeChat);
	}
}

void TopBarWidget::onCall() {
	if (const auto peer = _activeChat.peer()) {
		if (const auto user = peer->asUser()) {
			user->session().calls().startOutgoingCall(user);
		}
	}
}

void TopBarWidget::showMenu() {
	if (!_activeChat || _menu) {
		return;
	}
	_menu.create(parentWidget());
	_menu->setHiddenCallback([weak = Ui::MakeWeak(this), menu = _menu.data()]{
		menu->deleteLater();
		if (weak && weak->_menu == menu) {
			weak->_menu = nullptr;
			weak->_menuToggle->setForceRippled(false);
		}
	});
	_menu->setShowStartCallback(crl::guard(this, [this, menu = _menu.data()]{
		if (_menu == menu) {
			_menuToggle->setForceRippled(true);
		}
	}));
	_menu->setHideStartCallback(crl::guard(this, [this, menu = _menu.data()]{
		if (_menu == menu) {
			_menuToggle->setForceRippled(false);
		}
	}));
	_menuToggle->installEventFilter(_menu);
	const auto addAction = [&](
		const QString & text,
		Fn<void()> callback) {
		return _menu->addAction(text, std::move(callback));
	};
	if (const auto peer = _activeChat.peer()) {
		Window::FillPeerMenu(
			_controller,
			peer,
			addAction,
			Window::PeerMenuSource::History);
	} else if (const auto folder = _activeChat.folder()) {
		Window::FillFolderMenu(
			_controller,
			folder,
			addAction,
			Window::PeerMenuSource::History);
	} else {
		Unexpected("Empty active chat in TopBarWidget::showMenu.");
	}
	if (_menu->actions().empty()) {
		_menu.destroy();
	} else {
		_menu->moveToRight((parentWidget()->width() - width()) + st::topBarMenuPosition.x(), st::topBarMenuPosition.y());
		_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
	}
}

void TopBarWidget::toggleInfoSection() {
	if (Adaptive::ThreeColumn()
		&& (session().settings().thirdSectionInfoEnabled()
			|| session().settings().tabbedReplacedWithInfo())) {
		_controller->closeThirdSection();
	} else if (_activeChat.peer()) {
		if (_controller->canShowThirdSection()) {
			session().settings().setThirdSectionInfoEnabled(true);
			session().saveSettingsDelayed();
			if (Adaptive::ThreeColumn()) {
				_controller->showSection(
					Info::Memento::Default(_activeChat.peer()),
					Window::SectionShow().withThirdColumn());
			} else {
				_controller->resizeForThirdSection();
				_controller->updateColumnLayout();
			}
		} else {
			infoClicked();
		}
	} else {
		updateControlsVisibility();
	}
}

bool TopBarWidget::eventFilter(QObject *obj, QEvent *e) {
	if (obj == _membersShowArea) {
		switch (e->type()) {
		case QEvent::MouseButtonPress:
			mousePressEvent(static_cast<QMouseEvent*>(e));
			return true;

		case QEvent::Enter:
			_membersShowAreaActive.fire(true);
			break;

		case QEvent::Leave:
			_membersShowAreaActive.fire(false);
			break;
		}
	}
	return TWidget::eventFilter(obj, e);
}

int TopBarWidget::resizeGetHeight(int newWidth) {
	return st::topBarHeight;
}

void TopBarWidget::paintEvent(QPaintEvent *e) {
	if (_animatingMode) {
		return;
	}
	Painter p(this);

	auto hasSelected = (_selectedCount > 0);
	auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.value(hasSelected ? 1. : 0.));

	p.fillRect(QRect(0, 0, width(), st::topBarHeight), st::topBarBg);
	if (selectedButtonsTop < 0) {
		p.translate(0, selectedButtonsTop + st::topBarHeight);
		paintTopBar(p);
	}
}

void TopBarWidget::paintTopBar(Painter &p) {
	if (!_activeChat) {
		return;
	}
	auto nameleft = _leftTaken;
	auto nametop = st::topBarArrowPadding.top();
	auto statustop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height;
	auto availableWidth = width() - _rightTaken - nameleft;

	const auto history = _activeChat.history();
	const auto folder = _activeChat.folder();
	if (folder
		|| history->peer->isSelf()
		|| (_section == Section::Scheduled)) {
		// #TODO feed name emoji.
		auto text = (_section == Section::Scheduled)
			? ((history && history->peer->isSelf())
				? tr::lng_reminder_messages(tr::now)
				: tr::lng_scheduled_messages(tr::now))
			: folder
			? folder->chatListName()
			: tr::lng_saved_messages(tr::now);
		const auto textWidth = st::historySavedFont->width(text);
		if (availableWidth < textWidth) {
			text = st::historySavedFont->elided(text, availableWidth);
		}
		p.setPen(st::dialogsNameFg);
		p.setFont(st::historySavedFont);
		p.drawTextLeft(
			nameleft,
			(height() - st::historySavedFont->height) / 2,
			width(),
			text);
	} else if (const auto history = _activeChat.history()) {
		const auto peer = history->peer;
		const auto &text = peer->topBarNameText();
		const auto badgeStyle = Ui::PeerBadgeStyle{
			nullptr,
			&st::attentionButtonFg };
		const auto badgeWidth = Ui::DrawPeerBadgeGetWidth(
			peer,
			p,
			QRect(
				nameleft,
				nametop,
				availableWidth,
				st::msgNameStyle.font->height),
			text.maxWidth(),
			width(),
			badgeStyle);
		const auto namewidth = availableWidth - badgeWidth;

		p.setPen(st::dialogsNameFg);
		peer->topBarNameText().drawElided(
			p,
			nameleft,
			nametop,
			namewidth);

		p.setFont(st::dialogsTextFont);
		if (paintConnectingState(p, nameleft, statustop, width())) {
			return;
		} else if (history->paintSendAction(
				p,
				nameleft,
				statustop,
			availableWidth,
				width(),
				st::historyStatusFgTyping,
				crl::now())) {
			return;
		} else {
			paintStatus(p, nameleft, statustop, availableWidth, width());
		}
	}
}

bool TopBarWidget::paintConnectingState(
		Painter &p,
		int left,
		int top,
		int outerWidth) {
	if (!_connecting) {
		return false;
	}
	_connecting->draw(
		p,
		{
			st::topBarConnectingPosition.x() + left,
			st::topBarConnectingPosition.y() + top
		},
		outerWidth);
	left += st::topBarConnectingPosition.x()
		+ st::topBarConnectingAnimation.size.width()
		+ st::topBarConnectingSkip;
	p.setPen(st::historyStatusFg);
	p.drawTextLeft(left, top, outerWidth, tr::lng_status_connecting(tr::now));
	return true;
}

void TopBarWidget::paintStatus(
		Painter &p,
		int left,
		int top,
		int availableWidth,
		int outerWidth) {
	p.setPen(_titlePeerTextOnline
		? st::historyStatusFgActive
		: st::historyStatusFg);
	_titlePeerText.drawLeftElided(p, left, top, availableWidth, outerWidth);
}

QRect TopBarWidget::getMembersShowAreaGeometry() const {
	int membersTextLeft = _leftTaken;
	int membersTextTop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height;
	int membersTextWidth = _titlePeerText.maxWidth();
	int membersTextHeight = st::topBarHeight - membersTextTop;

	return myrtlrect(membersTextLeft, membersTextTop, membersTextWidth, membersTextHeight);
}

void TopBarWidget::mousePressEvent(QMouseEvent *e) {
	auto handleClick = (e->button() == Qt::LeftButton)
		&& (e->pos().y() < st::topBarHeight)
		&& (!_selectedCount);
	if (handleClick) {
		if (_animatingMode && _back->rect().contains(e->pos())) {
			backClicked();
		} else  {
			infoClicked();
		}
	}
}

void TopBarWidget::infoClicked() {
	if (!_activeChat) {
		return;
	} else if (_activeChat.folder()) {
		_controller->closeFolder();
	//} else if (const auto feed = _activeChat.feed()) { // #feed
	//	_controller->showSection(Info::Memento(
	//		feed,
	//		Info::Section(Info::Section::Type::Profile)));
	} else if (_activeChat.peer()->isSelf()) {
		_controller->showSection(Info::Memento(
			_activeChat.peer()->id,
			Info::Section(Storage::SharedMediaType::Photo)));
	} else {
		_controller->showPeerInfo(_activeChat.peer());
	}
}

void TopBarWidget::backClicked() {
	if (_activeChat.folder()) {
		_controller->closeFolder();
	} else {
		_controller->showBackFromStack();
	}
}

void TopBarWidget::setActiveChat(Dialogs::Key chat, Section section) {
	if (_activeChat == chat && _section == section) {
		return;
	}
	_activeChat = chat;
	_section = section;
	_back->clearState();
	update();

	updateUnreadBadge();
	refreshInfoButton();
	if (_menu) {
		_menuToggle->removeEventFilter(_menu);
		_menu->hideFast();
	}
	updateOnlineDisplay();
	updateControlsVisibility();
	refreshUnreadBadge();
}

void TopBarWidget::refreshInfoButton() {
	if (const auto peer = _activeChat.peer()) {
		auto info = object_ptr<Ui::UserpicButton>(
			this,
			_controller,
			peer,
			Ui::UserpicButton::Role::Custom,
			st::topBarInfoButton);
		info->showSavedMessagesOnSelf(true);
		_info.destroy();
		_info = std::move(info);
	//} else if (const auto feed = _activeChat.feed()) { // #feed
	//	_info.destroy();
	//	_info = object_ptr<Ui::FeedUserpicButton>(
	//		this,
	//		_controller,
	//		feed,
	//		st::topBarFeedInfoButton);
	}
	if (_info) {
		_info->setAttribute(Qt::WA_TransparentForMouseEvents);
	}
}

void TopBarWidget::resizeEvent(QResizeEvent *e) {
	updateSearchVisibility();
	updateControlsGeometry();
}

int TopBarWidget::countSelectedButtonsTop(float64 selectedShown) {
	return (1. - selectedShown) * (-st::topBarHeight);
}

void TopBarWidget::updateSearchVisibility() {
	const auto historyMode = (_section == Section::History);
	const auto smallDialogsColumn = _activeChat.folder()
		&& (width() < _back->width() + _search->width());
	_search->setVisible(historyMode && !smallDialogsColumn);
}

void TopBarWidget::updateControlsGeometry() {
	auto hasSelected = (_selectedCount > 0);
	auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.value(hasSelected ? 1. : 0.));
	auto otherButtonsTop = selectedButtonsTop + st::topBarHeight;
	auto buttonsLeft = st::topBarActionSkip + (Adaptive::OneColumn() ? 0 : st::lineWidth);
	auto buttonsWidth = (_forward->isHidden() ? 0 : _forward->contentWidth())
		+ (_sendNow->isHidden() ? 0 : _sendNow->contentWidth())
		+ (_delete->isHidden() ? 0 : _delete->contentWidth())
		+ _clear->width();
	buttonsWidth += buttonsLeft + st::topBarActionSkip * 3;

	auto widthLeft = qMin(width() - buttonsWidth, -2 * st::defaultActiveButton.width);
	auto buttonFullWidth = qMin(-(widthLeft / 2), 0);
	_forward->setFullWidth(buttonFullWidth);
	_sendNow->setFullWidth(buttonFullWidth);
	_delete->setFullWidth(buttonFullWidth);

	selectedButtonsTop += (height() - _forward->height()) / 2;

	_forward->moveToLeft(buttonsLeft, selectedButtonsTop);
	if (!_forward->isHidden()) {
		buttonsLeft += _forward->width() + st::topBarActionSkip;
	}

	_sendNow->moveToLeft(buttonsLeft, selectedButtonsTop);
	if (!_sendNow->isHidden()) {
		buttonsLeft += _sendNow->width() + st::topBarActionSkip;
	}

	_delete->moveToLeft(buttonsLeft, selectedButtonsTop);
	_clear->moveToRight(st::topBarActionSkip, selectedButtonsTop);

	if (_back->isHidden()) {
		_leftTaken = st::topBarArrowPadding.right();
	} else {
		const auto smallDialogsColumn = _activeChat.folder()
			&& (width() < _back->width() + _search->width());
		_leftTaken = smallDialogsColumn ? (width() - _back->width()) / 2 : 0;
		_back->moveToLeft(_leftTaken, otherButtonsTop);
		_leftTaken += _back->width();
		if (_info && !_info->isHidden()) {
			_info->moveToLeft(_leftTaken, otherButtonsTop);
			_leftTaken += _info->width();
		}
	}

	_rightTaken = 0;
	_menuToggle->moveToRight(_rightTaken, otherButtonsTop);
	if (_menuToggle->isHidden()) {
		_rightTaken += (_menuToggle->width() - _search->width());
	} else {
		_rightTaken += _menuToggle->width() + st::topBarSkip;
	}
	_infoToggle->moveToRight(_rightTaken, otherButtonsTop);
	if (!_infoToggle->isHidden()) {
		_rightTaken += _infoToggle->width() + st::topBarSkip;
	}
	_search->moveToRight(_rightTaken, otherButtonsTop);
	_rightTaken += _search->width() + st::topBarCallSkip;
	_call->moveToRight(_rightTaken, otherButtonsTop);
	_rightTaken += _call->width();

	updateMembersShowArea();
}

void TopBarWidget::finishAnimating() {
	_selectedShown.stop();
	updateControlsVisibility();
	update();
}

void TopBarWidget::setAnimatingMode(bool enabled) {
	if (_animatingMode != enabled) {
		_animatingMode = enabled;
		setAttribute(Qt::WA_OpaquePaintEvent, !_animatingMode);
		finishAnimating();
	}
}

void TopBarWidget::updateControlsVisibility() {
	if (_animatingMode) {
		hideChildren();
		return;
	}
	_clear->show();
	_delete->setVisible(_canDelete);
	_forward->setVisible(_canForward);
	_sendNow->setVisible(_canSendNow);

	auto backVisible = Adaptive::OneColumn()
		|| (App::main() && !App::main()->stackIsEmpty())
		|| _activeChat.folder();
	_back->setVisible(backVisible);
	if (_info) {
		_info->setVisible(Adaptive::OneColumn());
	}
	if (_unreadBadge) {
		_unreadBadge->show();
	}
	const auto historyMode = (_section == Section::History);
	updateSearchVisibility();
	_menuToggle->setVisible(historyMode && !_activeChat.folder());
	_infoToggle->setVisible(historyMode
		&& !_activeChat.folder()
		&& !Adaptive::OneColumn()
		&& _controller->canShowThirdSection());
	const auto callsEnabled = [&] {
		if (const auto peer = _activeChat.peer()) {
			if (const auto user = peer->asUser()) {
				return Global::PhoneCallsEnabled() && user->hasCalls();
			}
		}
		return false;
	}();
	_call->setVisible(historyMode && callsEnabled);

	if (_membersShowArea) {
		_membersShowArea->show();
	}
	updateControlsGeometry();
}

void TopBarWidget::updateMembersShowArea() {
	if (!App::main()) {
		return;
	}
	auto membersShowAreaNeeded = [this]() {
		auto peer = App::main()->peer();
		if ((_selectedCount > 0) || !peer) {
			return false;
		}
		if (auto chat = peer->asChat()) {
			return chat->amIn();
		}
		if (auto megagroup = peer->asMegagroup()) {
			return megagroup->canViewMembers() && (megagroup->membersCount() < Global::ChatSizeMax());
		}
		return false;
	};
	if (!membersShowAreaNeeded()) {
		if (_membersShowArea) {
			_membersShowAreaActive.fire(false);
			_membersShowArea.destroy();
		}
		return;
	} else if (!_membersShowArea) {
		_membersShowArea.create(this);
		_membersShowArea->show();
		_membersShowArea->installEventFilter(this);
	}
	_membersShowArea->setGeometry(getMembersShowAreaGeometry());
}

void TopBarWidget::showSelected(SelectedState state) {
	auto canDelete = (state.count > 0 && state.count == state.canDeleteCount);
	auto canForward = (state.count > 0 && state.count == state.canForwardCount);
	auto canSendNow = (state.count > 0 && state.count == state.canSendNowCount);
	if (_selectedCount == state.count && _canDelete == canDelete && _canForward == canForward && _canSendNow == canSendNow) {
		return;
	}
	if (state.count == 0) {
		// Don't change the visible buttons if the selection is cancelled.
		canDelete = _canDelete;
		canForward = _canForward;
		canSendNow = _canSendNow;
	}

	auto wasSelected = (_selectedCount > 0);
	_selectedCount = state.count;
	if (_selectedCount > 0) {
		_forward->setNumbersText(_selectedCount);
		_sendNow->setNumbersText(_selectedCount);
		_delete->setNumbersText(_selectedCount);
		if (!wasSelected) {
			_forward->finishNumbersAnimation();
			_sendNow->finishNumbersAnimation();
			_delete->finishNumbersAnimation();
		}
	}
	auto hasSelected = (_selectedCount > 0);
	if (_canDelete != canDelete || _canForward != canForward || _canSendNow != canSendNow) {
		_canDelete = canDelete;
		_canForward = canForward;
		_canSendNow = canSendNow;
		updateControlsVisibility();
	}
	if (wasSelected != hasSelected) {
		setCursor(hasSelected ? style::cur_default : style::cur_pointer);

		updateMembersShowArea();
		_selectedShown.start(
			[this] { selectedShowCallback(); },
			hasSelected ? 0. : 1.,
			hasSelected ? 1. : 0.,
			st::slideWrapDuration,
			anim::easeOutCirc);
	} else {
		updateControlsGeometry();
	}
}

void TopBarWidget::selectedShowCallback() {
	updateControlsGeometry();
	update();
}

void TopBarWidget::updateAdaptiveLayout() {
	updateControlsVisibility();
	updateInfoToggleActive();
	refreshUnreadBadge();
}

void TopBarWidget::refreshUnreadBadge() {
	if (!Adaptive::OneColumn() && !_activeChat.folder()) {
		if (_unreadBadge) {
			unsubscribe(base::take(_unreadCounterSubscription));
			_unreadBadge.destroy();
		}
		return;
	} else if (_unreadBadge) {
		return;
	}
	_unreadBadge.create(this);

	rpl::combine(
		_back->geometryValue(),
		_unreadBadge->widthValue()
	) | rpl::start_with_next([=](QRect geometry, int width) {
		_unreadBadge->move(
			geometry.x() + geometry.width() - width,
			geometry.y() + st::titleUnreadCounterTop);
	}, _unreadBadge->lifetime());

	_unreadBadge->show();
	_unreadBadge->setAttribute(Qt::WA_TransparentForMouseEvents);
	_unreadCounterSubscription = subscribe(
		Global::RefUnreadCounterUpdate(),
		[=] { updateUnreadBadge(); });
	updateUnreadBadge();
}

void TopBarWidget::updateUnreadBadge() {
	if (!_unreadBadge) return;

	const auto muted = session().data().unreadBadgeMutedIgnoreOne(_activeChat);
	const auto counter = session().data().unreadBadgeIgnoreOne(_activeChat);
	const auto text = [&] {
		if (!counter) {
			return QString();
		}
		return (counter > 999)
			? qsl("..%1").arg(counter % 100, 2, 10, QChar('0'))
			: QString::number(counter);
	}();
	_unreadBadge->setText(text, !muted);
}

void TopBarWidget::updateInfoToggleActive() {
	auto infoThirdActive = Adaptive::ThreeColumn()
		&& (session().settings().thirdSectionInfoEnabled()
			|| session().settings().tabbedReplacedWithInfo());
	auto iconOverride = infoThirdActive
		? &st::topBarInfoActive
		: nullptr;
	auto rippleOverride = infoThirdActive
		? &st::lightButtonBgOver
		: nullptr;
	_infoToggle->setIconOverride(iconOverride, iconOverride);
	_infoToggle->setRippleColorOverride(rippleOverride);
}

void TopBarWidget::updateOnlineDisplay() {
	if (!_activeChat.peer()) return;

	QString text;
	const auto now = base::unixtime::now();
	bool titlePeerTextOnline = false;
	if (const auto user = _activeChat.peer()->asUser()) {
		if (session().supportMode()
			&& !session().supportHelper().infoCurrent(user).text.empty()) {
			text = QString::fromUtf8("\xe2\x9a\xa0\xef\xb8\x8f check info");
			titlePeerTextOnline = false;
		} else {
			text = Data::OnlineText(user, now);
			titlePeerTextOnline = Data::OnlineTextActive(user, now);
		}
	} else if (const auto chat = _activeChat.peer()->asChat()) {
		if (!chat->amIn()) {
			text = tr::lng_chat_status_unaccessible(tr::now);
		} else if (chat->participants.empty()) {
			if (!_titlePeerText.isEmpty()) {
				text = _titlePeerText.toString();
			} else if (chat->count <= 0) {
				text = tr::lng_group_status(tr::now);
			} else {
				text = tr::lng_chat_status_members(tr::now, lt_count_decimal, chat->count);
			}
		} else {
			const auto self = session().user();
			auto online = 0;
			auto onlyMe = true;
			for (const auto user : chat->participants) {
				if (user->onlineTill > now) {
					++online;
					if (onlyMe && user != self) onlyMe = false;
				}
			}
			if (online > 0 && !onlyMe) {
				auto membersCount = tr::lng_chat_status_members(tr::now, lt_count_decimal, chat->participants.size());
				auto onlineCount = tr::lng_chat_status_online(tr::now, lt_count, online);
				text = tr::lng_chat_status_members_online(tr::now, lt_members_count, membersCount, lt_online_count, onlineCount);
			} else if (chat->participants.size() > 0) {
				text = tr::lng_chat_status_members(tr::now, lt_count_decimal, chat->participants.size());
			} else {
				text = tr::lng_group_status(tr::now);
			}
		}
	} else if (const auto channel = _activeChat.peer()->asChannel()) {
		if (channel->isMegagroup() && channel->membersCount() > 0 && channel->membersCount() <= Global::ChatSizeMax()) {
			if (channel->mgInfo->lastParticipants.empty() || channel->lastParticipantsCountOutdated()) {
				session().api().requestLastParticipants(channel);
			}
			const auto self = session().user();
			auto online = 0;
			auto onlyMe = true;
			for (auto &participant : std::as_const(channel->mgInfo->lastParticipants)) {
				if (participant->onlineTill > now) {
					++online;
					if (onlyMe && participant != self) {
						onlyMe = false;
					}
				}
			}
			if (online && !onlyMe) {
				auto membersCount = tr::lng_chat_status_members(tr::now, lt_count_decimal, channel->membersCount());
				auto onlineCount = tr::lng_chat_status_online(tr::now, lt_count, online);
				text = tr::lng_chat_status_members_online(tr::now, lt_members_count, membersCount, lt_online_count, onlineCount);
			} else if (channel->membersCount() > 0) {
				text = tr::lng_chat_status_members(tr::now, lt_count_decimal, channel->membersCount());
			} else {
				text = tr::lng_group_status(tr::now);
			}
		} else if (channel->membersCount() > 0) {
			text = tr::lng_chat_status_members(tr::now, lt_count_decimal, channel->membersCount());

		} else {
			text = channel->isMegagroup() ? tr::lng_group_status(tr::now) : tr::lng_channel_status(tr::now);
		}
	}
	if (_titlePeerText.toString() != text) {
		_titlePeerText.setText(st::dialogsTextStyle, text);
		_titlePeerTextOnline = titlePeerTextOnline;
		updateMembersShowArea();
		update();
	}
	updateOnlineDisplayTimer();
}

void TopBarWidget::updateOnlineDisplayTimer() {
	if (!_activeChat.peer()) return;

	const auto now = base::unixtime::now();
	auto minTimeout = crl::time(86400);
	const auto handleUser = [&](not_null<UserData*> user) {
		auto hisTimeout = Data::OnlineChangeTimeout(user, now);
		accumulate_min(minTimeout, hisTimeout);
	};
	if (const auto user = _activeChat.peer()->asUser()) {
		handleUser(user);
	} else if (auto chat = _activeChat.peer()->asChat()) {
		for (const auto user : chat->participants) {
			handleUser(user);
		}
	} else if (_activeChat.peer()->isChannel()) {
	}
	updateOnlineDisplayIn(minTimeout);
}

void TopBarWidget::updateOnlineDisplayIn(crl::time timeout) {
	_onlineUpdater.callOnce(timeout);
}

} // namespace HistoryView
