
#include <qmessagebox.h>
#include <qstringlist.h>
#include <qfiledialog.h>
#include <qfileinfo.h>
//#include <qobject.h>


#include "global.h"
#include "dvdmenu.h"
#include "qdvdauthor.h"
#include "menuobject.h"
#include "textobject.h"
#include "menupreview.h"
#include "imageobject.h"
#include "buttonobject.h"
#include "xml_dvdauthor.h"
#include "importdvdauthor.h"
#include "sourcefileentry.h"


namespace Import
{

DVDAuthor::DVDAuthor ( QDVDAuthor *pDVDAuthor )
{
  m_pDVDAuthor       = pDVDAuthor;
  m_pVMGMenu         = NULL;
  m_iCurrentTitleset = -1;
}

DVDAuthor::~DVDAuthor ()
{
  clear ( true );
}

/**
 * import will open a QFileDialog for selecting the dvdauthor xml file and will then 
 * prompt the user to select all associated spumux xml files.
 *
 * Once those files are specified, they are read in and it is attempted to automatically 
 * find the right spumux xml for the corresponding dvdauthor - menu.
 *
 * \TDOD: 
 * Define problems and allow the user to resolve some of them.
 */
bool DVDAuthor::import ( )
{
  CXmlDVDAuthor theXmlStructure;
  CXmlSpumux   *pSpumux;
  QStringList   listFileNames, listOfFailures;
  QValueList<CXmlSpumux *>spumuxList;
  int           t, iResponse;
  bool          bSuccess, bReturn;
  QFileInfo     theFile;

  // Read in the dvdauthor file.
  QString fileName = QFileDialog::getOpenFileName ( Global::qsCurrentPath, QObject::tr ("dvdauthor XML file ( *.xml *.XML)"));

  theFile.setFile ( fileName );
  Global::qsCurrentPath = theFile.dirPath( true );

  bSuccess = theXmlStructure.readXml ( fileName );

  iResponse = QMessageBox::information ( NULL, "Information", QObject::tr("Reading dvdauthor.xml Was %1 succesfull.\nDo you want to load spumux files ?").arg( bSuccess ? "" : "NOT"), QMessageBox::Yes, QMessageBox::No,QMessageBox::Cancel);
  if ( iResponse == QMessageBox::Cancel) 
    return true;
  
  if ( iResponse == QMessageBox::No )
    return buildFromDVDAuthorXML ( &theXmlStructure, spumuxList );

  while ( iResponse == QMessageBox::Yes ) {
    listOfFailures.clear ();
    listFileNames = QFileDialog::getOpenFileNames ( "*.xml", Global::qsCurrentPath, NULL, "OpenDvdauthorXml", "dvdauthor and spumux xml file selection");
    if ( listFileNames.count() > 0 ) {
      theFile.setFile ( listFileNames[0] );
      Global::qsCurrentPath = theFile.dirPath();

      for (t=0;t<(int)listFileNames.count();t++) {
	pSpumux = new CXmlSpumux;
	bSuccess = pSpumux->readXml ( listFileNames[t] );
	if ( bSuccess ) 
	  spumuxList.append ( pSpumux );
	else {
	  listOfFailures.append ( listFileNames[t] );
	  delete pSpumux;
	}
      }
    }
    if ( listOfFailures.count() > 0 )
      QMessageBox::information ( NULL, "Warning", QObject::tr("Failed to read %1 spumux-xml file%2 : \n%2").
				 arg(listOfFailures.count()).arg( listOfFailures.join ("\n").arg ((listOfFailures.count() > 1) ? "s" : "") ), QMessageBox::Ok, QMessageBox::NoButton);

    iResponse = QMessageBox::information ( NULL, "Information", QObject::tr("Reading %1 spumux-xml files Was %2 succesfull.\nDo you want to load more spumux files ?").
					   arg(listFileNames.count()).arg( bSuccess ? "" : "NOT"), QMessageBox::Yes, QMessageBox::No);
  }

  bReturn = buildFromDVDAuthorXML ( &theXmlStructure, spumuxList );
  // And finally clean up the allocated memory.
  for ( t=0;t<(int)spumuxList.count();t++)
    delete spumuxList[t];
  spumuxList.clear ();

  return bReturn;
}

void DVDAuthor::clear ( bool bDeleteAll )
{
  uint t;
  if ( ! bDeleteAll ) 
    return;
  
  if ( m_pVMGMenu )
    delete m_pVMGMenu;

  for ( t=0;t< m_listDVDMenus.count (); t++)
    delete m_listDVDMenus[t];
  m_listDVDMenus.clear ();

  for ( t=0;t<m_listSourceFileEntries.count();t++)
    delete m_listSourceFileEntries[t];
  m_listSourceFileEntries.clear ();
}

VMGMenu *DVDAuthor::takeVMGMenu () 
{
  VMGMenu *pVMGMenu = m_pVMGMenu;
  m_pVMGMenu = NULL;
  return pVMGMenu;
}

QValueList<DVDMenu *> DVDAuthor::takeDVDMenus ()
{
  QValueList<DVDMenu *>list = m_listDVDMenus;
  m_listDVDMenus.clear ();
  return list;
}

QValueList <SourceFileEntry *> DVDAuthor::takeSourceFileEntries ()
{
  QValueList<SourceFileEntry *>list = m_listSourceFileEntries;
  m_listSourceFileEntries.clear ();
  return list;
}

/////////////////////////////////////////////////////////////
//
// Private member functions.
//
////////////////////////////////////////////////////////////
bool DVDAuthor::buildFromDVDAuthorXML ( CXmlDVDAuthor *pDVDAuthor, QValueList<CXmlSpumux *> &spumuxList )
{
  uint t;
  int  iTitleset = 0;
  // First things first ...
  clear ( true );
  
  if ( ! buildFromVMGM ( &pDVDAuthor->getDVDAuthor()->vmgm ) )
    return false;

  // Sanity check. If there are no titlesets in the XML, don't bother.
  if ( pDVDAuthor->getDVDAuthor ()->ppArrayTitleset ) {
    // First we parse the DVDAuthor - xml structure to create all menus.
    while ( pDVDAuthor->getDVDAuthor ()->ppArrayTitleset[iTitleset] ) {
      m_iCurrentTitleset ++;
      if ( ! buildFromTitleset ( pDVDAuthor->getDVDAuthor ()->ppArrayTitleset[iTitleset++] ) )
	return false;
    }
  }

  // At this point we should have build a list of DVDMenu - objects 
  // and should be ready to parse the spumux files to assign some missing attributes
  for ( t=0;t<spumuxList.count();t++) {
    if ( ! buildFromSpumuxXML ( spumuxList[t] ) )
      return false;
  }

  // Stick a fork in me, I am done !
  return true;
}

bool DVDAuthor::buildFromVMGM ( CXmlDVDAuthor::vmgm_struct *pXMLVMGM )
{
  int iMenu  = 0;
  m_pVMGMenu = new VMGMenu ( m_pDVDAuthor );
  // NOTE: Need to check if there are really only Menus and no Titles in VMGM.
  if ( ! pXMLVMGM->ppArrayMenus )
    return true;
  
  m_iCurrentTitleset = 0;
  // We take the very first Menu to be the VMGM for QDVDAuthor's purposes. 
  // All the others are treated as standard DVDMenu's
  if ( ! buildFromMenus ( pXMLVMGM->ppArrayMenus[ iMenu++ ], m_pVMGMenu ) )
    return false;
  
  while ( pXMLVMGM->ppArrayMenus[ iMenu ] ) {
    if ( ! buildFromMenus ( pXMLVMGM->ppArrayMenus[ iMenu++ ] ) )
      return false;
  }

  // we got till here, let's celebrate by returning the true(th)
  return true;
}

bool DVDAuthor::buildFromTitleset ( CXmlDVDAuthor::titleset_struct *pXMLTitleset )
{
  int iMenu  = 0;
  int iTitle = 0;
  // Note, I believe the DVDSpec allows only for one Menus and one Titles - tag

  // Read in the Menus ...
  if ( pXMLTitleset->ppArrayMenus ) {
    while ( pXMLTitleset->ppArrayMenus[ iMenu ] ) {
      if ( ! buildFromMenus ( pXMLTitleset->ppArrayMenus [ iMenu++ ] ) )
	return false;
    }
  }

  // Read in the titles
  if ( pXMLTitleset->ppArrayTitles ) {
    while ( pXMLTitleset->ppArrayTitles[ iTitle ] ) {
      if ( ! buildFromTitles ( pXMLTitleset->ppArrayTitles [ iTitle++ ] ) )
	return false;
    }
  }

  return true;
}

bool DVDAuthor::buildFromMenus ( CXmlDVDAuthor::menus_struct *pXMLMenu, VMGMenu *pVMGMenu )
{
  int iPgc = 0;

  if ( ! pXMLMenu->ppArrayPgc )
    return true;
  
  while ( pXMLMenu->ppArrayPgc [ iPgc ] ) {
    if ( ! buildMenuFromPgc ( pXMLMenu->ppArrayPgc [ iPgc++ ], pVMGMenu ) )
      return false;
  }
  return true;
}

bool DVDAuthor::buildMenuFromPgc ( CXmlDVDAuthor::pgc_struct *pPgc, VMGMenu *pVMGMenu )
{
  int iVob    = 0;
  int iButton = 0;
  DVDMenu *pDVDMenu = pVMGMenu;
  CDVDMenuInterface *pInterface;

  // For now we simply disregard menus with no videos. This way we break the reconstruction
  // But we can visit this area at a later point again. 2005/10/23
  if ( ( ! pPgc->ppArrayVob ) && ( ! pPgc->ppArrayButton ) )
    return true;

  if ( ! pDVDMenu ) {
    pDVDMenu = new DVDMenu ( m_pDVDAuthor );
    m_listDVDMenus.append ( pDVDMenu );
  }
  pInterface = pDVDMenu->getInterface ();

  pInterface->iTitleset = m_iCurrentTitleset;
  pInterface->qsPre     = pPgc->pre.value;
  pInterface->qsPost    = pPgc->post.value;
  // Each Pgc can have multiple VOB's
  if ( pPgc->ppArrayVob ) {
    while ( pPgc->ppArrayVob[ iVob ] )  // simply add them to the DVDMenu - object.
      pInterface->listIntroVobs.append ( pPgc->ppArrayVob[ iVob++ ] );

    // but the last one becomes our DVDMenu - background.
    pInterface->listIntroVobs.remove ( pPgc->ppArrayVob[ --iVob ] );
    pInterface->qsMovieFileName = pPgc->ppArrayVob [ iVob ]->file;
    pInterface->qsPause = pPgc->ppArrayVob [ iVob ]->pause;
    // remove this VOB object as we got all information we needed from it.
    delete pPgc->ppArrayVob [ iVob ];

    // we should remove the VOBs array but keep the VOBs itself in memory,
    // as we have just transfered them the the DVDMenu - object.
    delete []pPgc->ppArrayVob;
    pPgc->ppArrayVob = NULL;
  } 
  
  // now let us take care of the buttons ...
  if ( pPgc->ppArrayButton ) {
    while ( pPgc->ppArrayButton[ iButton ] ) {
      if ( ! buildButtonFromPgc ( pPgc->ppArrayButton[ iButton++ ], pDVDMenu ) )
	return false;
    }
  }

  return true;
}

bool DVDAuthor::buildButtonFromPgc ( CXmlDVDAuthor::button_struct *pXMLButton, DVDMenu *pDVDMenu )
{
  QColor colorRed ( 255,  40,  40), colorBlue(  40, 40, 255 );
  TextObject   *pNewText;
  ButtonObject *pNewButton;
  MenuPreview  *pPreview = pDVDMenu->getMenuPreview();
  if ( ! pPreview )
    return false;

  pNewButton = (ButtonObject *)pPreview->createButtonObject ( false );
  pNewText   = (TextObject *)  pPreview->createTextObject   ( false );
  pNewText->setForegroundColor ( colorRed  );
  pNewText->setForegroundColor ( colorBlue );
  pNewButton->slotMoveable ( );
  pNewButton->setName      ( pXMLButton->name  );
  pNewButton->setAction    ( pXMLButton->value );
  pNewButton->appendNormal ( pNewText );

  pPreview->addMenuObject  ( pNewButton );
  return true;
}

bool DVDAuthor::buildFromTitles ( CXmlDVDAuthor::titles_struct *pTitles )
{
  int iPgc = 0;
  if ( ! pTitles->ppArrayPgc )
    return true;

  while ( pTitles->ppArrayPgc[ iPgc ] ) {
    if ( ! buildTitleFromPgc ( pTitles->ppArrayPgc[ iPgc++ ] ) )
      return false;
  }

  return true;
}

bool DVDAuthor::buildTitleFromPgc ( CXmlDVDAuthor::pgc_struct *pPgc )
{
  int iVob = 0;
  CXmlDVDAuthor::vob_struct *pVob;
  SourceFileInfo  *pInfo;
  SourceFileEntry *pEntry = new SourceFileEntry;

  pEntry->qsPre     = pPgc->pre.value;
  pEntry->qsPost    = pPgc->post.value;
  pEntry->iTitleset = m_iCurrentTitleset;
  if ( ! pPgc->ppArrayVob )
    return true;
  while ( pPgc->ppArrayVob[ iVob ] ) {
    pInfo = new SourceFileInfo;
    pVob  = pPgc->ppArrayVob[ iVob++ ];

    pInfo->qsFileName   = pVob->file;
    pInfo->listChapters = QStringList::split ( ',', pVob->chapters );
    pInfo->iTitleset    = m_iCurrentTitleset;
    if ( ( pVob->pause == QString ("inf") ) || ( pVob->pause.isEmpty() ) )
      pInfo->iPause     = -1;
    else
      pInfo->iPause     = pVob->pause.toInt();
    pEntry->listFileInfos.append ( pInfo );
  }
  pEntry->qsDisplayName.sprintf ("[%02d] - dvdauthor <%02d - files>", 
				 (int)m_pDVDAuthor->getFreeSourceSlot (), 
				 (int)pEntry->listFileInfos.count());

  //  m_pDVDAuthor->appendSourceFileEntry ( pEntry );
  m_listSourceFileEntries.append ( pEntry );
  return true;
}

bool DVDAuthor::buildFromSpumuxXML ( CXmlSpumux *pSpumux )
{
  DVDMenu *pDVDMenu;
  pDVDMenu = findMenuFromSpumux ( pSpumux );
  if ( ! pDVDMenu ) 
    return false;
  return true;
}

DVDMenu *DVDAuthor::findMenuFromSpumux ( CXmlSpumux *pSpumux )
{
  return findMenuFromSubpicture ( &pSpumux->m_subpictures );
}

DVDMenu *DVDAuthor::findMenuFromSubpicture ( CXmlSpumux::subpictures_struct *pSubpicture )
{
  return findMenuFromStream ( &pSubpicture->stream );
}

DVDMenu *DVDAuthor::findMenuFromStream ( CXmlSpumux::stream_struct *pStream )
{
  int iSpu = 0;
  DVDMenu *pDVDMenu = NULL;
  while ( pStream->ppArraySpu[ iSpu ] ) {
    pDVDMenu = findMenuFromSpu ( pStream->ppArraySpu [ iSpu++ ] );
    if ( pDVDMenu ) // findMenuFromSpu ( pStream->ppArraySpu [ iSpu++ ] ) )
      return pDVDMenu; // findMenuFromSpu ( pStream->ppArraySpu[ --iSpu ] );
  }
  return NULL;
}

DVDMenu *DVDAuthor::findMenuFromSpu ( CXmlSpumux::spu_struct *pSpu )
{
  uint t;
  bool bWeHaveAWinner;
  int  iXMLButtons, iMenu = 0;
  CXmlSpumux::button_struct *pXMLButton;
  QValueList <ButtonObject  *>listOfButtons;
  ButtonObject *pButtonObject;
  DVDMenu *pMenu = m_pVMGMenu;

  // The only link between spumux and dvdauthor xml files seem to be the button-names.
  while (  iMenu <= (int)m_listDVDMenus.count()  ) {
    listOfButtons = pMenu->getButtons ();
    if ( listOfButtons.count () > 0 ) {
      bWeHaveAWinner = true;
      for (t=0;t<listOfButtons.count();t++) {
	pButtonObject = listOfButtons[t];
	pXMLButton = findButtonObjectInArray ( pSpu->ppArrayButton, pButtonObject );
	if ( ! pXMLButton ) {
	  bWeHaveAWinner = false;
	  break;
	}
      }
      // count the number of XML buttons
      iXMLButtons = 0;
      while ( pSpu->ppArrayButton[ iXMLButtons++ ] ) ;
      iXMLButtons --; // counted one too many.
      // At this point it seems save to assume we have the right Spumux for the menu
      if ( ( bWeHaveAWinner ) && (iXMLButtons == (int)listOfButtons.count()) ) {
	return addSpuInfoToMenu ( pSpu, pMenu );
      }
    }
    // If this was not a hit, then let us try the next menu.
    pMenu = m_listDVDMenus[iMenu++];
  }
  return NULL;
}


CXmlSpumux::button_struct *DVDAuthor::findButtonObjectInArray ( CXmlSpumux::button_struct **ppArrayButton, ButtonObject *pButtonObject )
{
  uint iButton = 0;
  CXmlSpumux::button_struct *pXMLButton;

  if ( ! pButtonObject )
    return NULL;

  while ( ppArrayButton[ iButton ] ) {
    pXMLButton = ppArrayButton[ iButton++ ];
    if ( pXMLButton->label == pButtonObject->name() ) 
      return pXMLButton;
  }
  
  return NULL;
}

ButtonObject *DVDAuthor::findXMLButtonInList ( QValueList<ButtonObject *> &listOfButtons, CXmlSpumux::button_struct *pXMLButton )
{
  uint t;
  ButtonObject *pButtonObject;

  if ( ! pXMLButton )
    return NULL;
  
  for (t=0;t<listOfButtons.count();t++) {
    pButtonObject = listOfButtons[t];
    if ( pXMLButton->label == pButtonObject->name() ) 
      return pButtonObject;
  }
  
  return NULL;
}

DVDMenu *DVDAuthor::addSpuInfoToMenu ( CXmlSpumux::spu_struct *pSpu, DVDMenu *pMenu )
{
  uint iButton = 0;
  int iWidth, iHeight;
  // At this point we have the same number of buttons with the corresponding button name
  // So we should extract the button dimensions and also generate the button images / masks.
  CDVDMenuInterface       *pMenuInterface = pMenu->getInterface ();
  QValueList<ButtonObject *>listOfButtons = pMenu->getButtons   ();
  CXmlSpumux::button_struct *pXMLButton;
  ButtonObject *pButtonObject;
  QRect theRect;

  iButton = 0;
  while ( pSpu->ppArrayButton[ iButton ] ) {
    pXMLButton = pSpu->ppArrayButton[ iButton++ ];
    pButtonObject = findXMLButtonInList ( listOfButtons, pXMLButton );
    if ( ! pButtonObject )
      break; // error.

    if ( pXMLButton->label == pButtonObject->name() ) {
      iWidth  = pXMLButton->x1 - pXMLButton->x0;
      iHeight = pXMLButton->y1 - pXMLButton->y0;
      theRect.setRect ( pXMLButton->x0, pXMLButton->y0, iWidth, iHeight );
      pButtonObject->setRect ( theRect );
      pButtonObject->setNext ( NEXT_BUTTON_UP,    pXMLButton->up    );
      pButtonObject->setNext ( NEXT_BUTTON_DOWN,  pXMLButton->down  );
      pButtonObject->setNext ( NEXT_BUTTON_LEFT,  pXMLButton->left  );
      pButtonObject->setNext ( NEXT_BUTTON_RIGHT, pXMLButton->right );
      
      TextObject *pTextObject = (TextObject *)pButtonObject->getNormal ( 0 );
      if ( pTextObject )
	pTextObject->setRect ( theRect );

      // Next we should cut out the button images from the layers ...
      ImageObject *pSelectedObject    = getButtonImageFromLayer ( pMenu, theRect, pSpu->select    );
      ImageObject *pHighlightedObject = getButtonImageFromLayer ( pMenu, theRect, pSpu->highlight );
      if ( pSelectedObject )
	pButtonObject->appendSelected    ( pSelectedObject    );
      if ( pHighlightedObject )
	pButtonObject->appendHighlighted ( pHighlightedObject );
    }    
  }

  QTime qtNull;
  QTime qtStart = QTime::fromString ( pSpu->start  );
  QTime qtEnd   = QTime::fromString ( pSpu->end    );
  if ( ( qtStart.isValid () ) && ( qtEnd.isValid() ) ) {
    pMenuInterface->timeOffset   = qtStart;
    long iMsecDuration = qtStart.msecsTo ( qtEnd );
    pMenuInterface->timeDuration = qtNull.addMSecs ( iMsecDuration );
  }
  // And the last information from spu is the transparent color ...
  if ( !pSpu->transparent.isEmpty () )
    pMenuInterface->pgcColors[0] = QString ("0x%1").arg( pSpu->transparent );

  return pMenu;
}

// TODO: report DVDenus/XMLSpu untouched 
/*
<subpictures>
 <stream>
  <spu transparent="fefffe" force="yes" end="00:00:00.0" select="/tmp/ImportTest/Main Menu VMGM/selected.png" start="00:00:00.0" highlight="/tmp/ImportTest/Main Menu VMGM/highlighted.png" >
   <button y0="287" y1="397" x0="54" name="01_Button_2" x1="240" />
   <button y0="46" y1="175" x0="499" name="02_Button_1" x1="658" />
  </spu>
 </stream>
</subpictures>
*/

ImageObject *DVDAuthor::getButtonImageFromLayer ( DVDMenu *pMenu, QRect &theRect, QString qsImageName )
{
  if ( qsImageName.isEmpty () )
    return NULL;
  QString qsFileName;
  QImage  theImage ( qsImageName );
  if ( theImage.width() < 10 )
    return NULL;

  Utils        theUtil;
  QImage       theButtonImage ( theRect.width(), theRect.height(), 8, 256); //, theImage.depth() );
  QPixmap      thePixmap;
  ImageObject *pImage = new ImageObject ( pMenu->getMenuPreview () );

  bitBlt ( &theButtonImage, 0, 0, &theImage, theRect.x(), theRect.y(), theRect.width(), theRect.height(), Qt::CopyROP );
  qsFileName = theUtil.getUniqueTempFile(DRAGGED_IMAGE_NAME);
  theButtonImage.save (qsFileName, "PNG");

  thePixmap.convertFromImage ( theButtonImage );

  pImage->setZoom   ( 1.0f       );
  pImage->setRect   ( theRect    );
  pImage->setPixmap ( thePixmap  );
  pImage->setFile   ( qsFileName );

  return pImage;
}

//	// And last we connect the signals
//	connectStdSlots (pImageObject);
//	connect (pImageObject, SIGNAL(signalModifyMe(MenuObject *)), this, SLOT(slotModifyObject(MenuObject *)));

//	emit (signalUpdateStructure());
//	updatePixmap();
//	return pImageObject;


}; // End of namespace Import
