/*
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.

In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.

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

#include "styles/style_profile.h"
#include "styles/style_boxes.h"
#include "ui/widgets/buttons.h"
#include "boxes/confirm_box.h"
#include "boxes/report_box.h"
#include "mainwidget.h"
#include "observer_peer.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "lang/lang_keys.h"
#include "profile/profile_channel_controllers.h"

namespace Profile {

constexpr auto kEnableSearchMembersAfterCount = 50;

using UpdateFlag = Notify::PeerUpdate::Flag;

ActionsWidget::ActionsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_actions_section)) {
	auto observeEvents = UpdateFlag::ChannelAmIn
		| UpdateFlag::UserIsBlocked
		| UpdateFlag::BotCommandsChanged
		| UpdateFlag::MembersChanged;
	subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
		notifyPeerUpdated(update);
	}));

	validateBlockStatus();
	refreshButtons();
}

void ActionsWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
	if (update.peer != peer()) {
		return;
	}

	auto needFullRefresh = [&update, this]() {
		if (update.flags & UpdateFlag::BotCommandsChanged) {
			if (_hasBotHelp != hasBotCommand(qsl("help")) || _hasBotSettings != hasBotCommand(qsl("settings"))) {
				return true;
			}
		}
		return false;
	};
	if (needFullRefresh()) {
		refreshButtons();
	} else {
		if (update.flags & UpdateFlag::MembersChanged) {
			refreshDeleteChannel();
		}
		if (update.flags & UpdateFlag::ChannelAmIn) {
			refreshLeaveChannel();
		}
		if (update.flags & UpdateFlag::UserIsBlocked) {
			refreshBlockUser();
		}
		refreshVisibility();
	}

	contentSizeUpdated();
}

void ActionsWidget::validateBlockStatus() const {
	auto needFullPeer = [this]() {
		if (auto user = peer()->asUser()) {
			if (user->blockStatus() == UserData::BlockStatus::Unknown) {
				return true;
			} else if (user->botInfo && !user->botInfo->inited) {
				return true;
			}
		}
		return false;
	};
	if (needFullPeer()) {
		if (App::api()) App::api()->requestFullPeer(peer());
	}
}

Ui::LeftOutlineButton *ActionsWidget::addButton(const QString &text, const char *slot, const style::OutlineButton &st, int skipHeight) {
	auto result = new Ui::LeftOutlineButton(this, text, st);
	connect(result, SIGNAL(clicked()), this, slot);
	result->show();

	int top = buttonsBottom() + skipHeight;
	resizeButton(result, width(), top);

	_buttons.push_back(result);
	return result;
};

void ActionsWidget::resizeButton(Ui::LeftOutlineButton *button, int newWidth, int top) {
	int left = defaultOutlineButtonLeft();
	int availableWidth = newWidth - left - st::profileBlockMarginRight;
	accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
	button->resizeToWidth(availableWidth);
	button->moveToLeft(left, top);
}

void ActionsWidget::refreshButtons() {
	auto buttons = base::take(_buttons);
	for_const (auto &button, buttons) {
		delete button;
	}
	_blockUser = _leaveChannel = nullptr;

	if (auto user = peer()->asUser()) {
		if ((_hasBotHelp = hasBotCommand(qsl("help")))) {
			addButton(lang(lng_profile_bot_help), SLOT(onBotHelp()));
		}
		if ((_hasBotSettings = hasBotCommand(qsl("settings")))) {
			addButton(lang(lng_profile_bot_settings), SLOT(onBotSettings()));
		}
		addButton(lang(lng_profile_clear_history), SLOT(onClearHistory()));
		addButton(lang(lng_profile_delete_conversation), SLOT(onDeleteConversation()));
		if (user->botInfo) {
			addButton(lang(lng_profile_report), SLOT(onReport()), st::defaultLeftOutlineButton, st::profileBlockOneLineSkip);
		}
		refreshBlockUser();
	} else if (auto chat = peer()->asChat()) {
		if (chat->amCreator()) {
			addButton(lang(lng_profile_migrate_button), SLOT(onUpgradeToSupergroup()));
		}
		addButton(lang(lng_profile_clear_history), SLOT(onClearHistory()));
		addButton(lang(lng_profile_clear_and_exit), SLOT(onDeleteConversation()));
	} else if (auto channel = peer()->asChannel()) {
		if (channel->isMegagroup() && channel->membersCount() > kEnableSearchMembersAfterCount) {
			addButton(lang(lng_profile_search_members), SLOT(onSearchMembers()));
		}
		if (!channel->amCreator() && (!channel->isMegagroup() || channel->isPublic())) {
			addButton(lang(lng_profile_report), SLOT(onReport()));
		}
		refreshDeleteChannel();
		refreshLeaveChannel();
	}

	refreshVisibility();
}

void ActionsWidget::refreshVisibility() {
	setVisible(!_buttons.isEmpty());
}

QString ActionsWidget::getBlockButtonText() const {
	auto user = peer()->asUser();
	if (!user || (user->id == AuthSession::CurrentUserPeerId())) return QString();
	if (user->blockStatus() == UserData::BlockStatus::Unknown) return QString();

	if (user->isBlocked()) {
		if (user->botInfo) {
			return lang(lng_profile_unblock_bot);
		}
		return lang(lng_profile_unblock_user);
	} else if (user->botInfo) {
		return lang(lng_profile_block_bot);
	}
	return lang(lng_profile_block_user);
}

bool ActionsWidget::hasBotCommand(const QString &command) const {
	auto user = peer()->asUser();
	if (!user || !user->botInfo || user->botInfo->commands.isEmpty()) {
		return false;
	}

	for_const (auto &cmd, user->botInfo->commands) {
		if (!cmd.command.compare(command, Qt::CaseInsensitive)) {
			return true;
		}
	}
	return false;
}

void ActionsWidget::sendBotCommand(const QString &command) {
	auto user = peer()->asUser();
	if (user && user->botInfo && !user->botInfo->commands.isEmpty()) {
		for_const (auto &cmd, user->botInfo->commands) {
			if (!cmd.command.compare(command, Qt::CaseInsensitive)) {
				Ui::showPeerHistory(user, ShowAtTheEndMsgId);
				App::sendBotCommand(user, user, '/' + cmd.command);
				return;
			}
		}
	}

	// Command was not found.
	refreshButtons();
	contentSizeUpdated();
}

void ActionsWidget::refreshBlockUser() {
	if (auto user = peer()->asUser()) {
		auto blockText = getBlockButtonText();
		if (_blockUser) {
			if (blockText.isEmpty()) {
				_buttons.removeOne(_blockUser);
				delete _blockUser;
				_blockUser = nullptr;
			} else {
				_blockUser->setText(blockText);
			}
		} else if (!blockText.isEmpty()) {
			_blockUser = addButton(blockText, SLOT(onBlockUser()), st::attentionLeftOutlineButton, st::profileBlockOneLineSkip);
		}
	}
}

void ActionsWidget::refreshDeleteChannel() {
	if (auto channel = peer()->asChannel()) {
		if (channel->canDelete() && !_deleteChannel) {
			_deleteChannel = addButton(lang(channel->isMegagroup() ? lng_profile_delete_group : lng_profile_delete_channel), SLOT(onDeleteChannel()), st::attentionLeftOutlineButton);
		} else if (!channel->canDelete() && _deleteChannel) {
			_buttons.removeOne(_deleteChannel);
			delete _deleteChannel;
			_deleteChannel = nullptr;
		}
	}
}

void ActionsWidget::refreshLeaveChannel() {
	if (auto channel = peer()->asChannel()) {
		if (!channel->amCreator()) {
			if (channel->amIn() && !_leaveChannel) {
				_leaveChannel = addButton(lang(channel->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel), SLOT(onLeaveChannel()));
			} else if (!channel->amIn() && _leaveChannel) {
				_buttons.removeOne(_leaveChannel);
				delete _leaveChannel;
				_leaveChannel = nullptr;
			}
		}
	}
}

int ActionsWidget::resizeGetHeight(int newWidth) {
	for_const (auto button, _buttons) {
		resizeButton(button, newWidth, button->y());
	}
	return buttonsBottom();
}

int ActionsWidget::buttonsBottom() const {
	if (_buttons.isEmpty()) {
		return contentTop();
	}
	auto lastButton = _buttons.back();
	return lastButton->y() + lastButton->height();
}

void ActionsWidget::onBotHelp() {
	sendBotCommand(qsl("help"));
}

void ActionsWidget::onBotSettings() {
	sendBotCommand(qsl("settings"));
}

void ActionsWidget::onClearHistory() {
	QString confirmation;
	if (auto user = peer()->asUser()) {
		confirmation = lng_sure_delete_history(lt_contact, App::peerName(peer()));
	} else if (auto chat = peer()->asChat()) {
		confirmation = lng_sure_delete_group_history(lt_group, App::peerName(peer()));
	}
	if (!confirmation.isEmpty()) {
		Ui::show(Box<ConfirmBox>(confirmation, lang(lng_box_delete), st::attentionBoxButton, base::lambda_guarded(this, [this] {
			Ui::hideLayer();
			App::main()->clearHistory(peer());
			Ui::showPeerHistory(peer(), ShowAtUnreadMsgId);
		})));
	}
}

void ActionsWidget::onDeleteConversation() {
	QString confirmation, confirmButton;
	if (auto user = peer()->asUser()) {
		confirmation = lng_sure_delete_history(lt_contact, App::peerName(peer()));
		confirmButton = lang(lng_box_delete);
	} else if (auto chat = peer()->asChat()) {
		confirmation = lng_sure_delete_and_exit(lt_group, App::peerName(peer()));
		confirmButton = lang(lng_box_leave);
	}
	if (!confirmation.isEmpty()) {
		Ui::show(Box<ConfirmBox>(confirmation, confirmButton, st::attentionBoxButton, base::lambda_guarded(this, [this] {
			Ui::hideLayer();
			Ui::showChatsList();
			if (auto user = peer()->asUser()) {
				App::main()->deleteConversation(peer());
			} else if (auto chat = peer()->asChat()) {
				App::main()->deleteAndExit(chat);
			}
		})));
	}
}

void ActionsWidget::onBlockUser() {
	if (auto user = peer()->asUser()) {
		if (user->isBlocked()) {
			App::api()->unblockUser(user);
		} else {
			App::api()->blockUser(user);
		}
	}
}

void ActionsWidget::onUpgradeToSupergroup() {
	if (auto chat = peer()->asChat()) {
		Ui::show(Box<ConvertToSupergroupBox>(chat));
	}
}

void ActionsWidget::onDeleteChannel() {
	auto text = lang(peer()->isMegagroup() ? lng_sure_delete_group : lng_sure_delete_channel);
	Ui::show(Box<ConfirmBox>(text, lang(lng_box_delete), st::attentionBoxButton, base::lambda_guarded(this, [this] {
		Ui::hideLayer();
		Ui::showChatsList();
		if (auto chat = peer()->migrateFrom()) {
			App::main()->deleteAndExit(chat);
		}
		if (auto channel = peer()->asChannel()) {
			MTP::send(MTPchannels_DeleteChannel(channel->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::deleteChannelFailed));
		}
	})));
}

void ActionsWidget::onLeaveChannel() {
	auto channel = peer()->asChannel();
	if (!channel) return;

	auto text = lang(channel->isMegagroup() ? lng_sure_leave_group : lng_sure_leave_channel);
	Ui::show(Box<ConfirmBox>(text, lang(lng_box_leave), base::lambda_guarded(this, [this] {
		App::api()->leaveChannel(peer()->asChannel());
	})));
}

void ActionsWidget::onSearchMembers() {
	if (auto channel = peer()->asChannel()) {
		ParticipantsBoxController::Start(channel, ParticipantsBoxController::Role::Members);
	}
}

void ActionsWidget::onReport() {
	Ui::show(Box<ReportBox>(peer()));
}

} // namespace Profile