/****************************************************************************
** Class Utils implementation ...
**
** Created: Wed Sep 09 07:53:05 2004
**
** Here we have some misc functions which are needed by a few classes 
** but can not really be assigned to any of those classes.
**
****************************************************************************/

#include <stdlib.h>
#include <unistd.h>

#include <qfileinfo.h>
#include <qimage.h>
#include <qfile.h>
#include <qdir.h>

#include <qstylefactory.h>
#include <qapplication.h>
#include <qmetaobject.h>
#include <qstyle.h>

#include "global.h"
#include "utils.h"
#include "messagebox.h"


Utils::Utils()
{

}

Utils::~Utils ()
{

}

QString Utils::currentStyleName ( )
{
  QStringList list = QStyleFactory::keys();
  list.sort();

  for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
    QString styleName = *it;
    QStyle *candidate = QStyleFactory::create(styleName);
    Q_ASSERT(candidate);
    if (candidate->metaObject()->className() == QApplication::style().metaObject()->className()) {
      delete candidate;
      return styleName;
    }
    delete candidate;
  }
  return QString("Default");
}

QString Utils::formatChapter (QString qsInput)
{
	char cBuffer[20];
	QString qsReturn ("00:00:00.000");
	if (qsInput == QString ("0"))
		return qsReturn;
	// Here we tidy up the chapter. E.g. 0 gets converted to 00:00:00.000, 0.0.32 to 00:00:32.000 etc
	QStringList listParts = QStringList::split(":", qsInput);
	// SanityCheck ...
	if (listParts.count() < 3)
		return qsReturn;
	int iInput = (int)listParts[2].toFloat();
	sprintf (cBuffer, "%02d:%02d:%02d.%03d", listParts[0].toInt(), listParts[1].toInt(), iInput, (int)((listParts[2].toFloat() - iInput) * 1000));
	qsReturn = QString (cBuffer);
	return qsReturn;
}

QString Utils::getAttribute (QString qsAttrName, QString qsFullString)
{
	QString qsReturn;
	int iStart, iEnd;
	iStart = qsFullString.find (qsAttrName);
	if (iStart < 1)
		return qsReturn;
	// increase iStart to skip e.g.[chapters="] and get to the start of the chapters.
	iStart += qsAttrName.length() + 2;
	// Now we find the end ...
	iEnd = qsFullString.find ("\"", iStart);
	if (iEnd < 1) 
		return qsReturn;
	qsReturn = qsFullString.mid (iStart, iEnd-iStart);
	return qsReturn;
}

QRgb Utils::nearestColor (QRgb *rgbPalette, int iNumColors, QRgb rgbSeeking)
{
	QRgb rgbReturn = rgbPalette[0];
	int t, iRed, iGreen, iBlue, iSquare, iSeeking, iCompare, iClosestColor = 0xff*0xff*3+1;
	
	iRed     = qRed   (rgbSeeking);
	iGreen   = qGreen (rgbSeeking);
	iBlue    = qBlue  (rgbSeeking);
	// iSeeking holds the value for the color to seek.
	iSeeking = iRed*iRed + iGreen*iGreen + iBlue*iBlue;

	for (t=0;t<iNumColors;t++)	{
		iRed   = qRed   (rgbPalette[t]);
		iGreen = qGreen (rgbPalette[t]);
		iBlue  = qBlue  (rgbPalette[t]);
		iSquare = iRed*iRed + iGreen*iGreen + iBlue*iBlue;
		// Okay we are only interested in the total value (not +/-)
		iCompare = iSquare - iSeeking;

		if (iCompare < 0)
			iCompare = -iCompare;
		// Now let us check if we found a color closer then the previous one.
		if (iCompare < iClosestColor)	{
			rgbReturn = rgbPalette[t];
			iClosestColor = iCompare;
		}
	}
	return rgbReturn;
}


/** 
 * After a lot of frustration I decided to write this little (slow) routine, to 
 * - reduce the used colors to 4 AND
 * - merge all used colors to the lower parts of the palette
 * - reduce the number of colors in the palette.
 * This is needed for the button masks in QDVDAuthor. Max = 4 colors shared between
 * selected and highlighted mask.
 */
void Utils::reduceColors ( QImage &theImage, int iNumColors, QRgb *pPalette )
{
  bool bFound;
  uchar *pLine;
  unsigned long iCounter = 0;
  int x, y, t, iIndex, iImageColors;
  QRgb theNearestColor, theColor, *pImageColors;

  // First we have to ensure that we do have a color indexed image.
  theImage = theImage.convertDepthWithPalette (8, (QRgb *)pPalette, MAX_MASK_COLORS, Qt::ThresholdDither);

  // This will create a image which might have more then the requested colors.
  // So we replace all colors with the nearest color of the four.
  pImageColors = theImage.colorTable ();
  iImageColors = theImage.numColors  ();

  for (t=0;(int)t<iImageColors; t++) {
    theNearestColor = nearestColor (pPalette, 4, pImageColors[t]);
    //    theImage.setColor (t, theNearestColor & 0x00ffffff);
    theImage.setColor (t, theNearestColor );
  }
  
  // Here we are going to shift all colors down to the first iNumColors places
  for (y=0;y<theImage.height();y++) {
    pLine = theImage.scanLine ( y );
    for (x=0;x<theImage.width();x++) {
      iIndex = pLine[x];
      theColor = theImage.color ( iIndex );
      //      theColor &= 0x00ffffff; // disregard the alpha channel for now ...
      pLine[x] = 0;
      bFound = false;
      for ( t=0;t<iNumColors;t++ ) {
	// Okay we found the new color (and t = the new index)
	if ( pPalette[t] == theColor ) {
	  pLine[x] = t;
	  bFound = true;
	  t = iNumColors; // exit the inner most loop.
	}
      }
      if ( ! bFound ) // too many output in case something goes wrong 
	printf ( "Could not find color %X count <%ld>\n", (unsigned)theColor, ++iCounter );
    }
  }

  //  printf ( "numColors=%d width=<%d> height=<%d>\n", iImageColors, theImage.width(), theImage.height());
  //  for (t=0;t<iImageColors;t++) {
  //    printf ( "Color[%d] = %X", t, theImage.color (t));
  //    if ( t<iNumColors )
  //      printf ( " - Palette[%d] = %X", t, pPalette[t]);
  //    printf ("\n");
  //  }

  // Then we should cut down the number of colors used ...
  theImage.setNumColors ( iNumColors );

  // and then set the right colors ...
  for (t=0;t<iNumColors;t++)
    theImage.setColor ( t, pPalette [t] );
}

QString Utils::getVideoFormat (int iWidth, int iHeight)
{
	QString qsFormat = ("custom");
	if ( ((iWidth == 720) && (iHeight == 480) ) ||
	     ((iWidth == 704) && (iHeight == 480) ) ||
	     ((iWidth == 352) && (iHeight == 480) ) ||
	     ((iWidth == 352) && (iHeight == 240) ) )
		 qsFormat = QString ("ntsc");

	if ( ((iWidth == 720) && (iHeight == 576) ) ||
	     ((iWidth == 704) && (iHeight == 576) ) ||
	     ((iWidth == 352) && (iHeight == 576) ) ||
	     ((iWidth == 352) && (iHeight == 288) ) )
		 qsFormat = QString ("pal");

	return qsFormat;
}

int Utils::getFormat (int iWidth, int iHeight)
{
	int iFormat = FORMAT_NONE;
	if ((iWidth == 720) && (iHeight == 480) )
		iFormat = FORMAT_NTSC1;
	else if  ((iWidth == 704) && (iHeight == 480) )
		iFormat = FORMAT_NTSC2;
	else if  ((iWidth == 352) && (iHeight == 480) )
		iFormat = FORMAT_NTSC3;
	else if  ((iWidth == 352) && (iHeight == 240) )
		iFormat = FORMAT_NTSC4;
	else if  ((iWidth == 720) && (iHeight == 576) )
		iFormat = FORMAT_PAL1;
	else if ((iWidth == 704) && (iHeight == 576) )
		iFormat = FORMAT_PAL2;
	else if  ((iWidth == 352) && (iHeight == 576) )
		iFormat = FORMAT_PAL3;
	else if  ((iWidth == 352) && (iHeight == 288) )
		iFormat = FORMAT_PAL4;
	return iFormat;
}

QString Utils::iso639 (QString qsCode, bool bReverse)
{
	// This function will return the Country name of the found two-letter - code
	// according to the iso639 standard (ftp://dkuug.dk/i18n/ISO_639)
	char *arrayCountry [] = {"aa", "Afar", "ab", "Abkhazian", "af", "Afrikaans", 
	"am", "Amharic", "ar", "Arabic", "as", "Assamese", "ay", "Aymara", "az", "Azerbaijani", 
	"ba", "Bashkir", "be", "Byelorussian", "bg", "Bulgarian", "bh", "Bihari", "bi", "Bislama", 
	"bn", "Bengali; Bangla", "bo", "Tibetan", "br", "Breton", "ca", "Catalan", "co", "Corsican", 
	"cs", "Czech", "cy", "Welsh", "da", "Danish", "de", "German", "dz", "Bhutani", "el", "Greek", 
	"en", "English", "eo", "Esperanto", "es", "Spanish", "et", "Estonian", "eu", "Basque", 
	"fa", "Persian", "fi", "Finnish", "fj", "Fiji", "fo", "Faroese", "fr", "French", "fy", "Frisian", 
	"ga", "Irish", "gd", "Scots Gaelic", "gl", "Galician", "gn", "Guarani", "gu", "Gujarati", 
	"ha", "Hausa", "he", "Hebrew (formerly iw)", "hi", "Hindi", "hr", "Croatian", "hu", "Hungarian", 
	"hy", "Armenian", "ia", "Interlingua", "id", "Indonesian (formerly in)", "ie", "Interlingue", 
	"ik", "Inupiak", "is", "Icelandic", "it", "Italian", "iu", "Inuktitut", "ja", "Japanese", 
	"jw", "Javanese", "ka", "Georgian", "kk", "Kazakh", "kl", "Greenlandic", "km", "Cambodian", 
	"kn", "Kannada", "ko", "Korean", "ks", "Kashmiri", "ku", "Kurdish", "ky", "Kirghiz", 
	"la", "Latin", "ln", "Lingala", "lo", "Laothian", "lt", "Lithuanian", "lv", "Latvian, Lettish", 
	"mg", "Malagasy", "mi", "Maori", "mk", "Macedonian", "ml", "Malayalam", "mn", "Mongolian", 
	"mo", "Moldavian", "mr", "Marathi", "ms", "Malay", "mt", "Maltese", "my", "Burmese", 
	"na", "Nauru", "ne", "Nepali", "nl", "Dutch", "no", "Norwegian", "oc", "Occitan", 
	"om", "(Afan) Oromo", "or", "Oriya", "pa", "Punjabi", "pl", "Polish", "ps", "Pashto, Pushto", 
	"pt", "Portuguese", "qu", "Quechua", "rm", "Rhaeto-Romance", "rn", "Kirundi", "ro", "Romanian", 
	"ru", "Russian", "rw", "Kinyarwanda", "sa", "Sanskrit", "sd", "Sindhi", "sg", "Sangho", 
	"sh", "Serbo-Croatian", "si", "Sinhalese", "sk", "Slovak", "sl", "Slovenian", "sm", "Samoan", 
	"sn", "Shona", "so", "Somali", "sq", "Albanian", "sr", "Serbian", "ss", "Siswati", "st", "Sesotho", 
	"su", "Sundanese", "sv", "Swedish", "sw", "Swahili", "ta", "Tamil", "te", "Telugu", "tg", "Tajik", 
	"th", "Thai", "ti", "Tigrinya", "tk", "Turkmen", "tl", "Tagalog", "tn", "Setswana", "to", "Tonga", 
	"tr", "Turkish", "ts", "Tsonga", "tt", "Tatar", "tw", "Twi", "ug", "Uighur", "uk", "Ukrainian", 
	"ur", "Urdu", "uz", "Uzbek", "vi", "Vietnamese", "vo", "Volapuk", "wo", "Wolof", "xh", "Xhosa", 
	"yi", "Yiddish (formerly ji)", "yo", "Yoruba", "za", "Zhuang", "zh", "Chinese", "zu", "Zulu"};
	uint t, iCount;
	int iReverse;
	QString qsReturn;
	iCount = (int)(sizeof (arrayCountry) / (sizeof (char *) * 2.0));
	// Determines forward/backward looking up.
	iReverse = 0;
	if (bReverse)
		iReverse = -1;
	
	for (t=0;t<iCount;t++)	{
		if (qsCode == arrayCountry[t*2 - iReverse])	{
			qsReturn = QString ((char *)arrayCountry[t*2+1 + iReverse]);
			return qsReturn;
		}
	}
	return qsCode;
}

QString Utils::checkForExe( QString qsExe )
{
	QString qsExePath;
	QFileInfo fileInfo(QString ("%1/%2_out.txt").arg(Global::qsTempPath).arg(getlogin()));
	// the next check is to see if we can find 
	if ( system ((const char *)QString ("which %1 > %2/%3_out.txt 2>/dev/null").arg(qsExe).arg(Global::qsTempPath).arg(getlogin())) == -1)
		return QString ();
	fileInfo.setFile(QString("%1/%2_out.txt").arg(Global::qsTempPath).arg(getlogin()));
	// Okay we can not find it, so we wont bother asking the user ...
	if (fileInfo.size() > 4)	{
	  QFile theFile   (QString ("%1/%2_out.txt").arg(Global::qsTempPath).arg(getlogin()));
		theFile.open    (IO_ReadOnly);
		theFile.readLine(qsExePath, 4096);
		theFile.close();
		if (!qsExePath.isEmpty())	{
			qsExePath.remove("\n");
			return qsExePath;
		}
	}
	return QString();
}

QString Utils::getToolsDisplayName (QString qsExecutableName)
{
	int t = getIndexFromToolName (qsExecutableName);
	// All external Tools are defined in global.h
	struct structTools { 
		char *pExecutableName;
		char *pDisplayName;
		char *pDescription;
	};
	const structTools toolsArray[] = { EXTERNAL_TOOLS };

	if (t >= 0)
		return QString (toolsArray[t].pDisplayName);
	return qsExecutableName;
}

int Utils::getIndexFromToolName (QString qsExecutableName)
{
	uint t, iNrOfTools;
	// All external Tools are defined in global.h
	struct structTools { 
		char *pExecutableName;
		char *pDisplayName;
		char *pDescription;
	};
	const structTools toolsArray[] = { EXTERNAL_TOOLS };
	iNrOfTools = sizeof (toolsArray) / ((sizeof (char *) * 3));

	for (t=0;t<iNrOfTools;t++)
		if (qsExecutableName == QString (toolsArray[t].pExecutableName))
			return t;
	return -1;
}

QString Utils::getToolByIndex (uint iIndex)
{
	uint iNrOfTools;
	// All external Tools are defined in global.h
	struct structTools { 
		char *pExecutableName;
		char *pDisplayName;
		char *pDescription;
	};
	const structTools toolsArray[] = { EXTERNAL_TOOLS };
	iNrOfTools = sizeof (toolsArray) / ((sizeof (char *) * 3));
	if (iIndex > iNrOfTools-1)
		return QString();

	return QString (toolsArray[iIndex].pExecutableName);
}

QValueList<Utils::toolsPaths *>Utils::scanSystem()
{
	static QValueList<Utils::toolsPaths *>listToolsPaths;
	Utils::toolsPaths *pEntry;

	uint t, iNrOfTools;
	// All external Tools are defined in global.h
	struct structTools { 
		char *pExecutableName;
		char *pDisplayName;
		char *pDescription;
	};
	const structTools toolsArray[] = { EXTERNAL_TOOLS };
	iNrOfTools = sizeof (toolsArray) / ((sizeof (char *) * 3));

	// Note at this point if there were entries in thelist they ought to be deleted already.
	listToolsPaths.clear();
	for (t=0;t<iNrOfTools;t++)	{
		pEntry = new Utils::toolsPaths;
		pEntry->qsExecutableName = QString( toolsArray[t].pExecutableName );
		pEntry->qsFullPath  = checkForExe ( toolsArray[t].pExecutableName );
		if ( pEntry->qsFullPath.isEmpty() )	{
			 pEntry->qsFullPath = pEntry->qsExecutableName;
			 pEntry->bPresent = false;
		}
		else
			pEntry->bPresent = true;
		listToolsPaths.append(pEntry);
	}
	// Please remember unlike the norm to create/destroy objects in the same class I pass the entries in this list on to another class.
	return listToolsPaths;
}

bool Utils::isMandatory(uint iIndex)
{
	// All external Tools are defined in global.h
	struct structTools { 
		char *pExecutableName;
		char *pDisplayName;
		char *pDescription;
	};
	const structTools toolsArray[] = { EXTERNAL_TOOLS };
	if (QString (toolsArray[iIndex].pDescription).find ("Mandatory") > -1)
		return true;
	return false;
}

QString Utils::getTempFile (QString qsOrigFileName)
{
	QString qsTempDir = Global::qsTempPath + QString ("/") +  Global::qsProjectName + QString ("/");
	// we take this to check if the temp drive exists and if not, then we will create one ...
	QDir tempDir (qsTempDir);
	if (!tempDir.exists())
		tempDir.mkdir (qsTempDir);
	if (!tempDir.exists())	{
		MessageBox::warning ( NULL, QObject::tr ("Could not create temp directory"),
		QObject::tr ("Failed to create temp directory\n"
			"%1").arg (qsTempDir),
			QMessageBox::Ok, QMessageBox::NoButton);
			return Global::qsTempPath + QString ("/");
	}
	// Here we append the file name to the temp path ...
	if (!qsOrigFileName.isEmpty())	{
		QFileInfo fileInfo = QFileInfo(qsOrigFileName);
		qsTempDir = QString (qsTempDir + fileInfo.fileName());
	}
	return qsTempDir;
}

// creates a path under the temp dir ...
QString Utils::getTempPath ( QString qsInputPath )
{
  QString qsPath = getTempFile ( qsInputPath );
  QDir tempDir ( qsPath );
  if ( ! tempDir.exists ( ) )
    tempDir.mkdir ( qsPath );
  if ( ! tempDir.exists ( ) )  {
    MessageBox::warning ( NULL, QObject::tr ("Could not create temp directory"),
       QObject::tr ("Failed to create temp directory\n%1").arg ( qsPath ), 
       QMessageBox::Ok, QMessageBox::NoButton);
    return Global::qsTempPath + QString ("/");
  }
  return qsPath;
}

QString Utils::getUniqueTempFile (QString qsOrigFileName)
{
	uint t = 0;
	QString qsUniqueFileName, qsFileName;
	QFileInfo fileInfo (qsOrigFileName);
	QString qsOrigBaseName   = fileInfo.baseName ();
	QString qsOrigExtension  = fileInfo.extension();

	qsFileName.sprintf ("%s%03d.%s", (const char *)qsOrigBaseName, t++, (const char *)qsOrigExtension);
	qsUniqueFileName = getTempFile(qsFileName);
	// The same as above but we ensure the file does not yet exist.
	fileInfo.setFile(qsUniqueFileName);
	while (fileInfo.exists())	{
		qsFileName.sprintf ("%s%03d.%s", (const char *)qsOrigBaseName, t++, (const char *)qsOrigExtension);
		qsUniqueFileName = getTempFile(qsFileName);
		fileInfo.setFile(qsUniqueFileName);
	}
	return qsUniqueFileName;
}

QStringList Utils::getProjectsFromTempPath()
{
	// This function looks for "Main Menu VMGM/background.jpg"
	// under the temp path
	uint t;
	QStringList listOfProjects;
	QDir mainMenuDir, tempDir (Global::qsTempPath);
	QFile backgroundFile, menuBackgroundFile;
	
	// But we aree only interested in sub dirs ...
	tempDir.setFilter (QDir::Dirs);
	for (t=0;t<tempDir.count();t++)	{
		// three criterias 
		backgroundFile.setName(tempDir.absPath() + QString ("/%1/Main Menu VMGM/background.jpg").arg(tempDir[t]));
		menuBackgroundFile.setName(tempDir.absPath() + QString ("/%1/background.jpg").arg(tempDir[t]));
		mainMenuDir.setPath (tempDir.absPath() + QString ("/%1/Main Menu VMGM").arg(tempDir[t]));
		if (backgroundFile.exists ())
			listOfProjects.append (tempDir[t]);
		else if (menuBackgroundFile.exists ())
			listOfProjects.append (tempDir[t]);
		else if (mainMenuDir.exists ())
			listOfProjects.append (tempDir[t]);
	}
	return listOfProjects;
}

QImage  Utils::convertStringToImage (QString &qsImage)
{
	uint t;
	bool bOk;
	QImage theImage;
	QByteArray baData(qsImage.length());

	// Here we convert the hex values to char ...
	for (t=0;t<qsImage.length();t++)
		baData[t] = qsImage.mid (t*2, 2).toUInt(&bOk, 16);
	
	QDataStream stream( baData, IO_ReadOnly );
	stream >> theImage;

	return theImage;
}

QString Utils::convertImageToString (QImage &theImage)
{
	uint t;
	QString qsTemp, qsResult;
	QByteArray baData;
	QDataStream stream( baData, IO_WriteOnly );
	stream << (const QImage &)theImage;
	for (t=0;t<baData.count();t++)	{
		qsTemp.sprintf ("%02X", (uchar)baData[t]);
		qsResult += qsTemp;
	}
//printf ("Utils::convertImageToString QByteArray<\n%s\n>\n", (const char *)qsResult);
	return  qsResult;
}

bool Utils::recRmdir( const QString &dirName, const char *pExcludeFiles ) const
{
	// Recursively removes directroy and all sub-directories.
	// Attention this can be devestating !
	QString dirN = dirName;
	if(QDir::isRelativePath(dirN)) // if not absolute path, make it absolute
		dirN = QDir::current().path() + dirN; // FIXME: I'm not really sure that this works
	QDir dir(dirN);
	QStringList list = dir.entryList(QDir::All); // make list of entries in directory
	unsigned int i, lstCnt = list.count();
	QFileInfo fileInfo;
	QString curItem, lstAt;
	bool bRemove;
	for(i = 0; i < lstCnt; i++){ // loop through all items of list
		lstAt = *list.at(i);
		if(!(lstAt == ".") && !(lstAt == "..")){
			curItem = dirN + "/" + lstAt;
			fileInfo.setFile(curItem);
			if(fileInfo.isDir())       // is directory
				recRmdir(curItem); // call recRmdir() recursively for deleting subdirectory
			else	{                  // is file
				bRemove = true;
				if (pExcludeFiles)	{		// If we want to keep files
					if (lstAt.find (pExcludeFiles) == 0)	// which begin with the string *pExcludeFiles
						bRemove = false;	// Then we flag so ...
				}
				if (bRemove)
					QFile::remove(curItem);		// ok, delete file
			}
		}
	}
	dir.cdUp();
	return dir.rmdir(dirN); // delete empty dir and return if (now empty) dir-removing was successfull
}

long Utils::getMsFromString(QString qsInput)
{
  long iMilliSeconds;
  uint iHours, iMinutes, iSeconds, iStepper;
  float fSeconds=0.0;
  iStepper=0;
  // 00:00:00.000
  iHours = iMinutes = iSeconds = iMilliSeconds = 0;
  QStringList listParts = QStringList::split (":", qsInput);
  if (listParts.count() > 3)
    return 0;
  if (listParts.count() > 2)
    iHours = listParts[iStepper++].toInt();
  if (listParts.count() > 1)
    iMinutes = listParts[iStepper++].toInt();
  if (listParts.count() > 0)
    fSeconds = listParts[iStepper++].toFloat();
  iMilliSeconds = (uint)(1000*fSeconds) + 60*1000*iMinutes + 60*60*1000*iHours;
// 	uint iHours, iMinutes, iSeconds, iMilliSeconds;
// 	QTime length;
// 	
// 	if (qsInput.length() < 8)
// 	{
// 		qsInput.prepend("0");
// 	}
// 	qWarning(qsInput);
// 	
// 	length= QTime::fromString(qsInput);
// 	iHours = 
	
  return iMilliSeconds;
}

QString Utils::getStringFromMs ( long iInput )
{
	uint iHours, iMinutes, iSeconds;
        double fMilliSeconds=0.0;
        iHours = iMinutes = iSeconds = 0;
        QString qsReturn ("00:00:00.000");
        fMilliSeconds = (double)iInput;
        if (fMilliSeconds >= 3600000.0) // 60*60*1000
                iHours = (int)(fMilliSeconds/3600000.0);
        fMilliSeconds -= iHours * 3600000;
        if (fMilliSeconds >= 60000.0)   // 60*1000
                iMinutes = (int)(fMilliSeconds/60000.0);
        if (iMinutes > 60)
                iMinutes = 0;
        fMilliSeconds -= iMinutes * 60000;
        if (fMilliSeconds >= 1000.0)
                iSeconds = (int)(fMilliSeconds/1000.0);
        if (iSeconds > 60)
                iSeconds = 0;
        fMilliSeconds -= iSeconds * 1000;
        if (fMilliSeconds > 1000)
                fMilliSeconds = 0;

        qsReturn.sprintf ("%02d:%02d:%02d.%03d", iHours, iMinutes, iSeconds, (int)fMilliSeconds);
        return qsReturn;

	/**
	 * F.J.Cruz - 17/12/04
	 */
	 
// 	QTime tmpTime;
// 	QTime length;
// 	length = tmpTime.addMSecs(iInput);
// 	if (length.isNull())
// 	{
// 		qWarning("'length' is null ??");
// 	}
// 	qsReturn = length.toString("hh:mm:ss.zzz");
// 	
// 	qWarning(qsReturn);

// 	qsReturn.sprintf ("%02d:%02d:%02d.%03d", iHours, iMinutes, iSeconds, (int)fMilliSeconds);
// 	return qsReturn;
}

int Utils::getWHFromResolution ( QString qsResolution, bool bWidth )
{
  int iXPos;
  QString qsWidth, qsHeight;

  iXPos = qsResolution.find ("x");
  if ( bWidth ) {
    qsWidth  = qsResolution.left (iXPos);
    return qsWidth.toInt ();
  }
  qsHeight = qsResolution.right(iXPos);
  return qsHeight.toInt ();
}

bool Utils::isMpeg2 ( QString qsFileName )
{
  unsigned char Mpeg2Header[4] = {0x00, 0x00, 0x01, 0xba };
  int t, iByte;
  unsigned char c;
  QFile theFile ( qsFileName );

  if ( ! theFile.exists () )
    return false;

  if ( ! theFile.open ( IO_ReadOnly ) )
    return false;

  for (t=0;t<4;t++) {
    iByte = theFile.getch ();
    if ( iByte == -1 ) {
      theFile.close ();
      return false;
    }
    c = (unsigned char) iByte;
    if ( Mpeg2Header[t] != c )
      return false;
  }
  theFile.close ();

  // Okay getting here means the first 4 bytes are mpeg2 header compliant ... Greeeeeat !!!
  return true;
}

bool Utils::hasVobus ( QString qsFileName )
{
  // VOB is a special subset of the Mpeg2 standard. I stole this test version from DVDStyler's MPEG.cpp - file.
  // A great thank you goes to Alex Thuering for his great effort to advance DVDStyler. 
  // But Ya'll know 'Q'DVD-Author is much better ;o) 

  /*
    Navigation packets are PES packets with a stream id 0xbf, i.e.
    private stream 2.  It's made up of PCI, Presentation Control Information
    and DSI, Data Search Information.
    
    details: www.mpucoder.com
   */
  
  unsigned char PCI_PACK[] = { 0x00, 0x00, 0x01, 0xBF, 0x03, 0xD4 };
  unsigned char DSI_PACK[] = { 0x00, 0x00, 0x01, 0xBF, 0x03, 0xFA };
  
  QFile theFile ( qsFileName );
  if ( ! theFile.open ( IO_ReadOnly ) )
    return false;
  
  const long MAX_LENGTH=16384; // only looking at 1st 16KB - enough to contain nav packs
  unsigned char buffer[MAX_LENGTH];
  
  // skip zeroes at start of file
  int zeroCnt = 0;
  char c;
  while ( (c = (char)theFile.getch ( ) ) == 0)
    zeroCnt++;
  
  if (zeroCnt < 2 || c != 1)
    return false;
  
  buffer[0] = buffer[1] = 0; buffer[2] = 1;
  int iRead = theFile.readBlock ( (char *)(buffer+3), MAX_LENGTH-3);
  
  bool valid_pci = false;
  bool valid_dsi = false;
  for (int i=0; i<iRead-(int)sizeof(PCI_PACK); i++)
    {
      int j;
      for (j=0; j<(int)sizeof(PCI_PACK); j++)
	{
	  if(buffer[i+j] != PCI_PACK[j])
	    break;
	  else if (j == sizeof(PCI_PACK)-1)
	    valid_pci=true;
	}
      for (j=0; j<(int)sizeof(DSI_PACK); j++)
      {
	if(buffer[i+j] != DSI_PACK[j])
	  break;
	else if (j == sizeof(DSI_PACK)-1)
	  valid_dsi=true;
      }
      
      if (valid_pci && valid_dsi)
	return true;
    }
  
  return false;
}
