/***************************************************************************
 *   Copyright (C) 2005 by SUZUKI Tasuku                                   *
 *   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 <kmessagebox.h>
#include <kmainwindow.h>
#include <tkcoloractions.h>
#include <kfiledialog.h>
#include <kcursor.h>

#include "kkddoc.h"
#include "kkdcanvas.h"
#include "kkdsizehandler.h"
#include "kkdpaper.h"
#include "kkdimageitem.h"
#include "kkdtextitem.h"
#include "kkdaddressee.h"
#include "kkdaddress.h"
#include "kkdzipcode.h"
#include "kkdinputtextdialog.h"

#include "../kkaddressbook/kkacontactdata.h"

#define DEFAULT_ITEM_SIZE 20

using namespace KKDesigner;

KKDCanvas::KKDCanvas( KKDItemBase::DrawMode m, KKDPaper* paper, QWidget* parent, const char* name )
	: QWidget( parent, name )
	, m_paper( paper )
	, m_dragMode( None )
	, m_zoom( 1 )
	, m_mode( m )
	, m_data( NULL )
{
	connect( m_paper, SIGNAL( repaint( const QRect& ) ), this, SLOT( slotRepaint( const QRect& ) ) );
	connect( m_paper, SIGNAL( imageChanged() ), this, SLOT( imageChanged() ) );
	connect( document(), SIGNAL( currentChanged( KKDPaper* ) ), this, SLOT( currentChanged( KKDPaper* ) ) );

	for( KKDItemBase* item = m_paper->first(); item; item = m_paper->next() )
	{
		itemInserted( item );
	}

	if( !m_paper->image().isNull() )
	{
// 		setPaletteBackgroundPixmap( m_paper->image() );
	}
	else
	{
		setBackgroundMode( NoBackground );
		setPaletteBackgroundColor( white );
	}
	resetGeometry();
}

KKDCanvas::~KKDCanvas()
{
}

void KKDCanvas::resetGeometry()
{
	setFixedSize( (int)(size().width() * zoom()), (int)(size().height() * zoom()) );
	m_buffer.resize( size() );
}

//BEGIN Properties

const QSize KKDCanvas::size()
{
	return m_paper->size();
}

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

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

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

//END   Properties


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

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

void KKDCanvas::draw( const QPaintDevice* device, QRect rect )
{
	QPainter p;
	p.begin( &m_buffer, device );

	m_paper->drawItem( p, m_mode, m_data );
	for( KKDItemBase* item = m_paper->last(); item; item = m_paper->prev() )
	{
		if( item->rect().intersects( rect ) )
		{
			item->drawItem( p, m_mode, m_data );
		}
	}
	p.end();

	switch( m_dragMode )
	{
		case None:
		case Move:
			bitBlt( this, rect.topLeft(), &m_buffer, rect );
			break;
		case InsertText:
		case InsertImage:
		case InsertAddressee:
		case InsertAddress:
		case InsertZipcode:
		case Select:
		{
			if( m_overlay.width() + m_overlay.height() >= 5 )
			{
				QRect ovr = m_overlay.intersect( rect );
				QPixmap backPixmap( rect.size() );

				QPixmap ovrPixmap( ovr.size() );
				bitBlt( &ovrPixmap, QPoint(0,0), &m_buffer, ovr );
				QImage image( ovrPixmap.convertToImage() );
				image.setAlphaBuffer( true );

				int pixels = image.width() * image.height();
				unsigned int* data = (unsigned int *)image.bits();
				for( int i = 0; i < pixels; ++i )
				{
					data[i] = qRgba( 255, 255, 200, data[i] & 0xFF );
				}
				ovrPixmap.convertFromImage( image );

				QPainter pixPainter( &backPixmap );
				pixPainter.drawPixmap( QPoint(0,0), m_buffer, rect );
				pixPainter.setPen( yellow );
				pixPainter.drawPixmap( ovr.left() - rect.left(), ovr.top() - rect.top(), ovrPixmap );
				pixPainter.drawRect( ovr.left() - rect.left(), ovr.top() - rect.top(), ovr.width(), ovr.height() );
				pixPainter.end();

				bitBlt( this, rect.topLeft(), &backPixmap, backPixmap.rect() );
			}
			else
			{
				bitBlt( this, rect.topLeft(), &m_buffer, rect );
			}
			break;
		}
	}
}

void KKDCanvas::mousePressEvent( QMouseEvent* e )
{
	raise();
	document()->setCurrent( m_paper );
	if( m_mode != KKDItemBase::DesignMode ) return;

	m_fraSelectPos = e->pos();
	switch( m_dragMode )
	{
		case InsertText:
		case InsertImage:
		case InsertAddressee:
		case InsertAddress:
		case InsertZipcode:
		{
			if( !( e->state() & ShiftButton ) )
			{
				if( !m_paper->selected() )
				{
					document()->setSelected( m_paper, true, true );
				}
			}
			m_overlay.setRect( e->pos().x(), e->pos().y(), 0, 0 );
			break;
		}
		default:
		{
			KKDItemBase* item = NULL;
			// Does point has any item?
			for( KKDItemBase* i = m_paper->first(); i; i = m_paper->next() )
			{
				if( i->rect().contains( e->pos() ) )
				{
					item = i;
					break;
				}
			}

			switch( e->button() )
			{
				case LeftButton:
				{
					if( item != NULL )
					{
						if( item->selected() )
						{
							if( e->state() & ControlButton )
							{
								document()->setSelected( item, false, false );
							}
							else
							{
								setDragMode( Move );
							}
						}
						else
						{
							if( e->state() & ControlButton )
							{
								document()->setSelected( item, true, false );
							}
							else
							{
								document()->setSelected( item, true, true );
							}
							setDragMode( Move );
						}
					}
					else
					{
						if( !( e->state() & ShiftButton ) )
						{
							if( !m_paper->selected() )
							{
								document()->setSelected( m_paper, true, true );
							}
						}
						setDragMode( Select );
					}
					break;
				}
				case RightButton:
				{
					if( item != NULL )
					{
						if( item->selected() )
						{
							if( e->state() & ControlButton )
							{
								document()->setSelected( item, false, false );
							}
						}
						else
						{
							if( e->state() & ControlButton )
							{
								document()->setSelected( item, true, false );
							}
							else
							{
								document()->setSelected( item, true, true );
							}
						}
					}
					else
					{
						if( !( e->state() & ShiftButton ) )
						{
							if( !m_paper->selected() )
							{
								document()->setSelected( m_paper, true, true );
							}
						}
					}
					
					bool locked = true;
					int type = 0;
					for( KKDItemBase* i = m_paper->first(); i; i = m_paper->next() )
					{
						if( i->selected() && !i->locked() )
						{
							if( !i->locked() ) locked = false;
							type |= ( 1 << i->type() );
						}
					}
					
					KPopupMenu* menu = new KPopupMenu( this );
					
					if( !locked )
					{
						action( "edit_cut" )->plug( menu );
						action( "edit_copy" )->plug( menu );
						menu->insertSeparator();
						action( "raise" )->plug( menu );
						action( "lower" )->plug( menu );
						menu->insertSeparator();
						if( type & 1 << KKDItemBase::TextItem 
						|| type & 1 << KKDItemBase::Zipcode
						|| type & 1 << KKDItemBase::Address
						|| type & 1 << KKDItemBase::Addressee
						)
						{
							action( "fontfamily" )->plug( menu );
							action( "fontsize" )->plug( menu );
							action( "fontbold" )->plug( menu );
							action( "fontitalic" )->plug( menu );
							action( "fontunderline" )->plug( menu );
							action( "fontstrikeout" )->plug( menu );
							action( "color" )->plug( menu );
							menu->insertSeparator();
						}
						if( type & 1 << KKDItemBase::TextItem )
						{
							action( "aligntop" )->plug( menu );
							action( "alignmiddle" )->plug( menu );
							action( "alignbottom" )->plug( menu );
							action( "alignleft" )->plug( menu );
							action( "aligncenter" )->plug( menu );
							action( "alignright" )->plug( menu );
							menu->insertSeparator();
						}
						if( type & 1 << KKDItemBase::Address
						 || type & 1 << KKDItemBase::Addressee
						  )
						{
							action( "vertical" )->plug( menu );
							action( "horizontal" )->plug( menu );
							menu->insertSeparator();
						}

						if( type & 1 << KKDItemBase::ImageItem )
						{
							action( "imageerase" )->plug( menu );
							menu->insertSeparator();
						}
						action( "remove" )->plug( menu );
						menu->insertSeparator();
					}
					if( type == 0 && !m_paper->locked() )
					{
						action( "imageerase" )->plug( menu );
						menu->insertSeparator();
						action( "edit_paste" )->plug( menu );
						menu->insertSeparator();
					}
					action( "locked" )->plug( menu );
					menu->insertSeparator();
					action( "edit_select_all" )->plug( menu );
					action( "edit_deselect" )->plug( menu );
					menu->exec( e->globalPos() );
					break;
				}
				default:
					break;
			}
			break;
		}
	}
}

void KKDCanvas::mouseMoveEvent( QMouseEvent* e )
{
	if( m_mode != KKDItemBase::DesignMode ) return;

	switch( m_dragMode )
	{
		case None:
			break;
		case InsertText:
		case InsertImage:
		case InsertAddressee:
		case InsertAddress:
		case InsertZipcode:
		case Select:
		{
			if( m_overlay.width() + m_overlay.height() >= 5 )
			{
				setCursor( KCursor::crossCursor() );
			}
			int x = 0;
			int y = 0;
			int w = e->pos().x() - m_fraSelectPos.x();
			int 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;
			}
			QRect r( m_overlay );
			m_overlay.setRect( x, y, w, h );
			slotRepaint( m_overlay | r );
			break;
		}
		case Move:
		{
			QPtrList<KKDItemBase> list( *m_paper );
			for( KKDItemBase* item = list.first(); item; item = list.next() )
			{
				if( item->selected() )
				{
					item->setLocation( item->location() + e->pos() - m_fraSelectPos );
				}
			}
			m_fraSelectPos = e->pos();
			document()->moving();
			break;
		}
	}
}

void KKDCanvas::mouseReleaseEvent( QMouseEvent* e )
{
	if( m_mode != KKDItemBase::DesignMode ) return;

	switch( m_dragMode )
	{
		case Move:
			document()->moveEnd();
			setCursor( KCursor::arrowCursor() );
			break;
		case Select:
		{
			int x = 0;
			int y = 0;
			int w = e->pos().x() - m_fraSelectPos.x();
			int 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;
			}
			document()->selectRect( QRect( x, y, w, h ), !( e->state() & ShiftButton ) );
			setCursor( KCursor::arrowCursor() );
			break;
		}
		case InsertText:
		case InsertImage:
		case InsertAddressee:
		case InsertAddress:
		case InsertZipcode:
		{
			int x = 0;
			int y = 0;
			int w = e->pos().x() - m_fraSelectPos.x();
			int 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;
			}
			KKDItemBase* item = NULL;
			switch( m_dragMode )
			{
				case InsertText:
				case InsertAddressee:
				case InsertAddress:
				case InsertZipcode:
				{
					QFont font;
					font.setFamily( ((KFontAction*)action( "fontfamily" ))->font() );
					font.setPointSize( ((KFontSizeAction*)action( "fontsize" ))->fontSize() );
					font.setBold( ((KToggleAction*)action( "fontbold" ))->isChecked() );
					font.setItalic( ((KToggleAction*)action( "fontitalic" ))->isChecked() );
					font.setUnderline( ((KToggleAction*)action( "fontunderline" ))->isChecked() );
					font.setStrikeOut( ((KToggleAction*)action( "fontstrikeout" ))->isChecked() );
					QColor color( ((TKSelectColorAction*)action( "color" ))->color() );
					QString str;
					if( m_dragMode == InsertText )
					{
						str = KKDInputTextDialog::input( i18n( "New" ), this );
						if( str.isNull() ) break;
					}
					Orientation o;
					switch( m_dragMode )
					{
						case InsertText:
						{
							AlignmentFlags align;
							if( ((KRadioAction*)action( "alignleft" ))->isChecked() )
							{
								align = AlignLeft;
								o = Horizontal;
							}
							else if( ((KRadioAction*)action( "aligncenter" ))->isChecked() )
							{
								align = AlignHCenter;
								o = Horizontal;
							}
							else if( ((KRadioAction*)action( "alignright" ))->isChecked() )
							{
								align = AlignRight;
								o = Horizontal;
							}
							else if( ((KRadioAction*)action( "aligntop" ))->isChecked() )
							{
								align = AlignTop;
								o = Vertical;
							}
							else if( ((KRadioAction*)action( "alignmiddle" ))->isChecked() )
							{
								align = AlignVCenter;
								o = Vertical;
							}
							else if( ((KRadioAction*)action( "alignbottom" ))->isChecked() )
							{
								align = AlignBottom;
								o = Vertical;
							}
							else
							{
								align = AlignLeft;
								o = Horizontal;
							}
							item = new KKDTextItem( QPoint( x, y ), QSize( w, h ), font, color, o, str, align, m_paper );
							break;
						}
						case InsertZipcode:
							item = new KKDZipcode( QPoint( x, y ), QSize( w, h ), font, color, Horizontal, m_paper );
							break;
						default:
						{
							Orientation o;
							if( ((KRadioAction*)action( "horizontal" ))->isChecked() )
							{
								o = Horizontal;
							}
							else if( ((KRadioAction*)action( "vertical" ))->isChecked() )
							{
								o = Vertical;
							}
							else
							{
								o = Horizontal;
							}
							switch( m_dragMode )
							{
								case InsertAddressee:
									item = new KKDAddressee( QPoint( x, y ), QSize( w, h ), font, color, o, m_paper );
									break;
								case InsertAddress:
									item = new KKDAddress( QPoint( x, y ), QSize( w, h ), font, color, o, m_paper );
									break;
							}
							break;
						}
						break;
					}
					break;
				}
				case InsertImage:
				{
					KURL url = KFileDialog::getImageOpenURL( ":image", this );
					if( url.isEmpty() ) break;
					item = new KKDImageItem( QPoint( x, y ), QSize( w, h ), m_paper );
					((KKDImageItem*)item)->setImage( url.path() );
					break;
				}
				default:
					break;
			}
			if( item )
			{
				m_paper->prepend( item );
				itemInserted( item );
				document()->setSelected( item, true, true );
				document()->insert();
			}
			setCursor( KCursor::arrowCursor() );
			break;
		}
		default:
			break;
	}
	QRect r( m_overlay );
	m_overlay.setRect( 0, 0, 0, 0 );
	slotRepaint( r );
	setDragMode( None );
}

void KKDCanvas::mouseDoubleClickEvent( QMouseEvent* /*e*/ )
{
	if( m_mode != KKDItemBase::DesignMode ) return;
	if( document()->locked() ) return;

	int type = 0;	// Paper:0, Text:1, Image:2
	QString str( QString::null );
	QPtrList<KKDItemBase> list;
	for( KKDItemBase* item = m_paper->first(); item; item = m_paper->next() )
	{
		if( item->selected() )
		{
			if( item->isA( "KKDesigner::KKDTextItem" ) )
			{
				if( type != 0 && type != 1 ) return;
				type = 1;
				if( str.isNull() )
				{
					str = ((KKDTextItem*)item)->text();
				}
				else if( !str.isEmpty() && str != ((KKDTextItem*)item)->text() )
				{
					str = "";
				}
			}
			else if( item->isA( "KKDesigner::KKDImageItem" ) )
			{
				type = 2;
				if( type != 0 && type != 2 ) return;
			}
			list.append( item );
		}
	}
	switch( type )
	{
		case 1:
			str = KKDInputTextDialog::input( str, this );
			if( !str.isNull() )
			{
				document()->setText( str );
			}
			break;
		case 0:
		case 2:
		{
			KURL url = KFileDialog::getImageOpenURL( ":image", this );
			if( !url.isEmpty() )
			{
				document()->setImage( url.path() );
			}
			break;
		}
	}
}

void KKDCanvas::setDragMode( DragMode m )
{
	m_dragMode = m;
	switch( m )
	{
		case None:
			break;
		case Move:
			setCursor( KCursor::handCursor() );
			document()->moveStart();
			break;
		case InsertText:
		case InsertImage:
		case InsertAddressee:
		case InsertAddress:
		case InsertZipcode:
			break;
		case Select:
			break;
	}
}

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

void KKDCanvas::insertText()
{
	m_dragMode = InsertText;
	setCursor( KCursor::crossCursor() );
}

void KKDCanvas::insertImage()
{
	m_dragMode = InsertImage;
	setCursor( KCursor::crossCursor() );
}

void KKDCanvas::insertAddressee()
{
	m_dragMode = InsertAddressee;
	setCursor( KCursor::crossCursor() );
}

void KKDCanvas::insertAddress()
{
	m_dragMode = InsertAddress;
	setCursor( KCursor::crossCursor() );
}

void KKDCanvas::insertZipcode()
{
	m_dragMode = InsertZipcode;
	setCursor( KCursor::crossCursor() );
}

void KKDCanvas::itemInserted( KKDItemBase* item, KKDPaper* paper )
{
	if( paper != 0 && paper != m_paper ) return;
	if( m_mode != KKDItemBase::DesignMode ) return;

	connect( item, SIGNAL( repaint( const QRect& ) ), this, SLOT( slotRepaint( const QRect& ) ) );

	m_sizeHandles[item] = new KKDSizeHandler( 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() ) );
	connect( item, SIGNAL( selectedChanged() ), m_sizeHandles[item], SLOT( setVisible() ) );

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

void KKDCanvas::itemRemoved( KKDItemBase* item, KKDPaper* paper )
{
	if( paper != 0 && paper != m_paper ) return;
	if( m_mode != KKDItemBase::DesignMode ) return;

	disconnect( item, SIGNAL( repaint( const QRect& ) ), this, SLOT( slotRepaint( const QRect& ) ) );

	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() ) );
	disconnect( item, SIGNAL( selectedChanged() ), m_sizeHandles[item], SLOT( setVisible() ) );

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

KKDDoc* KKDCanvas::document()
{
	return (KKDDoc*)m_paper->parent();
}

void KKDCanvas::currentChanged( KKDPaper* p )
{
	if( m_paper == p ) raise();
}

KAction* KKDCanvas::action( const QString& name )
{
	QPtrList< KMainWindow >* list = KMainWindow::memberList;
	return ((KMainWindow*)list->at(0))->actionCollection()->action( name );
}

void KKDCanvas::setContactData( const KKAddressBook::KKAContactData* d )
{
	if( m_data )
	{
		disconnect( m_data, SIGNAL( updated() ), this, SLOT( drawData() ) );
	}
	m_data = d;
	drawData();
	if( m_data )
	{
		connect( m_data, SIGNAL( updated() ), this, SLOT( drawData() ) );
	}
}

void KKDCanvas::drawData()
{
	QPtrList<KKDItemBase> items( *m_paper );
	for( KKDItemBase* item = items.first(); item; item = items.next() )
	{
		if( item->isA( "KKDesigner::KKDAddress" )
		 || item->isA( "KKDesigner::KKDAddressee" )
		 || item->isA( "KKDesigner::KKDZipcode" ) )
		{
			repaint( item->rect(), false );
		}
	}
}
