/* This file is part of the KDE project
   Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
   Copyright (C) 2004 Alexander Dymo <cloudtemple@mskat.net>
   Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>

   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 "editoritem.h"
#include "editor.h"
#include "property.h"
#include "widget.h"
#include "factory.h"
#include "utils.h"

#include <qpainter.h>
#include <qpixmap.h>
#include <qheader.h>
#include <qstyle.h>
#include <qlabel.h>
#include <qlayout.h>

#include <kdebug.h>
#include <kiconloader.h>
#include <kstyle.h>
#include <kpopupmenu.h>
#include <kapplication.h>

#define BRANCHBOX_SIZE 9

namespace KoProperty {
class EditorItemPrivate
{
	public:
		EditorItemPrivate()
		: property(0) {}
		~EditorItemPrivate() {}

		Property  *property;
		Editor  *editor;
};

//! @internal
static void paintListViewExpander(QPainter* p, QWidget* w, int height, const QColorGroup& cg, bool isOpen)
{
	const int marg = (height -2 - BRANCHBOX_SIZE) / 2;
	int xmarg = marg;
//		if (dynamic_cast<EditorGroupItem*>(item))
//				xmarg = xmarg * 10 / 14 -1;
#if 0 
//! @todo disabled: kstyles do not paint background yet... reenable in the future...
	KStyle* kstyle = dynamic_cast<KStyle*>(widget->style());
	if (kstyle) {
		kstyle->drawKStylePrimitive(
			KStyle::KPE_ListViewExpander, p, w, QRect( xmarg, marg, BRANCHBOX_SIZE, BRANCHBOX_SIZE ), 
			cg, isOpen ? 0 : QStyle::Style_On,
				QStyleOption::Default);
	}
	else {
#endif
		Q_UNUSED(w);
		//draw by hand
		p->setPen( KPROPEDITOR_ITEM_BORDER_COLOR );
		p->drawRect(xmarg, marg, BRANCHBOX_SIZE, BRANCHBOX_SIZE);
		p->fillRect(xmarg+1, marg + 1, BRANCHBOX_SIZE-2, BRANCHBOX_SIZE-2,
//			item->listView()->paletteBackgroundColor());
			cg.base());
//		p->setPen( item->listView()->paletteForegroundColor() );
		p->setPen( cg.foreground() );
		p->drawLine(xmarg+2, marg+BRANCHBOX_SIZE/2, xmarg+BRANCHBOX_SIZE-3, marg+BRANCHBOX_SIZE/2);
		if(!isOpen) {
			p->drawLine(xmarg+BRANCHBOX_SIZE/2, marg+2,
				xmarg+BRANCHBOX_SIZE/2, marg+BRANCHBOX_SIZE-3);
		}
//	}
}

//! @internal
//! Based on KPopupTitle, see kpopupmenu.cpp
class GroupWidgetBase : public QWidget
{
	public:
		GroupWidgetBase(QWidget* parent)
		: QWidget(parent)
		, m_isOpen(true)
		, m_mouseDown(false)
		{
			setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed, 0, 1));
		}

		void setText( const QString &text )
		{
			m_titleStr = text;
		}

		void setIcon( const QPixmap &pix )
		{
			m_miniicon = pix;
		}

		virtual bool isOpen() const
		{
			return m_isOpen;
		}

		virtual void setOpen(bool set)
		{
			m_isOpen = set;
		}

		virtual QSize sizeHint () const
		{
			QSize s( QWidget::sizeHint() );
			s.setHeight(fontMetrics().height()*2);
			return s;
		}

	protected:
		virtual void paintEvent(QPaintEvent *) {
			QRect r(rect());
			QPainter p(this);
			QStyle::StyleFlags flags = m_mouseDown ? QStyle::Style_Down : QStyle::Style_Default;
			kapp->style().drawPrimitive(QStyle::PE_HeaderSection, &p, r, palette().active(), flags);

			paintListViewExpander(&p, this, r.height()+2, palette().active(), isOpen());
			if (!m_miniicon.isNull()) {
				p.drawPixmap(24, (r.height()-m_miniicon.height())/2, m_miniicon);
			}

			if (!m_titleStr.isNull())
			{
				int indent = 16 + (m_miniicon.isNull() ? 0 : (m_miniicon.width()+4));
				p.setPen(palette().active().text());
				QFont f = p.font();
				f.setBold(true);
				p.setFont(f);
				p.drawText(indent+8, 0, width()-(indent+8),
						height(), AlignLeft | AlignVCenter | SingleLine,
						m_titleStr);
			}
//			p.setPen(palette().active().mid());
//			p.drawLine(0, 0, r.right(), 0);
		}

		virtual bool event( QEvent * e ) {
			if (e->type()==QEvent::MouseButtonPress || e->type()==QEvent::MouseButtonRelease) {
				QMouseEvent* me = static_cast<QMouseEvent*>(e);
				if (me->button() == Qt::LeftButton) {
					m_mouseDown = e->type()==QEvent::MouseButtonPress;
					update();
				}
			}
			return QWidget::event(e);
		}

	protected:
		QString m_titleStr;
		QPixmap m_miniicon;
		bool m_isOpen : 1;
		bool m_mouseDown : 1;
};

class GroupWidget : public GroupWidgetBase
{
	public:
		GroupWidget(EditorGroupItem *parentItem)
		: GroupWidgetBase(parentItem->listView()->viewport())
		, m_parentItem(parentItem)
		{
		}

		virtual bool isOpen() const
		{
			return m_parentItem->isOpen();
		}

	protected:
		EditorGroupItem *m_parentItem;
};

class GroupContainer::Private
{
	public:
		Private() {}
		QVBoxLayout* lyr;
		GroupWidgetBase *groupWidget;
		QGuardedPtr<QWidget> contents;
};

}//namespace

using namespace KoProperty;

GroupContainer::GroupContainer(const QString& title, QWidget* parent)
: QWidget(parent)
, d(new Private())
{
	setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed, 0, 1));
	d->lyr = new QVBoxLayout(this);
	d->groupWidget = new GroupWidgetBase(this);
	d->groupWidget->setText( title );
	d->lyr->addWidget(d->groupWidget);
	d->lyr->addSpacing(4);
}

GroupContainer::~GroupContainer()
{
	delete d;
}

void GroupContainer::setContents( QWidget* contents )
{
	if (d->contents) {
		d->contents->hide();
		d->lyr->remove(d->contents);
		delete d->contents;
	}
	d->contents = contents;
	if (d->contents) {
		d->lyr->addWidget(d->contents);
		d->contents->show();
	}
	update();
}

bool GroupContainer::event( QEvent * e ) {
	if (e->type()==QEvent::MouseButtonPress) {
		QMouseEvent* me = static_cast<QMouseEvent*>(e);
		if (me->button() == Qt::LeftButton && d->contents && d->groupWidget->rect().contains(me->pos())) {
			d->groupWidget->setOpen(!d->groupWidget->isOpen());
			if (d->groupWidget->isOpen())
				d->contents->show();
			else
				d->contents->hide();
			d->lyr->invalidate();
			update();
		}
	}
	return QWidget::event(e);
}

//////////////////////////////////////////////////////

EditorItem::EditorItem(Editor *editor, EditorItem *parent, Property *property, QListViewItem *after)
 : KListViewItem(parent, after,
	property->captionForDisplaying().isEmpty() ? property->name() : property->captionForDisplaying())
{
	d = new EditorItemPrivate();
	d->property = property;
	d->editor = editor;

	setMultiLinesEnabled(true);
	//setHeight(static_cast<Editor*>(listView())->baseRowHeight()*3);
/*
	if (property && !property->caption().isEmpty()) {
			QSimpleRichText srt(property->caption(), font());
			srt.setWidth(columnWidth(0)-KPROPEDITOR_ITEM_MARGIN*2-20+1);
			int oldHeight = it.current()->height();
			int textHeight = srt.height()+KPROPEDITOR_ITEM_MARGIN;
			int textLines = textHeight / d->baseRowHeight + (((textHeight % d->baseRowHeight) > 0) ? 1 : 0);
			kdDebug() << " textLines: " << textLines << endl;
			if (textLines != newNumLines) {
				dynamic_cast<EditorItem*>(it.current())->setHeight(newNumLines * d->baseRowHeight);
			}
			kdDebug() << it.current()->text(0) << ":  "  << oldHeight << " -> " << newHeight << endl;
		}
*/
}

EditorItem::EditorItem(KListView *parent)
 : KListViewItem(parent)
{
	d = new EditorItemPrivate();
	d->property = 0;
	d->editor = 0;
	setMultiLinesEnabled(true);
}

EditorItem::EditorItem(EditorItem *parent, const QString &text)
 : KListViewItem(parent, text)
{
	d = new EditorItemPrivate();
	d->property = 0;
	d->editor = 0;
	setMultiLinesEnabled(true);
}

EditorItem::EditorItem(EditorItem *parent, EditorItem *after, const QString &text)
 : KListViewItem(parent, after, text)
{
	d = new EditorItemPrivate();
	d->property = 0;
	d->editor = 0;
	setMultiLinesEnabled(true);
}

EditorItem::~EditorItem()
{
	delete d;
}

Property*
EditorItem::property()
{
	return d->property;
}

void
EditorItem::paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int align)
{
	//int margin = static_cast<Editor*>(listView())->itemMargin();
	if(!d->property)
		return;

	if(column == 0)
	{
		QFont font = listView()->font();
		if(d->property->isModified())
			font.setBold(true);
		p->setFont(font);
		p->setBrush(cg.highlight());
		p->setPen(cg.highlightedText());
		KListViewItem::paintCell(p, cg, column, width, align);
		p->fillRect(parent() ? 0 : 50, 0, width, height()-1,
			QBrush(isSelected() ? cg.highlight() : backgroundColor()));
		p->setPen(isSelected() ? cg.highlightedText() : cg.text());
		int delta = -20+KPROPEDITOR_ITEM_MARGIN;
		if ((firstChild() && dynamic_cast<EditorGroupItem*>(parent()))) {
			delta = -KPROPEDITOR_ITEM_MARGIN-1;
		}
		if (dynamic_cast<EditorDummyItem*>(parent())) {
			delta = KPROPEDITOR_ITEM_MARGIN*2;
		}
		else if (parent() && dynamic_cast<EditorDummyItem*>(parent()->parent())) {
			if (dynamic_cast<EditorGroupItem*>(parent()))
				delta += KPROPEDITOR_ITEM_MARGIN*2;
			else
				delta += KPROPEDITOR_ITEM_MARGIN*5;
		}
		p->drawText(
			QRect(delta,2, width+listView()->columnWidth(1)-KPROPEDITOR_ITEM_MARGIN*2, height()),
			Qt::AlignLeft | Qt::AlignTop /*| Qt::SingleLine*/, text(0));

		p->setPen( KPROPEDITOR_ITEM_BORDER_COLOR );
		p->drawLine(width-1, 0, width-1, height()-1);
		p->drawLine(0, -1, width-1, -1);

		p->setPen( KPROPEDITOR_ITEM_BORDER_COLOR ); //! \todo custom color?
		if (dynamic_cast<EditorDummyItem*>(parent()))
			p->drawLine(0, 0, 0, height()-1 );
	}
	else if(column == 1)
	{
		QColorGroup icg(cg);
		icg.setColor(QColorGroup::Background, backgroundColor());
		p->setBackgroundColor(backgroundColor());
		Widget *widget = d->editor->createWidgetForProperty(d->property, false /*don't change Widget::property() */);
		if(widget) {
			QRect r(0, 0, d->editor->header()->sectionSize(1), height() - (widget->hasBorders() ? 0 : 1));
			p->setClipRect(r, QPainter::CoordPainter);
			p->setClipping(true);
			widget->drawViewer(p, icg, r, d->property->value());
			p->setClipping(false);
		}
	}
	p->setPen( KPROPEDITOR_ITEM_BORDER_COLOR ); //! \todo custom color?
	p->drawLine(0, height()-1, width, height()-1 );
}

void
EditorItem::paintBranches(QPainter *p, const QColorGroup &cg, int w, int y, int h)
{
	p->eraseRect(0,0,w,h);
	KListViewItem *item = static_cast<KListViewItem*>(firstChild());
	if(!item)
		return;

	QColor backgroundColor;
	p->save();
	p->translate(0,y);
	QFont font = listView()->font();
	while(item)
	{
		if(item->isSelected())
			backgroundColor = cg.highlight();
		else {
			if (dynamic_cast<EditorGroupItem*>(item))
				backgroundColor = cg.base();
			else
				backgroundColor = item->backgroundColor();
		}
//		p->fillRect(-50,0,50, item->height(), QBrush(backgroundColor));
		p->save();
		p->setPen( KPROPEDITOR_ITEM_BORDER_COLOR );
		int delta = 0;
		int fillWidth = w;
		int x = 0;
		if (dynamic_cast<EditorGroupItem*>(item->parent())) {
			delta = 0;//-19;
			fillWidth += 19;
		}
		else {
			if (dynamic_cast<EditorGroupItem*>(item) || /*for flat mode*/ dynamic_cast<EditorDummyItem*>(item->parent()))
				x = 19;
			else
				x = -19;
			fillWidth += 19;
		}
		if (dynamic_cast<EditorDummyItem*>(item->parent())) {
			x = 19;
		}
		else if (item->parent() && dynamic_cast<EditorDummyItem*>(item->parent()->parent())) {
			x = 0;
		}
		p->fillRect(x+1, 0, fillWidth-1, item->height()-1, QBrush(backgroundColor));
		p->drawLine(x, item->height()-1, w, item->height()-1 );
		if (!dynamic_cast<EditorGroupItem*>(item))
			p->drawLine(x, 0, x, item->height()-1 );
		p->restore();

//	for (int i=0; i<10000000; i++)
//		;
//		if(item->isSelected())  {
//			p->fillRect(parent() ? 0 : 50, 0, w, item->height()-1, QBrush(cg.highlight()));
//			p->fillRect(-50,0,50, item->height(), QBrush(cg.highlight()));
//		}

		//sorry, but we need to draw text here again
		font.setBold( dynamic_cast<EditorGroupItem*>(item)
			|| (static_cast<EditorItem*>(item)->property() && static_cast<EditorItem*>(item)->property()->isModified()) );
		p->setFont(font);
		p->setPen(item->isSelected() ? cg.highlightedText() : cg.text());
		if (item->firstChild() && dynamic_cast<EditorGroupItem*>(item->parent())) {
			delta = 19-KPROPEDITOR_ITEM_MARGIN-1;
		}
		else if (dynamic_cast<EditorDummyItem*>(item->parent())) {
			delta = 19;
		}
		if (item->parent() && dynamic_cast<EditorDummyItem*>(item->parent()->parent())) {
			if (dynamic_cast<EditorGroupItem*>(item->parent()))
				delta += KPROPEDITOR_ITEM_MARGIN*2;
			else
				delta += KPROPEDITOR_ITEM_MARGIN*5;
		}

		if (!dynamic_cast<EditorDummyItem*>(item->parent()))
			p->drawText(QRect(delta+1,0, w+listView()->columnWidth(1), item->height()),
			Qt::AlignLeft | Qt::AlignVCenter /*| Qt::SingleLine*/, item->text(0));

		if(item->firstChild())  {
			paintListViewExpander(p, listView(), item->height(),
				cg, item->isOpen());
		}

		// draw icon (if there is one)
		EditorItem *editorItem = dynamic_cast<EditorItem*>(item);
		if (editorItem && editorItem->property() && !editorItem->property()->icon().isEmpty()) {
			//int margin = listView()->itemMargin();
			QPixmap pix = SmallIcon(editorItem->property()->icon());
			if (!pix.isNull())
				p->drawPixmap(-19+(19-pix.width())/2, (item->height() - pix.height()) / 2, pix);
		}

		p->translate(0, item->totalHeight());
		item = (KListViewItem*)item->nextSibling();
	}
	p->restore();
}

void
EditorItem::paintFocus(QPainter *, const QColorGroup &, const QRect & )
{
}

int
EditorItem::compare( QListViewItem *i, int col, bool ascending ) const
{
	if (!ascending)
		return -QListViewItem::key( col, ascending ).localeAwareCompare( i->key( col, ascending ) );

	if (d->property) {
//		kopropertydbg << d->property->name() << " " << d->property->sortingKey() << " | "
//			<< static_cast<EditorItem*>(i)->property()->name() << " "
//			<< static_cast<EditorItem*>(i)->property()->sortingKey() << endl;
		return d->property->sortingKey()
			- ((dynamic_cast<EditorItem*>(i) && dynamic_cast<EditorItem*>(i)->property()) 
				? dynamic_cast<EditorItem*>(i)->property()->sortingKey() : 0);
	}

	return 0;
//	return d->order - static_cast<EditorItem*>(i)->d->order;
}

void
EditorItem::setHeight( int height )
{
	KListViewItem::setHeight(height);
}

//////////////////////////////////////////////////////

EditorGroupItem::EditorGroupItem(EditorItem *parent, EditorItem *after, const QString &text, const QString &icon, int sortOrder)
 : EditorItem(parent, after, text)
 , m_label(0)
 , m_sortOrder(sortOrder)
{
	init(icon);
}

EditorGroupItem::EditorGroupItem(EditorItem *parent, const QString &text, const QString &icon, int sortOrder)
 : EditorItem(parent, text)
 , m_label(0)
 , m_sortOrder(sortOrder)
{
	init(icon);
}

EditorGroupItem::~EditorGroupItem()
{
	delete m_label;
}

QWidget* EditorGroupItem::label() const
{
	return m_label;
}

void EditorGroupItem::init(const QString &icon)
{
	setOpen(true);
	setSelectable(false);
	m_label = new GroupWidget(this);
	m_label->setText(text(0)); //todo: icon?
	if (!icon.isEmpty())
		m_label->setIcon( SmallIcon(icon) );
	m_label->show();
}

void
EditorGroupItem::paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int /*align*/)
{
	Q_UNUSED(p);
	Q_UNUSED(cg);
	Q_UNUSED(column);
	Q_UNUSED(width);
	//no need to draw anything since there's a label on top of it
//	p->fillRect(0, 0, width, height(), cg.base());

	//if(column == 1)
	//	return;
	/*p->setPen( KPROPEDITOR_ITEM_BORDER_COLOR ); //! \todo custom color?

	p->setClipRect(listView()->itemRect(this));
	if(column == 1)
		p->translate(-listView()->columnWidth(0) + 20, 0);
	int totalWidth = listView()->columnWidth(0) + listView()->columnWidth(1) - 20;
	p->eraseRect(QRect(0,0, totalWidth,height()-1));
	p->drawLine(0, height()-1, totalWidth-1, height()-1);

	QFont font = listView()->font();
	font.setBold(true);
	p->setFont(font);
	p->setBrush(cg.highlight());
	//p->setPen(cg.highlightedText());
	KListViewItem::paintCell(p, cg, column, width, align);
	p->setPen(cg.text());
	p->drawText(QRect(0,0, totalWidth, height()),
		Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, text(0));*/
}

void
EditorGroupItem::setup()
{
	KListViewItem::setup();
	setHeight( height()+4 );
}

int
EditorGroupItem::compare( QListViewItem *i, int col, bool ascending ) const
{
	Q_UNUSED(col);
	Q_UNUSED(ascending);
	if (dynamic_cast<EditorGroupItem*>(i)) {
		return m_sortOrder 
			- dynamic_cast<EditorGroupItem*>(i)->m_sortOrder;
	}
	return 0;
}

////////////////////////////////////////////////////////

EditorDummyItem::EditorDummyItem(KListView *listview)
 : EditorItem(listview)
{
	setSelectable(false);
	setOpen(true);
}

EditorDummyItem::~EditorDummyItem()
{}

void
EditorDummyItem::setup()
{
	setHeight(0);
}
