/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "profile/profile_inner_widget.h"

#include "styles/style_profile.h"
#include "styles/style_window.h"
#include "profile/profile_cover.h"
#include "profile/profile_info_widget.h"
#include "profile/profile_settings_widget.h"
#include "profile/profile_invite_link_widget.h"
#include "profile/profile_shared_media_widget.h"
#include "profile/profile_actions_widget.h"
#include "profile/profile_members_widget.h"
#include "apiwrap.h"

namespace Profile {

InnerWidget::InnerWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
, _peer(peer)
, _cover(this, peer) {
	setAttribute(Qt::WA_OpaquePaintEvent);

	createBlocks();
}

void InnerWidget::createBlocks() {
	auto user = _peer->asUser();
	auto chat = _peer->asChat();
	auto channel = _peer->asChannel();
	auto megagroup = _peer->isMegagroup() ? channel : nullptr;
	if (user || channel || megagroup) {
		_blocks.push_back({ new InfoWidget(this, _peer), BlockSide::Right });
	}
	_blocks.push_back({ new SettingsWidget(this, _peer), BlockSide::Right });
	if (chat || channel || megagroup) {
		_blocks.push_back({ new InviteLinkWidget(this, _peer), BlockSide::Right });
	}
	_blocks.push_back({ new SharedMediaWidget(this, _peer), BlockSide::Right });
	if (channel && !megagroup) {
		_blocks.push_back({ new ChannelMembersWidget(this, _peer), BlockSide::Right });
	}
	_blocks.push_back({ new ActionsWidget(this, _peer), BlockSide::Right });
	if (chat || megagroup) {
		auto membersWidget = new MembersWidget(this, _peer);
		connect(membersWidget, SIGNAL(onlineCountUpdated(int)), _cover, SLOT(onOnlineCountUpdated(int)));
		_cover->onOnlineCountUpdated(membersWidget->onlineCount());
		_blocks.push_back({ membersWidget, BlockSide::Left });
	}
	for_const (auto &blockData, _blocks) {
		connect(blockData.block, SIGNAL(heightUpdated()), this, SLOT(onBlockHeightUpdated()));
	}
}

void InnerWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
	_visibleTop = visibleTop;
	_visibleBottom = visibleBottom;

	int notDisplayedAtBottom = height() - _visibleBottom;
	if (notDisplayedAtBottom > 0) {
		decreaseAdditionalHeight(notDisplayedAtBottom);
	}

	for_const (auto &blockData, _blocks) {
		int blockY = blockData.block->y();
		blockData.block->setVisibleTopBottom(visibleTop - blockY, visibleBottom - blockY);
	}
}

bool InnerWidget::shareContactButtonShown() const {
	return _cover->shareContactButtonShown();
}

void InnerWidget::showFinished() {
	_cover->showFinished();
}

void InnerWidget::decreaseAdditionalHeight(int removeHeight) {
	resizeToWidth(width(), height() - removeHeight);
}

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

	p.fillRect(e->rect(), st::profileBg);

	if (_mode == Mode::TwoColumn) {
		int leftHeight = countBlocksHeight(BlockSide::Left);
		int rightHeight = countBlocksHeight(BlockSide::Right);
		int shadowHeight = rightHeight;// qMin(leftHeight, rightHeight);

		int shadowLeft = _blocksLeft + _leftColumnWidth + _columnDivider;
		int shadowTop = _blocksTop + st::profileBlockMarginTop;
		p.fillRect(rtlrect(shadowLeft, shadowTop, st::lineWidth, shadowHeight - st::profileBlockMarginTop, width()), st::shadowColor);
	}
}

void InnerWidget::keyPressEvent(QKeyEvent *e) {
	if (e->key() == Qt::Key_Escape) {
		emit cancelled();
	}
}

int InnerWidget::countBlocksHeight(BlockSide countSide) const {
	int result = 0;
	for_const (auto &blockData, _blocks) {
		if (blockData.side != countSide || blockData.block->isHidden()) {
			continue;
		}

		result += blockData.block->height();
	}
	return result;
}

int InnerWidget::countBlocksLeft(int newWidth) const {
	int result = st::profileBlockLeftMin;
	result += (newWidth - st::windowMinWidth) / 2;
	return qMin(result, st::profileBlockLeftMax);
}

InnerWidget::Mode InnerWidget::countBlocksMode(int newWidth) const {
	bool hasLeftWidget = false, hasRightWidget = false;
	for_const (auto &blockData, _blocks) {
		if (!blockData.block->isHidden()) {
			if (blockData.side == BlockSide::Left) {
				hasLeftWidget = true;
			} else {
				hasRightWidget = true;
			}
		}
	}
	if (!hasLeftWidget || !hasRightWidget) {
		return Mode::OneColumn;
	}

	int availWidth = newWidth - _blocksLeft;
	if (availWidth >= st::profileBlockWideWidthMin + _columnDivider + st::profileBlockNarrowWidthMin) {
		return Mode::TwoColumn;
	}
	return Mode::OneColumn;
}

int InnerWidget::countLeftColumnWidth(int newWidth) const {
	int result = st::profileBlockWideWidthMin;

	int availWidth = newWidth - _blocksLeft;
	int additionalWidth = (availWidth - st::profileBlockWideWidthMin - _columnDivider - st::profileBlockNarrowWidthMin);
	if (additionalWidth > 0) {
		result += (additionalWidth / 2);
		accumulate_min(result, st::profileBlockWideWidthMax);
	}
	return result;
}

void InnerWidget::refreshBlocksPositions() {
	auto layoutBlocks = [this](BlockSide layoutSide, int left) {
		int top = _blocksTop;
		for_const (auto &blockData, _blocks) {
			if (_mode == Mode::TwoColumn && blockData.side != layoutSide) {
				continue;
			}
			if (blockData.block->isHidden()) {
				continue;
			}
			blockData.block->moveToLeft(left, top);
			top += blockData.block->height();
		}
	};
	layoutBlocks(BlockSide::Left, _blocksLeft);
	if (_mode == Mode::TwoColumn) {
		layoutBlocks(BlockSide::Right, _blocksLeft + _leftColumnWidth + _columnDivider);
	}
}

void InnerWidget::resizeBlocks(int newWidth) {
	for_const (auto &blockData, _blocks) {
		int blockWidth = newWidth - _blocksLeft;
		if (_mode == Mode::OneColumn) {
			blockWidth -= _blocksLeft;
		} else {
			if (blockData.side == BlockSide::Left) {
				blockWidth = _leftColumnWidth;
			} else {
				blockWidth -= _leftColumnWidth + _columnDivider;
			}
		}
		blockData.block->resizeToWidth(blockWidth);
	}
}

int InnerWidget::resizeGetHeight(int newWidth) {
	_cover->resizeToWidth(newWidth);

	_blocksTop = _cover->y() + _cover->height() + st::profileBlocksTop;
	_blocksLeft = countBlocksLeft(newWidth);
	_columnDivider = st::profileMemberPaddingLeft;
	_mode = countBlocksMode(newWidth);
	_leftColumnWidth = countLeftColumnWidth(newWidth);
	resizeBlocks(newWidth);

	refreshBlocksPositions();

	update();
	auto naturalHeight = countHeight();

	_addedHeight = qMax(_minHeight - naturalHeight, 0);
	return naturalHeight + _addedHeight;
}

int InnerWidget::countHeight() const {
	int newHeight = _cover->height();
	int leftHeight = countBlocksHeight(BlockSide::Left);
	int rightHeight = countBlocksHeight(BlockSide::Right);

	int blocksHeight = (_mode == Mode::OneColumn) ? (leftHeight + rightHeight) : qMax(leftHeight, rightHeight);
	newHeight += st::profileBlocksTop + blocksHeight + st::profileBlocksBottom;

	return newHeight;
}

void InnerWidget::onBlockHeightUpdated() {
	refreshBlocksPositions();

	int naturalHeight = countHeight();
	int notDisplayedAtBottom = naturalHeight - _visibleBottom;
	if (notDisplayedAtBottom < 0) {
		_addedHeight = -notDisplayedAtBottom;
	} else {
		_addedHeight = 0;
	}
	if (naturalHeight + _addedHeight != height()) {
		resize(width(), naturalHeight + _addedHeight);
	}
}

} // namespace Profile