/*
 * Copyright (c) 2010 Isode Limited.
 * All rights reserved.
 * See the COPYING file for more information.
 */

#include "QtStatusWidget.h"

#include <algorithm>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

#include <QBoxLayout>
#include <QComboBox>
#include <QLabel>
#include <QFrame>
#include <QPoint>
#include <QStackedWidget>
#include <QApplication>
#include <QDesktopWidget>
#include <qdebug.h>
#include <QListWidget>
#include <QListWidgetItem>
#include <QMovie>

#include "Swift/QtUI/QtElidingLabel.h"
#include "Swift/QtUI/QtLineEdit.h"
#include "Swift/QtUI/QtSwiftUtil.h"
#include <Swift/Controllers/StatusUtil.h>
#include <Swift/Controllers/StatusCache.h>

namespace lambda = boost::lambda;

namespace Swift {

QtStatusWidget::QtStatusWidget(StatusCache* statusCache, QWidget *parent) : QWidget(parent), statusCache_(statusCache), editCursor_(Qt::IBeamCursor), viewCursor_(Qt::PointingHandCursor) {
	allTypes_.push_back(StatusShow::Online);
	allTypes_.push_back(StatusShow::FFC);
	allTypes_.push_back(StatusShow::Away);
	allTypes_.push_back(StatusShow::XA);
	allTypes_.push_back(StatusShow::DND);
	allTypes_.push_back(StatusShow::None);
	
	isClicking_ = false;
	connecting_ = false;
	setMaximumHeight(24);

	connectingMovie_ = new QMovie(":/icons/connecting.mng");

	QHBoxLayout* mainLayout = new QHBoxLayout(this);
	mainLayout->setSpacing(0);
	mainLayout->setContentsMargins(0,0,0,0);

	stack_ = new QStackedWidget(this);
	stack_->setLineWidth(2);
	stack_->setFrameShape(QFrame::StyledPanel);
	mainLayout->addWidget(stack_);

	QWidget* page1 = new QWidget(this);
	stack_->addWidget(page1);
	QHBoxLayout* page1Layout = new QHBoxLayout(page1);
	page1Layout->setSpacing(0);
	page1Layout->setContentsMargins(0,0,0,0);
	page1->setCursor(viewCursor_);

	statusIcon_ = new QLabel(this);
	statusIcon_->setMinimumSize(16, 16);
	statusIcon_->setMaximumSize(16, 16);
	page1Layout->addWidget(statusIcon_);

	statusTextLabel_ = new QtElidingLabel(this);
	QFont font = statusTextLabel_->font();
	font.setItalic(true);
	statusTextLabel_->setFont(font);
	page1Layout->addWidget(statusTextLabel_);

	icons_[StatusShow::Online] = QIcon(":/icons/online.png");
	icons_[StatusShow::Away] = QIcon(":/icons/away.png");
	icons_[StatusShow::DND] = QIcon(":/icons/dnd.png");
	icons_[StatusShow::None] = QIcon(":/icons/offline.png");
	
	setStatusType(StatusShow::None);

	QWidget* page2 = new QWidget(this);
	QHBoxLayout* page2Layout = new QHBoxLayout(page2);
	page2Layout->setSpacing(0);
	page2Layout->setContentsMargins(0,0,0,0);
	stack_->addWidget(page2);
	
	statusEdit_ = new QtLineEdit(this);
	page2Layout->addWidget(statusEdit_);
	connect(statusEdit_, SIGNAL(returnPressed()), this, SLOT(handleEditComplete()));
	connect(statusEdit_, SIGNAL(escapePressed()), this, SLOT(handleEditCancelled()));
	connect(statusEdit_, SIGNAL(textChanged(const QString&)), this, SLOT(generateList()));

	setStatusText("");


	menu_ = new QListWidget();
	menu_->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint );
	menu_->setAlternatingRowColors(true);
	menu_->setFocusProxy(statusEdit_);
	menu_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	menu_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	QSizePolicy policy(menu_->sizePolicy());
	policy.setVerticalPolicy(QSizePolicy::Expanding);
	menu_->setSizePolicy(policy);


	connect(menu_, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(handleItemClicked(QListWidgetItem*)));

	viewMode();
}

QtStatusWidget::~QtStatusWidget() {
	delete menu_;
	delete connectingMovie_;
}

void QtStatusWidget::handleApplicationFocusChanged(QWidget* /*old*/, QWidget* /*now*/) {
	QWidget* now = qApp->focusWidget();
	if (!editing_ || stack_->currentIndex() == 0) {
		return;
	}
	if (!now || (now != menu_ && now != statusEdit_ && !now->isAncestorOf(statusEdit_) && !now->isAncestorOf(menu_) && !statusEdit_->isAncestorOf(now) && !menu_->isAncestorOf(now))) {
		handleEditCancelled();
	}
	
}

void QtStatusWidget::mousePressEvent(QMouseEvent*) {
	if (stack_->currentIndex() == 0) {
		handleClicked();
	}
}

void QtStatusWidget::generateList() {
	if (!editing_) {
		return;
	}
	QString text = statusEdit_->text();
	newStatusText_ = text;
	menu_->clear();
	foreach (StatusShow::Type type, icons_.keys()) {
		QListWidgetItem* item = new QListWidgetItem(text == "" ? getNoMessage() : text, menu_);
		item->setIcon(icons_[type]);
		item->setToolTip(P2QSTRING(statusShowTypeToFriendlyName(type)) + ": " + item->text());
		item->setStatusTip(item->toolTip());
		item->setData(Qt::UserRole, QVariant(type));
	}
	std::vector<StatusCache::PreviousStatus> previousStatuses = statusCache_->getMatches(Q2PSTRING(text), 8);
	foreach (StatusCache::PreviousStatus savedStatus, previousStatuses) {
		if (savedStatus.first.empty() || std::find_if(allTypes_.begin(), allTypes_.end(), 
					savedStatus.second == lambda::_1 && savedStatus.first == lambda::bind(&statusShowTypeToFriendlyName, lambda::_1)) != allTypes_.end()) {
			continue;
		}
		QListWidgetItem* item = new QListWidgetItem(P2QSTRING(savedStatus.first), menu_);
		item->setIcon(icons_[savedStatus.second]);
		item->setToolTip(item->text());
		item->setStatusTip(item->toolTip());
		item->setData(Qt::UserRole, QVariant(savedStatus.second));
	}
	foreach (StatusShow::Type type, icons_.keys()) {
		if (Q2PSTRING(text) == statusShowTypeToFriendlyName(type)) {
			continue;
		}
		QListWidgetItem* item = new QListWidgetItem(P2QSTRING(statusShowTypeToFriendlyName(type)), menu_);
		item->setIcon(icons_[type]);
		item->setToolTip(item->text());
		item->setStatusTip(item->toolTip());
		item->setData(Qt::UserRole, QVariant(type));
	}
	resizeMenu();
}

void QtStatusWidget::resizeMenu() {
	int height = menu_->sizeHintForRow(0) * menu_->count();
	int marginLeft;
	int marginTop;
	int marginRight;
	int marginBottom;
	menu_->getContentsMargins(&marginLeft, &marginTop, &marginRight, &marginBottom);
	height += marginTop + marginBottom;

	menu_->setGeometry(menu_->x(), menu_->y(), menu_->width(), height);
}

void QtStatusWidget::handleClicked() {
	editing_ = true;
	QDesktopWidget* desktop = QApplication::desktop();
	int screen = desktop->screenNumber(this);
	QPoint point = mapToGlobal(QPoint(0, height()));
	QRect geometry = desktop->availableGeometry(screen);
	int x = point.x();
	int y = point.y();
	int width = 200;
	int height = 80;

	int screenWidth = geometry.x() + geometry.width();
	if (x + width > screenWidth) {
		x = screenWidth - width;
	}
	//foreach (StatusShow::Type type, allTypes_) {
	//	if (statusEdit_->text() == P2QSTRING(statusShowTypeToFriendlyName(type))) {
			statusEdit_->setText("");
	//	}
	//}
	generateList();
	
	height = menu_->sizeHintForRow(0) * menu_->count();
	int marginLeft;
	int marginTop;
	int marginRight;
	int marginBottom;
	menu_->getContentsMargins(&marginLeft, &marginTop, &marginRight, &marginBottom);
	height += marginTop + marginBottom;
	width += marginLeft + marginRight;

	menu_->setGeometry(x, y, width, height);
	menu_->move(x, y);
	menu_->setMaximumWidth(width);
	menu_->show();
	activateWindow();
	statusEdit_->selectAll();
	stack_->setCurrentIndex(1);
	statusEdit_->setFocus();
	connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(handleApplicationFocusChanged(QWidget*, QWidget*)), Qt::QueuedConnection);
}

void QtStatusWidget::viewMode() {
	disconnect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(handleApplicationFocusChanged(QWidget*, QWidget*)));
	editing_ = false;
	menu_->hide();
	stack_->setCurrentIndex(0);
}

void QtStatusWidget::handleEditComplete() {
	editing_ = false;
	statusText_ = newStatusText_;
	viewMode();
	emit onChangeStatusRequest(selectedStatusType_, statusText_);
	statusCache_->addRecent(Q2PSTRING(statusText_), selectedStatusType_);
}

void QtStatusWidget::handleEditCancelled() {
	editing_ = false;
	setStatusText(statusText_);
	viewMode();
}

StatusShow::Type QtStatusWidget::getSelectedStatusShow() {
	return selectedStatusType_;
}

void QtStatusWidget::handleItemClicked(QListWidgetItem* item) {
	editing_ = false;
	selectedStatusType_ = static_cast<StatusShow::Type>(item->data(Qt::UserRole).toInt());
	QString message = item->data(Qt::DisplayRole).toString();
	newStatusText_ = message == getNoMessage() ? "" : message;
	statusEdit_->setText(newStatusText_);
	handleEditComplete();
}

void QtStatusWidget::setNewToolTip() {
	if (connecting_) {
		statusTextLabel_->setToolTip(tr("Connecting"));
	} else {
		statusTextLabel_->setToolTip(P2QSTRING(statusShowTypeToFriendlyName(selectedStatusType_)) + ": " + statusTextLabel_->text());
	}
}

void QtStatusWidget::setStatusText(const QString& text) {
	connectingMovie_->stop();
	statusText_ = text;
	statusEdit_->setText(text);
	QString escapedText(text.isEmpty() ? getNoMessage() : text);
	statusTextLabel_->setText(escapedText);
	setNewToolTip();
}

void QtStatusWidget::setConnecting() {
	connecting_ = true;
	statusIcon_->setMovie(connectingMovie_);
	connectingMovie_->start();
	setNewToolTip();
}

void QtStatusWidget::setStatusType(StatusShow::Type type) {
	connecting_ = false;
	selectedStatusType_ = icons_.contains(type) ? type : StatusShow::Online;
	statusIcon_->setPixmap(icons_[selectedStatusType_].pixmap(16, 16));
	setNewToolTip();
}

QString QtStatusWidget::getNoMessage() {
	return QString(tr("(No message)"));
}

}