/////////////////
// Hey
////////////////
#include <qdom.h>
#include <qfile.h>
#include <qcolor.h>
#include <qlistview.h>
#include <qfiledialog.h>
#include <qmessagebox.h>

#include "xml_dvd.h"
#include "xml_slideshow.h"

CXmlSlideshow::CXmlSlideshow ()
{
	node_name = QString ("slideshow");
	delay = -1;
	filter_delay = -1;
	xres = 720;
	yres = 480;	// NTSC for now
	ppArrayImg = NULL;
	ppArrayFilter = NULL;
}
CXmlSlideshow::~CXmlSlideshow ()
{
	clear ();
}

void CXmlSlideshow::clear ()
{
	// Lets clean up a little bit ...
	uint t=0;
	// The timObjectArray holds Img, and Filter objects, thus no need to delete these again.
	time_object *pTimeObject;
	for (t=0;t<m_listTimeObjects.count();t++)	{
		 pTimeObject = m_listTimeObjects[t];
			delete pTimeObject;
	}
	if (ppArrayImg)
		delete []ppArrayImg;
	if (ppArrayFilter)
		delete []ppArrayFilter;
	ppArrayImg = NULL;
	ppArrayFilter = NULL;
	m_listTimeObjects.clear();
	delay = -1;
	filter_delay = -1;
	xres = 720;
	yres = 480;	// NTSC for now
	background = QString ("");
	audio_list.clear();
}

CXmlSlideshow &CXmlSlideshow::operator = (CXmlSlideshow &theOther)
{
	uint t;
	time_object *pTimeObject, *pNewObject;

	clear ();
	delay = theOther.delay;
	filter_delay = theOther.filter_delay;
	xres = theOther.xres;
	yres = theOther.yres;
	background = theOther.background;
	slideshow_name = theOther.slideshow_name;
	audio_list = theOther.audio_list;
	// Here we copy the objects over ..
	for (t=0;t<theOther.count();t++)	{
		pTimeObject = theOther.getTimeObject(t);
		if (pTimeObject->node_name == "img")
			pNewObject = addImg(); //new img_struct;
		else
			pNewObject = addFilter();	//new filter_struct;
		*pNewObject = *pTimeObject;
	}
	return *this;
}

bool CXmlSlideshow::readXml ()
{
	// Here we read in a xml - file and create the neccesary underlying structure.
	//
	// For now we are going to ask for the file name here and handle the QDom...
	// Later on this is done a level further up and only QDomNode * is sent.
	//
	//////////////////////////////////////////////////////////////////////////////////
	QString fileName = QFileDialog::getOpenFileName ( QString("./"), QObject::tr ("Slideshow files ( *.slide)"));
	return readXml (fileName);
}

bool CXmlSlideshow::readXml (QString &fileName)
{
	// Here we read in a xml - file and create the neccesary underlying structure.
	//
	//////////////////////////////////////////////////////////////////////////////////
	// Assign the file
	QFile projectFile(fileName);
	if (!projectFile.open(IO_ReadWrite))
		return false;

	QDomDocument xmlDoc( SLIDESHOW_DOCTYPE );
	if (!xmlDoc.setContent (&projectFile))	{
		// Error handling ...
		projectFile.close();
		int iReturn = QMessageBox::warning ( NULL, QObject::tr ("Slideshow file seems to be defective."),
			QObject::tr ("Do you want to try to load another slideshow file ?"),
			QMessageBox::Yes, QMessageBox::No);
		if (iReturn == QMessageBox::Yes)
			return readXml (); // Okay, user wants to specify another project file.
	}
	// And now read in all remaining nodes and handle them accordingly.
	time_object *pTimeObject = NULL;
	img_struct *pTempImg = new img_struct;	// temp to get the node_name.
	filter_struct *pTempFilter = new filter_struct;	// temp to get the node_name.
	bool bReturn = true;
	QDomElement docElem = xmlDoc.documentElement();
	QDomNode xmlNode = docElem.firstChild();
	QDomAttr a = docElem.attributeNode ( SLIDESHOW_DELAY );
	delay = a.value().toInt();
	a = docElem.attributeNode ( SLIDESHOW_FILTER_DELAY );
	filter_delay = a.value().toInt();
	a = docElem.attributeNode ( SLIDESHOW_BACKGROUND );
	background = a.value();
	a = docElem.attributeNode ( SLIDESHOW_NAME );
	slideshow_name = a.value();
	a = docElem.attributeNode ( SLIDESHOW_XRES );
	xres = a.value().toInt();
	a = docElem.attributeNode ( SLIDESHOW_YRES );
	yres = a.value().toInt();
	a = docElem.attributeNode ( SLIDESHOW_AUDIO_LIST );
	audio_list = QStringList::split("+-+", a.value());

	if (xres < 1)
		xres = 720;
	if (yres < 1)
		yres = 480;

	if (delay <= 0)
		delay = 5;
	if (filter_delay <= 0)
		filter_delay = 3;
	float fCurrentTime = 0.0f;
	while( !xmlNode.isNull() ) {
		QDomElement searchTree = xmlNode.toElement();
		pTimeObject = NULL;
		if (searchTree.tagName() == pTempImg->node_name)
			pTimeObject = (time_object *)addImg();
		else if (searchTree.tagName() == pTempFilter->node_name)
			pTimeObject = (time_object *)addFilter();
		if (pTimeObject)
			bReturn = pTimeObject->readXml(&searchTree);
		else
			return false;
		// If there has been a problem then return false.
		if (!bReturn)
			return false;

		// And here we calculate some extra values ...
		if (pTimeObject->fDuration <= 0.0f) {
			if (searchTree.tagName() == pTempImg->node_name)
				pTimeObject->fDuration = delay;
			else
				pTimeObject->fDuration = filter_delay;
		}
		pTimeObject->fStartTime = fCurrentTime;
		fCurrentTime += pTimeObject->fDuration;
		pTimeObject->fEndTime = fCurrentTime;
		// Otherwise go to the next node ...
		xmlNode = xmlNode.nextSibling();
	}
	return true;
}

bool CXmlSlideshow::writeXml ()
{
	// Here we write to a xml - file and create the neccesary underlying structure.
	//
	// For now we are going to ask for the file name here and handle the QDom...
	// Later on this is done a level further up and only QDomNode * is sent.
	//
	//////////////////////////////////////////////////////////////////////////////////
	QString fileName = QFileDialog::getSaveFileName ( QString("./"), QObject::tr ("Slideshow files ( *.slide)"));
	if (fileName.isNull())
		return false;
	return writeXml(fileName);
}

bool CXmlSlideshow::writeXml (QString &fileName)
{
//	debug_out ("CXmlSlideshow::writeXml <%s><%s>\n",(const char *)pNodeElement->tagName(), (const char *)node_name);
	uint t;
	// Assign the file
	QFile projectFile(fileName);
	if (!projectFile.open(IO_WriteOnly))
		return false;

	QDomDocument xmlDoc( SLIDESHOW_DOCTYPE );	// <""> for now.
	QDomElement slideshowNode = xmlDoc.createElement( node_name );	// <slideshow>
	if (delay > 0)
		slideshowNode.setAttribute( SLIDESHOW_DELAY,  QString ("%1").arg(delay));
	if (filter_delay > 0)
		slideshowNode.setAttribute( SLIDESHOW_FILTER_DELAY,  QString ("%1").arg(filter_delay));
	if (!background.isNull())
		slideshowNode.setAttribute( SLIDESHOW_BACKGROUND, background );
	if (!slideshow_name.isNull())
		slideshowNode.setAttribute( SLIDESHOW_NAME, SLIDESHOW_NAME );
	if (xres > 0)
		slideshowNode.setAttribute( SLIDESHOW_XRES,  QString ("%1").arg(xres));
	if (yres > 0)
		slideshowNode.setAttribute( SLIDESHOW_YRES,  QString ("%1").arg(yres));
	if (audio_list.count() > 0)
		slideshowNode.setAttribute( SLIDESHOW_AUDIO_LIST, audio_list.join (QString ("+-+")) );
	// And now proceed to writing the rest of the file.
	xmlDoc.appendChild( slideshowNode );

	for (t=0;t<m_listTimeObjects.count();t++)
		m_listTimeObjects[t]->writeXml(&xmlDoc, &slideshowNode);
	QString xml = xmlDoc.toString();
//	printf ("%s\n", (const char *)xml);
	projectFile.writeBlock(xml, qstrlen (xml));

	projectFile.close();
	return true;
}

uint CXmlSlideshow::count ()
{
	return m_listTimeObjects.count();
}

int  CXmlSlideshow::findImg (CXmlSlideshow::img_struct *pImg)
{
	uint t;
	for (t=0;t<count();t++)	{
		if (m_listTimeObjects[t] == (time_object *)pImg)
			return t;
	}
	return -1;
}

uint CXmlSlideshow::countImg ()
{
	if (!ppArrayImg)
		return 0;
	int iCount = 0;
	img_struct *pImg = ppArrayImg[iCount];
	while (pImg)	{
		iCount ++;
		pImg = ppArrayImg[iCount];
	}
	return iCount;
}

uint CXmlSlideshow::countFilter ()
{
	if (!ppArrayFilter)
		return 0;
	int iCount = 0;
	filter_struct *pFilter = ppArrayFilter[iCount];
	while (pFilter)	{
		iCount ++;
		pFilter = ppArrayFilter[iCount];
	}
	return iCount;
}

CXmlSlideshow::time_object *CXmlSlideshow::getTimeObject(uint iObjectNo)
{
	if (iObjectNo < m_listTimeObjects.count())
		return m_listTimeObjects[iObjectNo];
	else
		return NULL;
}

CXmlSlideshow::img_struct *CXmlSlideshow::getImg(uint iImgNo)
{
	if (iImgNo < countImg())
		return ppArrayImg[iImgNo];
	else
		return NULL;
}

CXmlSlideshow::filter_struct *CXmlSlideshow::getFilter(uint iFilterNo)
{
	if (iFilterNo < countFilter())
		return ppArrayFilter[iFilterNo];
	else
		return NULL;
}

CXmlSlideshow::img_struct *CXmlSlideshow::addImg()
{
	// This function simply enlarges the array of available titlesets.
	// That'll keep the structure dynamic.
	if (!ppArrayImg)	{
		// This is the first ...
		ppArrayImg=new img_struct *[2];
		ppArrayImg[0]=new img_struct;
		ppArrayImg[1]=NULL;
		m_listTimeObjects.append (ppArrayImg[0]);
		return ppArrayImg[0];
	}

	int i,t=0;
	img_struct *pImg = ppArrayImg[t];
	img_struct **ppNewArray=NULL, **ppOldArray=NULL;
	while (pImg)	{
		t ++;
		pImg = ppArrayImg[t];
	}
	// Now we have the count of actual titlesets.
	ppNewArray=new img_struct *[t+2];
	for (i=0;i<t;i++)	{
		ppNewArray[i]=ppArrayImg[i];
	}
	ppNewArray[i] = new img_struct();
	ppNewArray[i+1] = NULL;
	// Now we can delete th old array (but not the contents.
	ppOldArray = ppArrayImg;
	ppArrayImg = ppNewArray;
	delete []ppOldArray;
	// And finally return the latest addition ...
	m_listTimeObjects.append (ppArrayImg[i]);
	return ppArrayImg[i];
}

CXmlSlideshow::filter_struct *CXmlSlideshow::addFilter()
{
	// This function simply enlarges the array of available titlesets.
	// That'll keep the structure dynamic.
	if (!ppArrayFilter)	{
		// This is the first ...
		ppArrayFilter = new filter_struct *[2];
		ppArrayFilter[0]=new filter_struct;
		ppArrayFilter[1]=NULL;
		m_listTimeObjects.append (ppArrayFilter[0]);
		return ppArrayFilter[0];
	}

	int i,t=0;
	filter_struct *pFilter = ppArrayFilter[t];
	filter_struct **ppNewArray=NULL, **ppOldArray=NULL;
	while (pFilter)	{
		t ++;
		pFilter = ppArrayFilter[t];
	}
	// Now we have the count of actual titlesets.
	ppNewArray=new filter_struct *[t+2];
	for (i=0;i<t;i++)	{
		ppNewArray[i]=ppArrayFilter[i];
	}
	ppNewArray[i] = new filter_struct();
	ppNewArray[i+1] = NULL;
	// Now we can delete th old array (but not the contents.
	ppOldArray = ppArrayFilter;
	ppArrayFilter = ppNewArray;
	delete []ppOldArray;
	// And finally return the latest addition ...
	m_listTimeObjects.append (ppArrayFilter[i]);
	return ppArrayFilter[i];
}

CXmlSlideshow::time_object::time_object ()
{
	node_name	= QString ("");
	fDuration	= -1.0f;	// Define the duration in seconds
	fStartTime	= -1.0f;
	fEndTime	= -1.0f;
	pModifier 	= NULL;
}

CXmlSlideshow::img_struct::img_struct ()
	: time_object ()
{
	node_name = QString ("img");
	src = QString ("");
	text = QString ("");
	width = 0;
	height = 0;
	rotate = 0.0f;
}

CXmlSlideshow::img_struct::~img_struct ()
{
	if (pModifier)	{
		ImageManipulator *pManipulator = (ImageManipulator *)pModifier;
		delete pManipulator;
	}
}

CXmlSlideshow::filter_struct::filter_struct ()
	: time_object ()
{
	node_name = QString ("filter");
	name = QString ("");	// the type of filter, e.g. crossfade/fadein/fadeout
	subtitle = QString ("");
}

CXmlSlideshow::effect_struct::effect_struct ()
{
	node_name = QString ("effect");
	QString name;		// either crop/kenburns/scroll
	x0 = 0;
	y0 = 0;
	x1 = 0;
	y1 = 0;
	xe0 = 0;
	ye0 = 0;
	xe1 = 0;
	ye1 = 0;
	scroll = QString ("");	// either left or right
}

bool CXmlSlideshow::img_struct::readXml (QDomElement *pDocElem)
{
	bool bReturn = true;
	if (pDocElem->tagName() != node_name)
		return false;
	// So lets get first the attributes for this node.
	QDomAttr a = pDocElem->attributeNode ( IMG_SRC );
	src = a.value();
	a = pDocElem->attributeNode ( IMG_TEXT );
	text = a.value();
	a = pDocElem->attributeNode ( IMG_WIDTH );
	width = a.value().toInt();
	a = pDocElem->attributeNode ( IMG_HEIGHT );
	height = a.value().toInt();
	a = pDocElem->attributeNode ( IMG_ROTATE );
	rotate = a.value().toFloat();
	a = pDocElem->attributeNode ( IMG_DURATION );
	fDuration = a.value().toFloat();

	QDomNode xmlNode = pDocElem->firstChild();
	ImageManipulator tmpImageManipulator;
	while( !xmlNode.isNull() ) {
		QDomElement searchTree = xmlNode.toElement();
		ImageManipulator *pImageManipulator = new ImageManipulator;
		if (searchTree.tagName() == effect.node_name)
			bReturn = effect.readXml(&searchTree);
		if (searchTree.tagName() == tmpImageManipulator.node_name)	{
			pModifier = (void *)pImageManipulator;
			bReturn = pImageManipulator->readProjectFile( searchTree );
		}
		// If there has been a problem then return false.
		if (!bReturn)
			return false;
		// Otherwise go to the next node ...
		xmlNode = xmlNode.nextSibling();
	}
	return true;
}

bool CXmlSlideshow::effect_struct::readXml (QDomElement *pDocElem)
{
	if (pDocElem->tagName() != node_name)
		return false;
	// So lets get first the attributes for this node.
	QDomAttr a = pDocElem->attributeNode ( EFFECT_NAME );
	name = a.value();
	a = pDocElem->attributeNode ( EFFECT_X0 );
	x0 = a.value().toInt();
	a = pDocElem->attributeNode ( EFFECT_Y0 );
	y0 = a.value().toInt();
	a = pDocElem->attributeNode ( EFFECT_X1 );
	x1 = a.value().toInt();
	a = pDocElem->attributeNode ( EFFECT_Y1 );
	y1 = a.value().toInt();

	a = pDocElem->attributeNode ( EFFECT_XE0 );
	xe0 = a.value().toInt();
	a = pDocElem->attributeNode ( EFFECT_YE0 );
	ye0 = a.value().toInt();
	a = pDocElem->attributeNode ( EFFECT_XE1 );
	xe1 = a.value().toInt();
	a = pDocElem->attributeNode ( EFFECT_YE1 );
	ye1 = a.value().toInt();
	a = pDocElem->attributeNode ( EFFECT_SCROLL );
	scroll = a.value();
	return true;
}

bool CXmlSlideshow::filter_struct::readXml (QDomElement *pDocElem)
{
	if (pDocElem->tagName() != node_name)
		return false;
	// So lets get first the attributes for this node.
	QDomAttr a = pDocElem->attributeNode ( FILTER_NAME );
	name = a.value();
	a = pDocElem->attributeNode ( FILTER_DURATION );
	fDuration = a.value().toFloat();
	a = pDocElem->attributeNode ( FILTER_SUBTITLE );
	subtitle = a.value();
	return true;
}

bool CXmlSlideshow::img_struct::writeXml(QDomDocument *pDocument, QDomElement *pNodeElement)
{
//	debug_out ("CXmlSlideshow::img_struct::writeXml <%s><%s>\n",(const char *)pNodeElement->tagName(), (const char *)node_name);
	QDomElement imgNode = pDocument->createElement( node_name );	// <img>

	if (!src.isEmpty())
		imgNode.setAttribute( IMG_SRC, src );
	if (!text.isEmpty())
		imgNode.setAttribute( IMG_TEXT, text );
	if (width > 1)
		imgNode.setAttribute( IMG_WIDTH, QString ("%1").arg(width) );
	if (height > 1)
		imgNode.setAttribute( IMG_HEIGHT, QString ("%1").arg(height) );
	if (rotate != 0.0)
		imgNode.setAttribute( IMG_ROTATE, QString ("%1").arg(rotate) );
	if (fDuration > -1.0f)
		imgNode.setAttribute( IMG_DURATION, QString ("%1").arg(fDuration) );
	if (pModifier)
		((ImageManipulator *)pModifier)->writeProjectFile( imgNode );

	pNodeElement->appendChild( imgNode );
	return effect.writeXml(pDocument, pNodeElement);
}

bool CXmlSlideshow::effect_struct::writeXml(QDomDocument *pDocument, QDomElement *pNodeElement)
{
//	debug_out ("CXmlSlideshow::effect_struct::writeXml <%s><%s>\n",(const char *)pNodeElement->tagName(), (const char *)node_name);
	QDomElement effectNode = pDocument->createElement( node_name );	// <img>

	if (!name.isNull())
		effectNode.setAttribute( EFFECT_NAME, name );
	else
		return true;
	if (x0 > 1)
		effectNode.setAttribute( EFFECT_X0, QString ("%1").arg(x0) );
	if (y0 > 1)
		effectNode.setAttribute( EFFECT_Y0, QString ("%1").arg(y0) );
	if (x1 > 1)
		effectNode.setAttribute( EFFECT_X1, QString ("%1").arg(x1) );
	if (y1 > 1)
		effectNode.setAttribute( EFFECT_Y1, QString ("%1").arg(y1) );
	if (xe0 > 1)
		effectNode.setAttribute( EFFECT_XE0, QString ("%1").arg(xe0) );
	if (ye0 > 1)
		effectNode.setAttribute( EFFECT_YE0, QString ("%1").arg(ye0) );
	if (xe1 > 1)
		effectNode.setAttribute( EFFECT_XE1, QString ("%1").arg(xe1) );
	if (ye1 > 1)
		effectNode.setAttribute( EFFECT_YE1, QString ("%1").arg(ye1) );
	if (!scroll.isEmpty())
		effectNode.setAttribute( EFFECT_SCROLL, scroll );
	pNodeElement->appendChild( effectNode );
	return true;
}

bool CXmlSlideshow::filter_struct::writeXml(QDomDocument *pDocument, QDomElement *pNodeElement)
{
//	debug_out ("CXmlSlideshow::effect_struct::writeXml <%s><%s>\n",(const char *)pNodeElement->tagName(), (const char *)node_name);
	QDomElement filterNode = pDocument->createElement( node_name );	// <img>

	if (!name.isNull())
		filterNode.setAttribute( FILTER_NAME, name );
	if (fDuration > 0.0f)
		filterNode.setAttribute( FILTER_DURATION, QString ("%1").arg(fDuration) );
	if (!subtitle.isEmpty())
		filterNode.setAttribute( FILTER_SUBTITLE, subtitle );
	pNodeElement->appendChild( filterNode );
	return true;
}

CXmlSlideshow::effect_struct& CXmlSlideshow::effect_struct::operator =(CXmlSlideshow::effect_struct &theOther)
{
	name = theOther.name;
	x0 = theOther.x0;
	y0 = theOther.y0;
	x1 = theOther.x1;
	y1 = theOther.y1;
	xe0 = theOther.xe0;
	ye0 = theOther.ye0;
	xe1 = theOther.xe1;
	ye1 = theOther.ye1;
	scroll = theOther.scroll;
	return *this;
}
CXmlSlideshow::time_object& CXmlSlideshow::img_struct::operator =(CXmlSlideshow::time_object &theOther)
{
	CXmlSlideshow::img_struct *pTheOther = (CXmlSlideshow::img_struct *)&theOther;
	ImageManipulator *pMan, *pOtherMan;
	pMan = (ImageManipulator *)pModifier;
	// check if we already have a modifier ...
	if ( pMan )
		delete pMan;
	pMan = NULL;
	if (pTheOther->pModifier)	{
		pOtherMan = (ImageManipulator *)pTheOther->pModifier;
		//create a modifier, and copy over ...
		pMan = new ImageManipulator;
		*pMan = *pOtherMan;
		pModifier = pMan;
	}
	src = pTheOther->src;
	text = pTheOther->text;
	width = pTheOther->width;
	height = pTheOther->height;
	rotate  = pTheOther->rotate;
	effect   = pTheOther->effect;
	fEndTime  = pTheOther->fEndTime;
	fDuration  = pTheOther->fDuration;
	fStartTime  = pTheOther->fStartTime;
	return *this;
}
CXmlSlideshow::time_object& CXmlSlideshow::filter_struct::operator =(CXmlSlideshow::time_object &theOther)
{
	CXmlSlideshow::filter_struct *pTheOther = (CXmlSlideshow::filter_struct *)&theOther;
	name   = pTheOther->name;
	subtitle= pTheOther->subtitle;
	fEndTime = pTheOther->fEndTime;
	fDuration = pTheOther->fDuration;
	fStartTime = pTheOther->fStartTime;
	return *this;
}

