/***************************************************************************
 *   Copyright (C) 2004 by Tasuku Suzuki                                   *
 *   tasuku@linux-life.net                                                 *
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <qobjectlist.h>
#include <qpainter.h>
#include <qlistview.h>

#include <kdebug.h>
#include <klocale.h>

#include "kkdoc.h"
#include "kkwidget.h"
#include "kksizehandler.h"
#include "kkitem.h"

#define DEFAULT_ITEM_SIZE 20

KKWidget::KKWidget( KKItem* item, QWidget* parent, bool readonly, const char* name )
	: QWidget( parent, name )
{
	m_readonly = readonly;
	m_item = item;
	if( !m_readonly )
	{
		connect( m_item, SIGNAL( repaint( const QRect& ) ), this, SLOT( slotRepaint( const QRect& ) ) );
		connect( m_item, SIGNAL( selectionChanged( bool ) ), this, SLOT( selectionChanged( bool ) ) );
		connect( m_item, SIGNAL( imageChanged() ), this, SLOT( imageChanged() ) );
		connect( m_item, SIGNAL( orientationChanged() ), this, SLOT( orientationChanged() ) );
	}

	KKItem* child = (KKItem*)m_item->firstChild();
	while( child )
	{
		if( !m_readonly )
		{
			itemInserted( m_item, child );
		}
		child = (KKItem*)child->nextSibling();
	}
	m_zoom = 1;

	m_size = m_item->size();

	if( !m_readonly )
	{
		QFrame* fra;
#ifdef KKWINDOWOPACITY
		m_fraSelect = new QFrame( this );
		fra = m_fraSelect;
		fra->setFrameStyle( QFrame::Box | QFrame::Plain );
		fra->setLineWidth( 1 );
		fra->setWindowOpacity( 0.5 );
		fra->setPaletteBackgroundColor( blue );
		fra->hide();
#else
		m_fraSelectLeft = new QFrame( this );
		fra = m_fraSelectLeft;
		fra->setLineWidth( 1 );
		fra->setFrameStyle( QFrame::VLine | QFrame::Plain );
		fra->hide();

		m_fraSelectTop = new QFrame( this );
		fra = m_fraSelectTop;
		fra->setLineWidth( 1 );
		fra->setFrameStyle( QFrame::HLine | QFrame::Plain );
		fra->hide();

		m_fraSelectRight = new QFrame( this );
		fra = m_fraSelectRight;
		fra->setLineWidth( 1 );
		fra->setFrameStyle( QFrame::VLine | QFrame::Plain );
		fra->hide();

		m_fraSelectBottom = new QFrame( this );
		fra = m_fraSelectBottom;
		fra->setLineWidth( 1 );
		fra->setFrameStyle( QFrame::HLine | QFrame::Plain );
		fra->hide();
#endif
	}

	if( !m_item->image().isNull() )
	{
		setPaletteBackgroundPixmap( m_item->imageResized() );
	}
	else
	{
		setBackgroundMode( NoBackground );
		setPaletteBackgroundColor( white );
	}
	resetGeometry();
	connect( document(), SIGNAL( inserted( QListViewItem*, KKItem* ) ), this, SLOT( itemInserted( QListViewItem*, KKItem* ) ) );
	connect( document(), SIGNAL( removed( QListViewItem*, KKItem* ) ), this, SLOT( itemRemoved( QListViewItem*, KKItem* ) ) );
}


KKWidget::~KKWidget()
{
	if( !m_readonly )
	{
		for( QListViewItem* item = m_item->firstChild(); item; item = item->nextSibling() )
		{
			delete m_sizeHandles[(KKItem*)item];
		}
	}
}

void KKWidget::resetGeometry()
{
	setFixedSize( (int)(size().width() * zoom()), (int)(size().height() * zoom()) );
	move( location() * zoom() );
}

// BEGIN Properties

// BEGIN Size
void KKWidget::setSize( QSize s )
{
	m_size = s;
	resetGeometry();
}
// END

// BEGIN Location
QPoint KKWidget::location()
{
	return m_item->location();
}

void KKWidget::setLocation( QPoint l )
{
	m_item->setLocation( l );
	resetGeometry();
}

// END

// BEGIN Zoom
double KKWidget::zoom()
{
	if( m_zoom < 0 )
	{
		return -1.0 / (double)(m_zoom);
	}
	else
	{
		return m_zoom;
	}
}

void KKWidget::setZoom( int z )
{
	m_zoom = z;
	resetGeometry();
	repaint();
	return;
	QObjectList* l = queryList( "KKWidget" );
	QObjectListIterator it( *l );
	QObject *obj;

	while ( (obj = it.current()) != 0 ) {
		++it;
		((KKWidget*)obj)->setZoom( z );
	}
	delete l;
}
// END

// END   Properties


void KKWidget::paintEvent( QPaintEvent* e )
{
	draw( this, e->rect() );
}

void KKWidget::draw( const QPaintDevice* device )
{
	draw( device, m_item->rect() );
}

void KKWidget::draw( const QPaintDevice* device, QRect rect )
{
#define DOUBLEBUFFERING
	QPainter p;
#ifdef DOUBLEBUFFERING
	QPixmap pm( size() );
	p.begin( &pm, device );
	if( !m_item->image().isNull() )
	{
		bitBlt( &pm, rect.topLeft(), paletteBackgroundPixmap(), rect );
	}
	else
	{
		p.fillRect( rect, QBrush( white ) );
	}
#else
	p.begin( device );
#endif

	m_item->drawItem( p, zoom() );
	if( m_item->firstChild() )
	{
		drawEach( m_item->firstChild(), p, rect );
	}
	p.end();
#ifdef DOUBLEBUFFERING
	bitBlt( this, rect.topLeft(), &pm, rect );
#endif
}

void KKWidget::drawEach( QListViewItem* item, QPainter& p, const QRect& rect )
{
	// To draw reverse order( lastChild to firstChild )
	QListViewItem* next = item->nextSibling();
	if( next ) drawEach( next, p, rect );
	if( ((KKItem*)item)->rect().intersects( rect ) )
	{
		((KKItem*)item)->drawItem( p, zoom() );
	}
}

void KKWidget::mousePressEvent( QMouseEvent* e )
{
	raise();
	if( m_readonly )
	{
		document()->setSelected( m_item, false );
		return;
	}
	KKItem* child;
	if( e->button() == LeftButton )
	{
		switch( dragMode() )
		{
			case None:
				m_fraSelectPos = e->pos();

				// 選択されているアイテム上の場合移動モードにする
				child = (KKItem*)m_item->firstChild();
				while( child )
				{
					if( child->isSelected() && child->rect().contains( e->pos() ) )
					{
						setCursor( pointingHandCursor );
						setDragMode( Move );
						document()->moveStart();
						return;
					}
					child = (KKItem*)child->nextSibling();
				}


				// アイテム上の場合そのアイテムで移動モードにする
				child = (KKItem*)m_item->firstChild();
				while( child )
				{
					if( child->rect().contains( e->pos() ) )
					{
						if( !( e->state() & (ControlButton | ShiftButton) )  )
						{
							document()->clearSelection();
						}
						document()->setSelected( child, true );
						setCursor( pointingHandCursor );
						setDragMode( Move );
						document()->moveStart();
						return;
					}
					child = (KKItem*)child->nextSibling();
				}

				// 何もない場合選択モードにする
				document()->clearSelection();
				document()->setSelected( m_item, true );
				setDragMode( Select );
				startDrag();
				break;
			case Insert:
				// すべてのアイテムを選択解除
				document()->clearSelection();
				document()->setSelected( m_item, true );
				m_fraSelectPos = e->pos();
				startDrag();
				break;
			default:
				break;
		}
	}
	else if( e->button() == RightButton )
	{
		// above selected item
		KKItem* child = (KKItem*)m_item->firstChild();
		while( child )
		{
			if( child->isSelected() && child->rect().contains( e->pos() ) )
			{
				emit popup( e->globalPos(), false );
				return;
			}
			child = (KKItem*)child->nextSibling();
		}

		// above item
		child = (KKItem*)m_item->firstChild();
		while( child )
		{
			if( child->rect().contains( e->pos() ) )
			{
				document()->clearSelection();
				document()->setSelected( child, true );
				emit popup( e->globalPos(), false );
				return;
			}
			child = (KKItem*)child->nextSibling();
		}

		// base
		document()->clearSelection();
		document()->setSelected( m_item, true );
		emit popup( e->globalPos(), true );

		// 2004-11-14 Kazuki Ohta <mover@hct.zaq.ne.jp>
		// TODO: Property Setting Dialog is needed?
		// 2004-11-14 Tasuku Suzuki <tasuku@linux-life.net>
		// Modified like above.
		// Now I don't need property setting dialog yet.
		// But if you need it, make action and edit kreetingkardui.rc.
		// And please show the dialog when double clicked.
	}
}

void KKWidget::mouseMoveEvent( QMouseEvent* e )
{
	if( m_readonly )
	{
		return;
	}
	QRect rect;
	KKItem* child;
	int x, y, w, h;
	switch( dragMode() )
	{
		case None:
			m_fraSelectPos = e->pos();

/*			// 選択されているアイテム上の場合リサイズ用のカーソルに変更
			child = (KKItem*)m_item->firstChild();
			while( child )
			{
				if( child->isSelected() && child->rect().contains( e->pos() ) )
				{
					setCursor( waitCursor );
					return;
				}
				child = (KKItem*)child->nextSibling();
			}*/
			setCursor( arrowCursor );
			break;
		case Select:
		case Insert:
			if( e->pos() != m_fraSelectPos ) setCursor( crossCursor );
			w = e->pos().x() - m_fraSelectPos.x();
			h = e->pos().y() - m_fraSelectPos.y();
			if( w > 0 )
			{
				x = m_fraSelectPos.x();
			}
			else
			{
				x = e->pos().x();
				w *= -1;
			}
			if( h > 0 )
			{
				y = m_fraSelectPos.y();
			}
			else
			{
				y = e->pos().y();
				h *= -1;
			}
#ifdef KKWINDOWOPACITY
			m_fraSelect->setGeometry( x, y, w, h );
#else
			m_fraSelectLeft->setGeometry( x, y, 1, h );
			m_fraSelectTop->setGeometry( x, y, w, 1 );
			m_fraSelectRight->setGeometry( x+w-1, y, 1, h );
			m_fraSelectBottom->setGeometry( x, y+h-1, w, 1 );
#endif
			break;
		case Move:
			child = (KKItem*)m_item->firstChild();
			while( child )
			{
				if( child->isSelected() )
				{
					child->setLocation( child->location() + e->pos() - m_fraSelectPos );
				}
				child = (KKItem*)child->nextSibling();
			}
			m_fraSelectPos = e->pos();
			document()->moving();
			break;
	}
}

void KKWidget::mouseReleaseEvent( QMouseEvent* e )
{
	if( m_readonly )
	{
		return;
	}
	int x, y, w, h;
	switch( dragMode() )
	{
		case Move:
			document()->moveEnd();
			setCursor( arrowCursor );
			break;
		case Select:
		{
			w = e->pos().x() - m_fraSelectPos.x();
			h = e->pos().y() - m_fraSelectPos.y();
			if( w > 0 )
			{
				x = m_fraSelectPos.x();
			}
			else
			{
				x = e->pos().x();
				w *= -1;
			}
			if( h > 0 )
			{
				y = m_fraSelectPos.y();
			}
			else
			{
				y = e->pos().y();
				h *= -1;
			}
			int count = selectItem( QRect( x, y, w, h ), e->state() & ShiftButton );
			m_item->listView()->setSelected( m_item, ( count == 0 ) );
			setCursor( arrowCursor );
			break;
		}
		case Insert:
		{
			w = e->pos().x() - m_fraSelectPos.x();
			h = e->pos().y() - m_fraSelectPos.y();
			if( w >= 0 )
			{
				x = m_fraSelectPos.x();
				if( w == 0)
					w = DEFAULT_ITEM_SIZE;
			}
			else
			{
				x = e->pos().x();
				w *= -1;
			}
			if( h >= 0 )
			{
				y = m_fraSelectPos.y();
				if( h == 0)
					h = DEFAULT_ITEM_SIZE;
			}
			else
			{
				y = e->pos().y();
				h *= -1;
			}
			KKItem* item = new KKItem( m_item );
			item->setLocation( x, y );
			item->setSize( w, h );
			itemInserted( m_item, item );
			m_item->listView()->setSelected( m_item, false );
			m_item->listView()->setSelected( item, true );
			document()->insert();
			setCursor( arrowCursor );
			break;
		}
		default:
			break;
	}
#ifdef KKWINDOWOPACITY
	m_fraSelect->hide();
#else
	m_fraSelectLeft->hide();
	m_fraSelectTop->hide();
	m_fraSelectRight->hide();
	m_fraSelectBottom->hide();
#endif
	setDragMode( None );
}

int KKWidget::selectItem( QRect rect, bool blAdd )
{
	int rtn = 0;
	bool bl;
	if( !blAdd ) m_item->listView()->clearSelection();
	KKItem* child = (KKItem*)m_item->firstChild();
	while( child )
	{
		if( rect.width() == 0 && rect.height() == 0 )
		{
			bl = child->rect().contains( rect.x(), rect.y() );
		}
		else
		{
			bl = rect.intersects( child->rect() );
		}
		child->listView()->setSelected( child, bl ? true : ( blAdd ? child->isSelected() : false ) );
		if( bl ){
			rtn++;
		}
		child = (KKItem*)child->nextSibling();
	}
	return rtn;
}

void KKWidget::startDrag()
{
#ifdef KKWINDOWOPACITY
	m_fraSelect->move( m_fraSelectPos );
	m_fraSelect->show();
#else
	m_fraSelectLeft->show();
	m_fraSelectLeft->move( m_fraSelectPos );
	m_fraSelectLeft->resize( 1, 1 );
	m_fraSelectTop->show();
	m_fraSelectTop->move( m_fraSelectPos );
	m_fraSelectTop->resize( 1, 1 );
	m_fraSelectRight->show();
	m_fraSelectRight->move( m_fraSelectPos );
	m_fraSelectRight->resize( 1, 1 );
	m_fraSelectBottom->show();
	m_fraSelectBottom->move( m_fraSelectPos );
	m_fraSelectBottom->resize( 1, 1 );
#endif
}

void KKWidget::selectionChanged( bool b )
{
	if( b ) raise();
}

void KKWidget::selectionChanged( KKItem* item, bool b )
{
	m_sizeHandles[item]->setVisible( b );
}

void KKWidget::imageChanged()
{
	if( !m_item->image().isNull() )
	{
		setPaletteBackgroundPixmap( m_item->imageResized() );
	}
	else
	{
		setBackgroundMode( NoBackground );
		setPaletteBackgroundColor( white );
	}
}

void KKWidget::orientationChanged()
{
	setSize( m_item->size() );
	imageChanged();
	emit resized();
}

void KKWidget::insert()
{
	setDragMode( KKWidget::Insert );
	setCursor( crossCursor );
}

void KKWidget::itemInserted( QListViewItem* parent, KKItem* item )
{
	if( m_readonly ) return;
	if( parent != m_item ) return;

	connect( item, SIGNAL( repaint( const QRect& ) ), this, SLOT( slotRepaint( const QRect& ) ) );
	connect( item, SIGNAL( selectionChanged( KKItem*, bool ) ), this, SLOT( selectionChanged( KKItem*, bool ) ) );

	m_sizeHandles[item] = new KKSizeHandler( this, item );
	connect( m_sizeHandles[item], SIGNAL( start() ), document(), SLOT( resizeStart() ) );
	connect( m_sizeHandles[item], SIGNAL( resizing() ), document(), SLOT( resizing() ) );
	connect( m_sizeHandles[item], SIGNAL( end() ), document(), SLOT( resizeEnd() ) );

	if( item->isSelected() ) m_sizeHandles[item]->setVisible( true );
	repaint( item->rect() );
}

void KKWidget::itemRemoved( QListViewItem* parent, KKItem* item )
{
	if( m_readonly ) return;
	if( parent != m_item ) return;

	disconnect( item, SIGNAL( repaint( const QRect& ) ), this, SLOT( slotRepaint( const QRect& ) ) );
	disconnect( item, SIGNAL( selectionChanged( KKItem*, bool ) ), this, SLOT( selectionChanged( KKItem*, bool ) ) );

	disconnect( m_sizeHandles[item], SIGNAL( start() ), document(), SLOT( resizeStart() ) );
	disconnect( m_sizeHandles[item], SIGNAL( resizing() ), document(), SLOT( resizing() ) );
	disconnect( m_sizeHandles[item], SIGNAL( end() ), document(), SLOT( resizeEnd() ) );

	delete m_sizeHandles[item];
	repaint( item->rect() );
	document()->setSelected( m_item, true );
}

KKDoc* KKWidget::document()
{
	return (KKDoc*)m_item->listView();
}
