/***************************************************************************
 *   Copyright (C) 2004, 2005 Thomas Nagy                                  *
 *   tnagy2^8@yahoo.fr                                                     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License version 2        *
 *   as published by the Free Software Foundation (see COPYING)            *
 *                                                                         *
 *   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.                          *
 ***************************************************************************/

#include <math.h>
#include <stdlib.h>

#include <qsimplerichtext.h>
#include <qpainter.h>
#include <qwmatrix.h>

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

#include "aux.h"
#include "settings.h"
#include "DDataItem.h"
#include "DDataControl.h"
#include "DCanvasView.h"
#include "DCanvasItem.h"
#include "DCanvasLink.h"
#include "DCanvasPos.h"
#include "DCanvasFlag.h"

static const int offset = 4;
static const int maxwidth = 50;
static const int itemz = 5;
static const int linkz = 4;
static const int flagz = 7;

QValueList<DRefRecord*> DCanvasItem::s_allreferences;

DCanvasItem::DCanvasItem(DCanvasView * parent, int id) : 
	QCanvasRectangle(parent->canvas()), DGuiItem( parent, id)
{
	m_canvasview = static_cast<DCanvasView*> (parent);

	m_link = new DCanvasLink( canvas(), this );
	m_link->setZ(linkz);
	m_link->show();

	m_pos = new DCanvasPos( canvas(), this );
	//m_pos->hide();
	m_pos->setZ(itemz);

	setSize(50, 50);
	setSelected(false);
	setDisplayIdx(false);

	m_lastupdate = 0;

	updatePos();
	show();
}

DCanvasItem::~DCanvasItem()
{
	unsigned int i=0;
	while (i<s_allreferences.count())
	{
		// delete all references to this particular item
		DRefRecord* record = s_allreferences[i];
		if (record->m_orig == Id())
		{
			delete record->m_ref;
			s_allreferences.remove(record);
		}
		else i++;
	}

	QValueList<DCanvasFlag*> upd = m_flagmap.values();
	for (unsigned int i=0; i<upd.count(); ++i)
	{
		delete upd[i];
	}
	
	m_link->hide();
	delete m_link;

	m_pos->hide();
	delete m_pos;

	hide();
}

int DCanvasItem::rtti() const
{
	return Rtti_DCanvasItem;
}

void DCanvasItem::setSelected(bool b)
{
	// kdWarning()<<"DCanvasItem::setSelected - "<<Id()<<" "<<b<<endl;
	if (b) setZ(itemz+1);
	else setZ(itemz);
	QCanvasRectangle::setSelected(b);
}

void DCanvasItem::setOffset(const QPoint & p)
{
	m_pressOffset.setX( (int) (p.x() - x()) );
	m_pressOffset.setY( (int) (p.y() - y()) );
}

void DCanvasItem::moveOffset(const QPoint & p)
{
	int newX = p.x() - m_pressOffset.x();
	int newY = p.y() - m_pressOffset.y();

	move(newX, newY);
}

void DCanvasItem::move(double xx, double yy)
{
	QCanvasItem::move(xx, yy);
	m_pos->updatePos();
	m_pos->move( x() - m_pos->width() - 2, y() );

	if (!data()) return;
	else data()->setXY( (DDataItem::Coord) x(), (DDataItem::Coord) y());
	// FIXME
	updateLink();
	updateFlagPos();

	m_canvasview->extendIfNecessary(xx, yy, xx+width(), yy+height());
}

void DCanvasItem::updateLink()
{
	m_link->setDest( m_canvasview->canvasItem( data()->Parent() ) );
	m_link->updatePos();
	for (unsigned int i=0; i<m_references.count(); i++)
	{
		m_references[i]->updatePos();
		// FIXME
		//m_references[i]->updateColor();
	}
	updateRefs();
}

void DCanvasItem::updatePos()
{
	//kdWarning()<<"updatepos called"<<endl;

	invalidate();

	DDataItem *item = data();

	QWMatrix mat = m_canvasview->worldMatrix();

	// compute the dimensions first
	QString msg = "<html><span style=\"color:";
	msg.append( data()->textColor().name() );
	msg.append("\">");
	msg.append( DDataItem::protectXML(item->dispmsg()) );
	msg.append("</span></html>");

	QSimpleRichText rt(msg, item->m_defaultFont);
	rt.adjustSize();

	m_link->updateColor();
	int pixwidth = 0, pixheight = 0; // dimensions of the canvas item

	pixwidth = MAX( rt.width(), item->m_pixtb.width() ) + 2*offset;
	if (! control()->m_picturesOnCanvas )
		pixwidth = MAX( rt.width(), item->m_pixtb.width() ) + 2*offset;

	pixheight = item->m_pixtb.height() + rt.height() + 2*offset;
	if (! control()->m_picturesOnCanvas )
		pixheight = rt.height() + 2*offset;

	setSize( pixwidth, pixheight);

	// force the position
	if (item) move(item->x(), item->y());
	else move(x(), y());
	m_pos->updatePos();


	// flags - in practice, the user can only set one flag at a time, hence this condition
	if (m_flagmap.count() != data()->countFlags())
	{
		QMap<int, DCanvasFlag*> tmpflags = m_flagmap;
		m_flagmap.clear();

		for (unsigned int flag=0; flag<30; ++flag)
		{
			if (!data()->hasFlag(flag)) continue;

			if (tmpflags.contains(flag))
			{
				m_flagmap[flag] = tmpflags[flag];
				tmpflags.remove(flag);
			}
			else
			{
				//kdDebug()<<"adding flag "<<flag<<endl;
				DCanvasFlag *f = new DCanvasFlag(canvas(), this, flag);
				m_flagmap[flag] = f;
				f->setZ( flagz );
			}
		}

		// now clear the flags remaining, if any
		QValueList<DCanvasFlag*> todelete = tmpflags.values();
		for (unsigned int i=0; i<todelete.count(); ++i)
		{
			//kdDebug()<<"deleting flag"<<endl;
			delete todelete[i];
		}
		updateFlagPos();
	}

	update();
}

void DCanvasItem::loadPos()
{
	move(data()->x(), data()->y());
}

bool DCanvasItem::onWidget(const QPoint & p)
{
	return ( x() <= p.x() && x() + width() >= p.x() && y() <= p.y() && y() + height() >= p.y() );
}

void DCanvasItem::drawShape(QPainter &p)
{
	DDataItem *item = data();
	if (!item)
	{
		kdWarning()<<"BUG in DCanvasItem::drawShape"<<endl;
		p.setBrush( white );
		p.setPen( black );
		p.drawRoundRect( (int) x() , (int) y() , width(), height(), 0, 0 );
		return;
	}

	// compute the dimensions first
	QString msg = "<html><span style=\"color:";
	msg.append( item->textColor().name() );
	msg.append("\">");
	msg.append( DDataItem::protectXML(item->dispmsg()) );

	//QString msg = "<html>";
	//QString msg2 = DDataItem::protectXML(item->dispmsg());
	//msg2 += "<span style=\"color:#ff0000; background-color:#00ff00;\">e</span>a";
	//msg.append( msg2 );

	msg.append("</span></html>");
	//msg.append("</html>");


	QSimpleRichText rt(msg, item->m_defaultFont);
	rt.adjustSize();
	m_link->updateColor();

	int pixwidth = 0, pixheight = 0; // dimensions of the canvas item

	pixwidth = MAX( rt.width(), item->m_pixtb.width() ) + 2*offset;
	if (! control()->m_picturesOnCanvas )
		pixwidth = MAX( rt.width(), item->m_pixtb.width() ) + 2*offset;

	pixheight = item->m_pixtb.height() + rt.height() + 2*offset;
	if (! control()->m_picturesOnCanvas )
		pixheight = rt.height() + 2*offset;

	//setSize( pixwidth, pixheight);

	//m_displayedpix.resize( pixwidth, pixheight);

	// set the painter
	//QPainter p(&m_displayedpix);
	p.setFont( item->m_defaultFont );

	QColorGroup cg;

	cg.setColor( QColorGroup::Background, item->fillColor() );
	p.setBrush( item->fillColor() );
	p.setPen( item->outlineColor() );

	// paint
	p.drawRoundRect( rect(), 0, 0 );

	if ( control()->m_picturesOnCanvas )
		p.drawPixmap( rect().topLeft() + QPoint((pixwidth - item->m_pixtb.width())/2, offset) , item->m_pixtb);


	int offsetheight = offset;
	if ( control()->m_picturesOnCanvas )
		offsetheight = item->m_pixtb.height();

	//kdWarning()<<"calling heavy functions"<<endl;

	QPoint topf = rect().topLeft();
	rt.draw(&p, topf.x() + offset+1, topf.y() + offsetheight, rect(), cg);

	if (item->Parent() == DItem::NOITEM
			&& item->colorScheme() != Settings::EnumColorMode::root_
			&& item->colorScheme() != Settings::EnumColorMode::default_)
	{
		p.setBrush( item->realFillColor() );
		p.drawEllipse( topf.x() + width() - 12, topf.y() + 5, 7, 7);
	}

	if (!item->m_text.isNull()  && item->m_text.length() > 15)
	{
		// draw a triangle, telling if the object has a text attachment
		QPointArray pa(3);
		pa[0] = rect().topRight();
		pa[1] = pa[0]+QPoint(-6,0);
		pa[2] = pa[0]+QPoint(0,6);
		p.setBrush(item->outlineColor());
		p.drawPolygon(pa);
	}

	if (isSelected())
	{
		// draw 4 small squares, meaning that the object is selected
		QBrush brush(blue);
		int s = 6;
		int w = rect().width();
		int h = rect().height();
		p.fillRect(topf.x()        , topf.y()        , s, s, brush);
		p.fillRect(topf.x()        , topf.y() + h - s, s, s, brush);
		p.fillRect(topf.x() + w - s, topf.y()        , s, s, brush);
		p.fillRect(topf.x() + w - s, topf.y() + h - s, s, s, brush);
	}
}

bool DCanvasItem::displayChildIdx() const
{
	return m_displaychildidx;
}

void DCanvasItem::setDisplayIdx(bool b)
{
	m_displaychildidx = b;
	m_pos->setVisible(b);
}

int DCanvasItem::numChild()
{
	if (data()->Parent() == DItem::NOITEM)
		return DItem::NOITEM;

	return m_canvasview->dataItem(data()->Parent())->childIdx(Id());
}

void DCanvasItem::setSize( int w, int h )
{
	if (width() != w || height() != h)
	{
		// kdWarning()<<"DCanvasItem::setSize true"<<endl;
		QCanvasRectangle::setSize( w, h );
		//updatePos();
		updateFlagPos();
		m_canvasview->dataTree()->notifyChildren(Id());
	}
}

void DCanvasItem::updateFlagPos()
{
	// and then update the flags position
	DDataItem::Coord posx = x()+width();
	DDataItem::Coord posy = y() - 2;
	QValueList<DCanvasFlag*> upd = m_flagmap.values();
	for (unsigned int i=0; i<upd.count(); ++i)
	{
		upd[i]->move( posx -2 - upd[i]->width(), posy - upd[i]->height() );
		posx -= 2 + upd[i]->width();
	}
}

void DCanvasItem::expandSubtree(double angleStart, double angleEnd)
{
	double totalsize = (double) data()->countFamily();

	// find the item angles
	// give an amount of space proportional to the amount of children
	double substart=angleStart, subend=0;
	for (unsigned int i=0; i<data()->countChildren(); i++)
	{
		DDataItem *item = (DDataItem*) control()->Item( data()->childNum(i) );
		int subsize = item->countFamily();
		subend = substart + (subsize * (angleEnd-angleStart)) / totalsize;
		DCanvasItem *citem = m_canvasview->canvasItem( data()->childNum(i) );

		citem->m_angle = substart + (subend - substart)/2.;
		citem->move( x() + width()/2. - citem->width()/2. + 130*cos(citem->m_angle), 
				y() + height()/2. - citem->height()/2. + 130*sin(citem->m_angle) );

		citem->expandSubtree(substart, subend);

		substart = subend;
	}
}

DDataItem::Coord DCanvasItem::sizeH()
{
	if ( data()->countChildren() > 0 )
	{
		DDataItem::Coord size = 0;
		for (unsigned int i=0; i<data()->countChildren(); i++)
		{
			DCanvasItem *citem = m_canvasview->canvasItem( data()->childNum( i ) );
			size += citem->sizeH();
		}

		if (size > height() + NESTING)
			return size;
		else
			return height() + NESTING;
	}

	return height() + NESTING;
}

void DCanvasItem::setPosition(DDataItem::Coord xx, DDataItem::Coord yy, bool rightside)
{
	DDataItem::Coord tmpsize = sizeH();
	DDataItem::Coord starty = yy;

	DDataItem::Coord oldx = data()->x();
	DDataItem::Coord oldy = data()->y();
	DDataItem::Coord newx = xx;
	DDataItem::Coord newy = 0;
	
	// yy is the start of the block
	if (rightside && Settings::reorgCyclicMap()) 
		newy = yy+(0 - tmpsize + NESTING)/2-height()/2;
	else 
		newy = yy+(tmpsize-NESTING)/2-height()/2;

	move(newx, newy);

	// necessary for undo/redo
	m_canvasview->dataTree()->registerMapPositionChange(Id(), oldx, oldy, newx, newy);

	if ( data()->countChildren() <= 0 ) return;

	for (unsigned int i=0; i<data()->countChildren(); i++)
	{
		DCanvasItem* citem = m_canvasview->canvasItem( data()->childNum( i ) );

		if (!citem)
		{
			kdWarning()<<"bug DCanvasItem::setPosition "<<endl;
			continue;
		}

		DDataItem::Coord xpos = 0;
		if (rightside)
			xpos = x() + width() + BINSPACE;
		else
			xpos = x() - citem->width() - BINSPACE;

		citem->setPosition(xpos, starty, rightside);

		if (rightside && Settings::reorgCyclicMap()) starty -= citem->sizeH();
		else starty += citem->sizeH();
	}
}

void DCanvasItem::addRef(int dest)
{
	DCanvasRef* ref = new DCanvasRef(canvas(), this);
	ref->setDest( m_canvasview->canvasItem(dest) );
	ref->show();
	ref->updatePos();
	DRefRecord* record = new DRefRecord(Id(), dest, ref);
	s_allreferences += record;
	m_references += ref;
}

void DCanvasItem::rmRef(int dest)
{
	unsigned int i=0;
	while (i<s_allreferences.count())
	{
		DRefRecord* record = s_allreferences[i];
		if (record->m_orig == Id() && record->m_dest == dest)
		{
			m_references.remove(record->m_ref);
			delete record->m_ref;
			s_allreferences.remove(record);
		}
		else i++;
	}
}

void DCanvasItem::updateRefs()
{
	for (unsigned int i=0; i<s_allreferences.count(); i++)
	{
		DRefRecord* record = s_allreferences[i];
		if (record->m_dest == Id())
		{
			record->m_ref->updatePos();
		}
	}
}

const DDataControl* DCanvasItem::control()
{
	return m_canvasview->dataTree();
}

void DCanvasItem::updateRefColor()
{
	for (unsigned int i=0; i<s_allreferences.count(); i++)
	{
		// delete all references to this particular item
		DRefRecord* record = s_allreferences[i];
		record->m_ref->updateColor();
	}
}
