/***************************************************************************
 *   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 <qtextstream.h>
#include <qcolor.h>
#include <qfont.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qdir.h>

#include <kio/netaccess.h>
#include <kio/job.h>

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

%:include "aux.h"

#include "DDataControl.h"
#include "settings.h"
#include "DDataItem.h"

DDataItem::DDataItem(DDataControl *manager, int id) : DItem(id)
{
	m_manager = manager;
	m_parent = NOITEM;
	m_lastchild = 0; // this is an index

	m_defaultFont = m_manager->m_canvasFont;

	m_x = 0;
	m_y = 0;

	m_colorscheme = Settings::EnumColorMode::default_;

	m_pix.resize(0, 0);
	m_pixtb.resize(0, 0);
}

DDataItem::~DDataItem()
{
	m_children.clear();
}

QString DDataItem::dispmsg()
{
	QString msg = i18n("Empty item");
	if (m_summary.length() > 0) msg = m_summary;
	else if (m_piccaption.length() > 0) msg = m_piccaption;
	return msg;
}

void DDataItem::addChild(int id)
{
	if ( id == Id() ) return;

	m_children.push_back(id);
}

void DDataItem::setParent(int id)
{
	if ( id == Id() )
	{
		kdWarning()<<"BUG: attempted to set an item's parent to itself"<<endl;
		return;
	}

	m_parent = id;
}

void DDataItem::setNoParent()
{
	setParent(NOITEM);
}

void DDataItem::removeChild(int id)
{
	if ( id == Id() ) return;
	m_children.remove( id );
}

unsigned int DDataItem::countChildren() const
{
	return m_children.count();
}

int DDataItem::childNum(unsigned int idx) const
{
	if (idx >= 0 && idx < countChildren()) return m_children[idx]; 
	else return NOITEM;
}

unsigned int DDataItem::childIdx(unsigned int id) const
{
	return m_children.findIndex(id);
}

void DDataItem::setChildIdx(unsigned int id, unsigned int pos)
{
	if (pos <0 || pos >= countChildren()) return;
	if (! m_children.contains(id) ) return;

	// Remove the element temporarily
	unsigned int cur_idx = m_children.findIndex(id);
	QValueListIterator<int> cur_itr = m_children.at(cur_idx);
	m_children.remove(cur_itr);

	// Re-insert the element
	if (pos >= m_children.count())
	{
		// Since the list is now missing an element, we need to append
		// rather than try to use an out-of-range iterator index.
		m_children.append(id);
	}
	else
	{
		QValueListIterator<int> new_itr = m_children.at(pos);
		m_children.insert(new_itr, id);
	}
}

void DDataItem::setXY(DDataItem::Coord x, DDataItem::Coord y)
{
	//kdDebug()<<"setting item id "<<Id()<<" to position "<<x<<" "<<y<<endl;
	m_x = x;
	m_y = y;
}

void DDataItem::printSelf(QTextStream& s)
{
	s<<"<item>\n";

	// id
	s<<" <id>"<<Id()<<"</id>\n";

	// position on canvas
	s<<" <xpos>"<<m_x<<"</xpos>"<<"<ypos>"<<m_y<<"</ypos>\n";

	// features
	s<<" <colorscheme>"<<m_colorscheme<<"</colorscheme>\n";
	s<<" <defaultfont>"<<m_defaultFont.toString()<<"</defaultfont>\n";
	s<<" <fillcolor>"<<m_fillcolor.name()<<"</fillcolor>\n";
	s<<" <outlinecolor>"<<m_outlinecolor.name()<<"</outlinecolor>\n";
	s<<" <textcolor>"<<m_textcolor.name()<<"</textcolor>\n";


	// data - user-provided data so we must be careful
	s<<" <text>"<<DDataItem::protectXML(m_text)<<"</text>\n";
	s<<" <summary>"<<DDataItem::protectXML(m_summary)<<"</summary>\n";
	s<<" <comment>"<<DDataItem::protectXML(m_comment)<<"</comment>\n";    


	s<<" <picurl>"<<DDataItem::protectXML(m_picurl)<<"</picurl>\n";
	s<<" <piccaption>"<<DDataItem::protectXML(m_piccaption)<<"</piccaption>\n";

	// family :)
	s<<" <parent>"<<Parent()<<"</parent>\n";
	for (unsigned int i=0; i<m_children.count(); i++)
		s<<" <child>"<<m_children[i]<<"</child>\n";

	// references
	for (unsigned int i=0; i<m_references.count(); i++)
	{
		DRef* ref = m_references[i];
		if (!m_manager->Item(ref->m_ref)) continue;
		s<<" <ref dest=\""<<ref->m_ref<<"\" />\n";
	}

	// links
	for (unsigned int i=0; i<m_urllist.count(); i++)
	{
		s<<" <link url=\"" <<DDataItem::protectXML(m_urllist[i].m_url);
		s<<"\" caption=\"" <<DDataItem::protectXML(m_urllist[i].m_caption) <<"\" />\n";
	}

	// flags
	for (unsigned int i=0; i<m_flags.count(); i++)
	{
		s<<" <flag>"<<QString::number( m_flags[i] )<<"</flag>\n";
	}
	
	// that's all folks
	s<<"</item>\n";
}


QString DDataItem::protectXML(const QString & str)
{
	QString ret = str;

	ret.replace( "&", "&amp;");
	ret.replace( "<", "&lt;");
	ret.replace( ">", "&gt;");
	ret.replace( "\'", "&apos;" );
	ret.replace( '\"', "&quot;");

	return ret;
}

QString DDataItem::printXMLTag(const QString & tag, const QString & str, int padding)
{
	QString ret;
	for (int i=0; i<padding; i++) ret.append(" ");

	ret.append("<");
	ret.append(tag);
	ret.append(">");

	ret.append(protectXML(str));

	ret.append("</");
	ret.append(tag);
	ret.append(">\n");

	return ret;
}

void DDataItem::setColorScheme(int c)
{
	m_manager->registerMapColorChange( Id(), m_colorscheme, c, m_fillcolor, m_fillcolor );
	m_colorscheme = c;

	if (Parent() == NOITEM && m_manager->isUndoRedoLocked() && c!=Settings::EnumColorMode::default_)
		KMessageBox::information( 0, 
			i18n("The item is a root item, so the color will only appear when it becomes linked to a parent mindmap item"),
			0, "inform_user_when_root_item_and_color_changes");
}

void DDataItem::setCustomColors(QColor c1, QColor c2, QColor c3)
{
	m_manager->registerMapColorChange( Id(), m_colorscheme, Settings::EnumColorMode::custom_, m_fillcolor, c1 );
	m_colorscheme = Settings::EnumColorMode::custom_;

	m_fillcolor = c1;
	m_outlinecolor = c2;
	m_textcolor = c3;

	if (Parent() == NOITEM && m_manager->isUndoRedoLocked()) KMessageBox::information( 0, 
			i18n("The item is a root item, so the color will only appear when it becomes linked to a parent mindmap item"),
			0, "inform_user_when_root_item_and_color_changes");
}

QColor DDataItem::fillColor()
{
	if (Parent() == NOITEM) return Settings::fillcolor( Settings::EnumColorMode::root_ );
	return realFillColor();
}

QColor DDataItem::realFillColor()
{
	if (m_colorscheme == Settings::EnumColorMode::custom_) return m_fillcolor;
	return Settings::fillcolor( m_colorscheme ); 
}

QColor DDataItem::outlineColor()
{
	if (Parent() == NOITEM) return Settings::outlinecolor( Settings::EnumColorMode::root_ );
	if (m_colorscheme == Settings::EnumColorMode::custom_) return m_outlinecolor;
	return Settings::outlinecolor( m_colorscheme );
}

QColor DDataItem::textColor()
{
	if (Parent() == NOITEM) return Settings::textcolor( Settings::EnumColorMode::root_ );
	if (m_colorscheme == Settings::EnumColorMode::custom_) return m_textcolor;
	return Settings::textcolor( m_colorscheme );
}

void DDataItem::clearPix()
{
	QString localfile = m_manager->getTmpDir()->absPath()+"/"+"pic-"+QString::number(Id());
	KIO::del(KURL(localfile+".png"), false, false);
	KIO::del(KURL(localfile+".jpg"), false, false);
	
	m_picfilename = QString::null;
	m_picurl = QString::null;
	m_pix.resize(0, 0);
	m_pixtb.resize(0, 0);
}

void DDataItem::loadPix(const QString & srcfilename)
{
	//kdWarning()<<"DDataItem::loadPix "<<srcfilename<<endl;

	// get the file name
	bool defined = false;
	m_picfilename = "pic-" + QString::number(Id());
	if (srcfilename.endsWith(".png", false))
	{
		m_picfilename.append(".png");
		defined = true;
	}
	else if (srcfilename.endsWith(".jpg", false) || srcfilename.endsWith(".jpeg", false) )
	{
		m_picfilename.append(".jpg");
		defined = true;
	}

	if (!defined)
	{
		//kdWarning()<<"unknown picture type or no picture given at DDataItem::loadPix"<<endl;
		return;
	}

	// load the file
	m_pix.resize(0, 0);
	m_pixtb.resize(0, 0);

	QString localfile = m_manager->getTmpDir()->absPath()+"/"+m_picfilename;

	// copy the file in the cache directory
	if (KURL(srcfilename) != KURL(localfile))
	{
		//kdWarning()<<"(deleting) "<<localfile<<" and picfile is supposed to be "<<m_picfilename<<endl;
		/*KIO::del( KURL(localfile) );
		  if (KURL(srcfilename).isLocalFile())
		  KIO::copy( KURL(srcfilename), KURL(localfile), true );
		  else*/
		KIO::NetAccess::file_copy( KURL(srcfilename), KURL(localfile), -1, true );
	}

	QImage img( localfile );
	if (img.isNull()) // corrupted file ?
	{
		kdWarning()<<"cannot load the file, removing it from the project directory"<<endl;
		KIO::del( KURL(localfile), false, false );
		return;
	}

	// load the thumbnail and the pixmap for work
	m_pix.convertFromImage( img );

	if (! m_pix.isNull())
		m_pixtb.convertFromImage( 
				img.smoothScale( m_manager->m_pixSize, m_manager->m_pixSize, QImage::ScaleMin )  );
}

int DDataItem::countFamily() const
{
	int total = 1;
	for (unsigned int i=0; i<countChildren(); i++)
	{
		DDataItem *item= (DDataItem*)m_manager->Item( childNum(i) );
		if (item) total += item->countFamily();
		else kdWarning()<<"DDataItem::countFamily bug"<<endl;
	}
	return total;
}

void DDataItem::setChanged()
{
	m_manager->notifyItemModified(Id());
}

void DDataItem::incrementFontSize(int val)
{
	int ptsize = m_defaultFont.pointSize();
	ptsize+=val;
	if (ptsize>30) ptsize=30;
	if (ptsize<4) ptsize=4;

	m_defaultFont.setPointSize( ptsize );
}

void DDataItem::addRef(int id)
{
	// check if the reference already exists
	for (unsigned int i=0; i<m_references.count(); i++)
	{
		DRef *ref = m_references[i];
		if (ref->m_ref == id)
		{
			kdWarning()<<"ref already exists DDataItem::addRef "<<id<<endl;
			return;
		}
	}

	// the reference does not exist, add one
	DRef *ref = new DRef;
	ref->m_ref = id;
	m_references.append(ref);
}

bool DDataItem::isRef(int id)
{
	for (unsigned int i=0; i<m_references.count(); i++)
	{
		DRef *ref = m_references[i];
		if (ref->m_ref == id) return true;
	}
	return false;
}

void DDataItem::rmRef(int id)
{
	// remove from the list of references
	for (unsigned int i=0; i<m_references.count(); i++)
	{
		DRef *ref = m_references[i];
		if (ref->m_ref == id)
		{
			m_references.remove(ref);
			delete ref;
		}
	}
}

DRef* DDataItem::ref_idx(unsigned int idx)
{
	if (idx >= m_references.count()) return NULL;
	return m_references[idx];
}

unsigned int DDataItem::ref_count()
{
	return m_references.count();
}

void DDataItem::setFlag(int flag, bool add)
{
	if (add==false)
	{
		int result = m_flags.remove(flag);
		if (result>0) m_manager->registerMapFlagChange(Id(), flag, false);
	}
	else if (!m_flags.contains(flag))
	{
		m_flags.append(flag);
		m_manager->registerMapFlagChange(Id(), flag);
	}
}

bool DDataItem::hasFlag(int flag)
{
	return m_flags.contains(flag);
}
