/*
	Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org>
	Copyright (c) 2002 David Jarvie <software@astrojar.org.uk>
	Copyright (c) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
	Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>
	Coypright (c) 2010 Volker Lanz <vl@fidra.de>

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Library General Public
	License as published by the Free Software Foundation; either
	version 2 of the License, or (at your option) any later version.

	This library 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
	Library General Public License for more details.

	You should have received a copy of the GNU Library General Public License
	along with this library; see the file COPYING.LIB.  If not, write to
	the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
	Boston, MA 02110-1301, USA.
*/

#include "kdateedit.h"

#include "veneer/datetime.h"

#include <QAbstractItemView>
#include <QApplication>
#include <QEvent>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QDesktopWidget>
#include <QDebug>

#include <klineedit.h>

KDateEdit::KDateEdit(QWidget* parent, const char* name) :
	QComboBox(parent),
	mReadOnly(false),
	mDiscardNextMousePress(false)
{
	setObjectName(name);

	// need at least one entry for popup to work
	setMaxCount(1);
	setEditable(true);

	mDate = QDate::currentDate();
	QString today = Veneer::formatShortDate(mDate);

	addItem(today);
	setCurrentIndex(0);
	setSizeAdjustPolicy(AdjustToContents);

	connect(lineEdit(), SIGNAL(returnPressed()), this, SLOT(lineEnterPressed()));
	connect(this, SIGNAL(textChanged(const QString&)), SLOT(slotTextChanged(const QString&)));

	mPopup = new KDatePickerPopup(QDate::currentDate(), this);
	mPopup->hide();
	mPopup->installEventFilter(this);

	connect(mPopup, SIGNAL(dateChanged(const QDate&)), SLOT(dateSelected(const QDate&)));

	lineEdit()->installEventFilter(this);

	mTextChanged = false;
}

void KDateEdit::setDate(const QDate& date)
{
	assignDate(date);
	updateView();
}

QDate KDateEdit::date() const
{
	return mDate;
}

void KDateEdit::setReadOnly(bool readOnly)
{
	mReadOnly = readOnly;
	lineEdit()->setReadOnly(readOnly);
}

bool KDateEdit::isReadOnly() const
{
	return mReadOnly;
}

void KDateEdit::showPopup()
{
	if (mReadOnly)
		return;

	QRect desk = QApplication::desktop()->screenGeometry(this);

	QPoint popupPoint = mapToGlobal(QPoint(0, 0));

	int dateFrameHeight = mPopup->sizeHint().height();

	if (popupPoint.y() + height() + dateFrameHeight > desk.bottom())
		popupPoint.setY(popupPoint.y() - dateFrameHeight);
	else
		popupPoint.setY(popupPoint.y() + height());

	int dateFrameWidth = mPopup->sizeHint().width();

	if (popupPoint.x() + dateFrameWidth > desk.right())
		popupPoint.setX(desk.right() - dateFrameWidth);

	if (popupPoint.x() < desk.left())
		popupPoint.setX(desk.left());

	if (popupPoint.y() < desk.top())
		popupPoint.setY(desk.top());

	if (mDate.isValid())
		mPopup->setDate(mDate);
	else
		mPopup->setDate(QDate::currentDate());

	mPopup->popup(popupPoint);

	// The combo box is now shown pressed. Make it show not pressed again
	// by causing its (invisible) list box to emit a 'selected' signal.
	// First, ensure that the list box contains the date currently displayed.
	QDate date = parseDate();
	assignDate(date);
	updateView();

	// Now, simulate an Enter to unpress it
	QAbstractItemView* lb = view();
	if (lb)
	{
		lb->setCurrentIndex(lb->model()->index(0, 0));
		QApplication::postEvent(lb, new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier));
	}
}

void KDateEdit::dateSelected(const QDate& date)
{
	if (assignDate(date))
	{
		updateView();

		emit dateChanged(date);
		emit dateEntered(date);

		if (date.isValid())
			mPopup->hide();
	}
}

void KDateEdit::lineEnterPressed()
{
	bool replaced = false;

	QDate date = parseDate(&replaced);

	if (assignDate(date))
	{
		if (replaced)
			updateView();

		emit dateChanged(date);
		emit dateEntered(date);
	}
}

QDate KDateEdit::parseDate(bool* replaced) const
{
	QString text = currentText();
	QDate result;

	if (replaced)
		*replaced = false;

	if (text.isEmpty())
		result = QDate();
	else
		result = Veneer::readDate(text);

	return result;
}

void KDateEdit::focusOutEvent(QFocusEvent* e)
{
	if (mTextChanged)
	{
		lineEnterPressed();
		mTextChanged = false;
	}

	QComboBox::focusOutEvent(e);
}

void KDateEdit::keyPressEvent(QKeyEvent* e)
{
	int step = 0;

	if (e->key() == Qt::Key_Up)
		step = 1;
	else if (e->key() == Qt::Key_Down)
		step = -1;

	if (step && !mReadOnly)
	{
		QDate date = parseDate();

		if (date.isValid())
		{
			date = date.addDays(step);

			if (assignDate(date))
			{
				updateView();
				emit dateChanged(date);
				emit dateEntered(date);
			}
		}
	}

	QComboBox::keyPressEvent(e);
}

bool KDateEdit::eventFilter(QObject* object, QEvent* event)
{
	if (object == lineEdit())
	{
		// We only process the focus out event if the text has changed
		// since we got focus
		if ((event->type() == QEvent::FocusOut) && mTextChanged)
		{
			lineEnterPressed();
			mTextChanged = false;
		}
		else if (event->type() == QEvent::KeyPress)
		{
			// Up and down arrow keys step the date
			QKeyEvent* keyEvent = (QKeyEvent* )event;

			if (keyEvent->key() == Qt::Key_Return)
			{
				lineEnterPressed();
				return true;
			}
		}
	}
	else
	{
		// It's a date picker event
		switch (event->type())
		{
			case QEvent::MouseButtonDblClick:
			case QEvent::MouseButtonPress:
			{
				QMouseEvent* mouseEvent = (QMouseEvent*)event;
				if (!mPopup->rect().contains(mouseEvent->pos()))
				{
					QPoint globalPos = mPopup->mapToGlobal(mouseEvent->pos());
					if (QApplication::widgetAt(globalPos) == this)
					{
						// The date picker is being closed by a click on the
						// KDateEdit widget. Avoid popping it up again immediately.
						mDiscardNextMousePress = true;
					}
				}

				break;
			}

			default:
				break;
		}
	}

	return false;
}

void KDateEdit::mousePressEvent(QMouseEvent* event)
{
	if (event->button() == Qt::LeftButton && mDiscardNextMousePress)
	{
		mDiscardNextMousePress = false;
		return;
	}

	QComboBox::mousePressEvent(event);
}

void KDateEdit::slotTextChanged(const QString& )
{
	QDate date = parseDate();

	if (assignDate(date))
		emit dateChanged(date);

	mTextChanged = true;
}

bool KDateEdit::assignDate(const QDate& date)
{
	mDate = date;
	mTextChanged = false;
	return true;
}

void KDateEdit::updateView()
{
	QString dateString;

	if (mDate.isValid())
		dateString = Veneer::formatShortDate(mDate);

	// We do not want to generate a signal here,
	// since we explicitly setting the date
	bool blocked = blockSignals(true);
	removeItem(0);
	insertItem(0, dateString);
	blockSignals(blocked);
}
