/****************************************************************************
**
**  indeview_slides.cpp  of the IndeView project, see http://www.indeview.org
**
**
**  The IndeView application is a stand-alone viewer to display presentations
**  created by IndeView creator modules like IndeViewExport.bas or kpr2iv.sh.
**  -------------------------------------------------------------------------
**  This enables you to show OpenOffice/StarOffice or KOffice presentations
**  on machines where the respective presentation program is not available.
**
**
**  Copyright (C) 2003..2004 Michael & Karl-Heinz Zimmer.
**  All rights reserved.
**
**  This file is distributed under the terms of the
**  GNU Lesser General Public License version 2.1 as published by the
**  Free Software Foundation and appearing in the file LICENSE included
**  in the packaging of this file.
**
**  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
**  THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
**  PURPOSE.
**
**  See http://www.indeview.org for further information.
**
**  Contact info@indeview.org if any conditions of this
**  licensing are not clear to you.
**
**
**  Please consider sending
**  a little picture postcard to   Michael & Karl-Heinz Zimmer
**                                 Butzengasse 15
**                                 54343 Föhren
**                                 Germany
**
**  Your feedback will be very much appreciated: we would be glad about
**  receiving a visual representation of where on earth you are living
**  using our little module.
**
*****************************************************************************/

#include <qapplication.h>
#include <qlayout.h>
#include <qvbox.h>
#include <qdialog.h>
#include <qlabel.h>
#include <qpainter.h>
#include <qcursor.h>
#include <qfile.h>
#include <qurl.h>

//#include <qmessagebox.h>


#include "indeview_slides.h"
#include "indeview_slide.h"



IndeViewSlide* findSlide(QPtrList<IndeViewSlide>& slides, const QString& sID)
{
  if( sID.isEmpty() || sID == "NONE" )
    return 0;
  for( IndeViewSlide* slide = slides.first(); slide; slide = slides.next() )
    if( sID == slide->sPageID )
      return slide;
  return 0;
}


bool readSlide( const QString& infoFile,
                IndeViewSlide& slide,
                QTextEdit* progressWidget,
                QTextEdit* detailsWidget )
{
  //QMessageBox::critical(qApp->desktop(), "infoFile", infoFile);
  QFile file( infoFile );
  if ( file.open( IO_ReadOnly ) ) {
    QString infoFilePath;
    {
      QUrl infoFileURL( infoFile );
      QString sName( infoFileURL.fileName() );
      const int idxFileName = infoFile.findRev( sName );
      if( -1 != idxFileName ){
        infoFilePath = infoFile.left( idxFileName );
         //QMessageBox::critical(qApp->desktop(), "infoFilePath", infoFilePath);
      }else{
        QString infoFilePath( infoFileURL.toString(true, true) );
        if( infoFilePath.startsWith("file:", false) ){
          infoFilePath.remove(0, 5);
          infoFilePath = infoFilePath.stripWhiteSpace();
        }
        if( -1 == infoFilePath.find(":") ){
          QString appFile(qApp->applicationFilePath());
          if( 1 == appFile.find(":") )
            infoFilePath.prepend( appFile.left(2) );
        }
         //QMessageBox::critical(qApp->desktop(), "infoFilePath", infoFilePath);
      }
    }

    QTextStream stream( &file );
    QString line;
    while ( !stream.atEnd() ) {
      line = stream.readLine().simplifyWhiteSpace();
      // ignore empty lines and lines starting with '#' or starting with "//"
      if( !line.isEmpty() && !line.startsWith("#") && !line.startsWith("//") ){
        const int idx = line.find('=');
        // ignore all lines having no '=' sign
        if( 0 < idx ){
          const QString key(line.left( idx ).upper());
          QString value(line.mid( idx+1 ));

          if( key == "NAME" ){
            if( progressWidget )
              progressWidget->append( QString("<b>%1</b>").arg(value) );
            if( detailsWidget )
              detailsWidget->clear();
            slide.sPageName = value;
          }else if( key == "FILE" ){
            QSize size;
            QString sizeString;
            bool bXFound = false;
            const int xPos = value.findRev("x", -1, false);
            if( 1 < xPos ){
              const int spacePos = value.findRev(" ", xPos);
              if( 0 < xPos ){
                bool bOK;
                const int w = value.mid(spacePos+1, xPos-spacePos-1).toInt(&bOK);
                if( bOK ){
                  int h = value.mid(xPos+1).toInt(&bOK);
                  if( bOK ){
                    size.setWidth(w);
                    size.setHeight(h);
                    sizeString = value.mid(xPos+1);
                    value.truncate( spacePos );
                    bXFound = true;
                  }
                }
              }
            }
            if( bXFound ){
              if( detailsWidget )
                detailsWidget->append( sizeString );
              // convert relative paths into absolute ones
              if( value.startsWith("file:", false) ){
                value.remove(0, 5);
                value = value.stripWhiteSpace();
              }
              if( -1 == value.find(":") && !value.startsWith("/") && !value.startsWith("\\") ){
                QUrl url( value );
                value = infoFilePath + url.path();
              }
              slide.addImageDescription(IndeViewSizeAndFile(value, size));
            }else{
              if( detailsWidget )
                detailsWidget->append( QString("ERROR: <i>%1</i> has no size!").arg(value) );
            }
          }else if( key == "NOTES" ){
            slide.lNotes.append( value );
          }else if( key == "TRANSITIONEFFECT" ){
            value = value.upper();
            if( value == "NONE" )
              slide.eFadeEffect = IndeViewFadeEffect_NONE;
            else // unknown?  Let's use the default value
              slide.eFadeEffect = IndeViewFadeEffect_NONE;
          }else if( key == "TRANSITIONSPEED" ){
            value = value.upper();
            if( value == "SLOW" )
              slide.nAnimationSpeed = 1;
            else if( value == "MEDIUM" )
              slide.nAnimationSpeed = 5;
            else if( value == "FAST" )
              slide.nAnimationSpeed = 9;
            else{
              bool bOK;
              int nSpeed = value.toInt( &bOK );
              if( bOK && 1<=nSpeed && nSpeed<=9 )
                slide.nAnimationSpeed = nSpeed;
            }
          }else if( key == "TRANSITIONSOUND" ){
            if( value == "NONE" )
              slide.sFadeSound = "";
            else
              slide.sFadeSound = value;
          }else if( key == "TRANSITIONSOUNDMODE" ){
            value = value.upper();
            if( value == "FULL" )
              slide.eFadeSoundMode = IndeViewFadeSoundMode_FULL;
            else if( value == "CUT" )
              slide.eFadeSoundMode = IndeViewFadeSoundMode_CUT;
          }else if( key == "VIEWSOUND" ){
            if( value == "NONE" )
              slide.sViewSound = "";
            else
              slide.sViewSound = value;
          }else if( key == "VIEWSOUNDLOOP" ){
            bool bOK;
            int nLoop = value.toInt( &bOK );
            if( bOK )
              slide.nViewSoundLoop = nLoop;
          }else if( key == "VIEWDURATION" ){
            bool bOK;
            int nSec10 = value.toInt( &bOK );
            if( bOK )
              slide.nDuration = nSec10;
          }else if( key == "VIEWDURATIONMODE" ){
            value = value.upper();
            if( value == "ABSOLUTE" )
              slide.eDurationMode = IndeViewDurationMode_ABSOLUTE;
            else if( value == "RELATIVE" )
              slide.eDurationMode = IndeViewDurationMode_RELATIVE;
          }else if( key == "CHANGETONEXT" ){
            value = value.upper();
            if( value == "AUTO" )
              slide.eChange = IndeViewChangeMode_AUTO;
            else if( value == "USER" )
              slide.eChange = IndeViewChangeMode_USER;
          }
        }
      }
    }
    file.close();
    return true;
  }
  return false;
}


IndeViewSlides::IndeViewSlides( const QString& runFileName,
                                bool createAnOwnProgressDialog,
                                QTextEdit* existingProgressWidget,
                                QTextEdit* existingDetailsWidget ):
  mRootSlide( 0 ),
  mLastSlide( 0 )
{
  mCache = new IndeViewCachedPixmaps();

  QApplication::setOverrideCursor(QCursor( Qt::WaitCursor ));

  mSlides.setAutoDelete( true );

  QFile file( runFileName );
  if ( file.open( IO_ReadOnly ) ) {
    QString runFilePath;
    {
      // QMessageBox::critical(qApp->desktop(), "runFile", runFile);
      QUrl runFileURL( runFileName );
      QString sName( runFileURL.fileName() );
      const int idxFileName = runFileName.findRev( sName );
      if( -1 != idxFileName ){
        runFilePath = runFileName.left( idxFileName );
        // QMessageBox::critical(qApp->desktop(), "runFilePath", runFilePath);
      }else{
        QString runFilePath( runFileURL.toString(true, true) );
        if( runFilePath.startsWith("file:", false) ){
          runFilePath.remove(0, 5);
          runFilePath = runFilePath.stripWhiteSpace();
        }
        if( -1 == runFilePath.find(":") ){
          QString appFile(qApp->applicationFilePath());
          if( 1 == appFile.find(":") )
            runFilePath.prepend( appFile.left(2) );
        }
        // QMessageBox::critical(qApp->desktop(), "runFilePath", runFilePath);
      }
    }
    QString version;

    QTextEdit* progressWidget = 0;
    QTextEdit* detailsWidget  = 0;
    QDialog *dialog = 0;
    if( createAnOwnProgressDialog ){
      dialog = new QDialog ( qApp->desktop() );
      dialog->setCaption( "IndeView" );
      dialog->setPaletteBackgroundColor( Qt::lightGray );
      dialog->setPaletteForegroundColor( Qt::black );
      QVBoxLayout* vLay = new QVBoxLayout( dialog, 8 );

      progressWidget = new QTextEdit( dialog );
      progressWidget->setTextFormat( Qt::RichText );
      progressWidget->setPaletteBackgroundColor( Qt::white );
      progressWidget->setPaletteForegroundColor( Qt::black );
      vLay->addWidget( progressWidget, 1 );

      detailsWidget = new QTextEdit( dialog );
      detailsWidget->setTextFormat( Qt::RichText );
      detailsWidget->setPaletteBackgroundColor( Qt::white );
      detailsWidget->setPaletteForegroundColor( Qt::black );
      vLay->addWidget( detailsWidget, 2 );

      dialog->move( qApp->desktop()->width()/2 -200,
                    qApp->desktop()->height()/2-200 );
      dialog->setFixedSize(400,400);
      dialog->raise();
      dialog->show();
    }else{
      progressWidget = existingProgressWidget;
      detailsWidget  = existingDetailsWidget;
    }
    if( progressWidget )
      progressWidget->append( "<b>loading presentation:</b><br>" );

    QTextStream stream( &file );
    QString line;
    bool bCommon = false;
    IndeViewSlide* pSlide = 0;
    QString sStartPageID;    // the internal name of the start slide
    while ( !stream.atEnd() ) {
      line = stream.readLine().simplifyWhiteSpace();
      // ignore empty lines and lines starting with '#' or starting with "//"
      if( !line.isEmpty() && !line.startsWith("#") && !line.startsWith("//") ){
        if( line.startsWith("[") ){
          const int idx = line.find(']');
          // ignore all lines starting with '[' if they have no closing bracket
          if( -1 < idx ){
             // ignore trailing garbage behind the closing ']'
            const QString section = line.mid(1, idx-1).upper();
            if( !section.isEmpty() ){
              if( pSlide ){
                mSlides.append( pSlide );
                pSlide = 0;
              }
              bCommon = section == "COMMON";
              if( !bCommon ){
                pSlide = new IndeViewSlide( section, *mCache );
              }
            }
          }   // ignore all lines that preceed the first section
        }else if( bCommon || pSlide ){
          const int idx = line.find('=');
          // ignore all lines having no '=' sign
          if( 0 < idx ){
            const QString key(line.left( idx ).upper());
            QString value(line.mid( idx+1 ));
            if( bCommon ){
              if( key == "START" ){
                sStartPageID = value.upper();
              }else if( key == "VERSION" ){
                version = value.upper();
              }
            }else{
              if( key == "INFO" ){
                // convert relative paths into absolute ones
                if( value.startsWith("file:", false) ){
                  value.remove(0, 5);
                  value = value.stripWhiteSpace();
                }
                if( -1 == value.find(":") && !value.startsWith("/") && !value.startsWith("\\") ){
                  QUrl url( value );
                  value = runFilePath + url.path();
                }
                if( readSlide( value,
                               *pSlide,
                               progressWidget,
                               detailsWidget ) ){
                  // left empty intentionally
                }
              }else if( key == "NEXT" ){
                pSlide->sNextID = value.upper();
              }
            }
          }
        }
      }
    }
    file.close();
    if( pSlide ){
      mSlides.append( pSlide ); // save last slide section
      pSlide = 0;
    }

    // build the linked slide-tree
    mRootSlide = findSlide(mSlides, sStartPageID);
    mLastSlide = mRootSlide;
    if( mRootSlide ){
      (mRootSlide)->pPrev = 0;
      (mRootSlide)->pNext = findSlide(mSlides, (mRootSlide)->sNextID);
      if( (mRootSlide)->pNext ){
        (mRootSlide)->pNext->pPrev = mRootSlide;
        pSlide = (mRootSlide)->pNext;
        mLastSlide = pSlide;
        do{
          pSlide->pNext = findSlide(mSlides, pSlide->sNextID);
          if( pSlide->pNext ){
            pSlide->pNext->pPrev = pSlide;
          }
          pSlide = pSlide->pNext;
          if( pSlide && !pSlide->pNext )
            mLastSlide = pSlide;
        }while( pSlide && !pSlide->pNext ); // this avoids recursion
      }
    }
    if( dialog )
      delete dialog;
  }
  QApplication::restoreOverrideCursor();	// restore original cursor
}


IndeViewSlides::~IndeViewSlides()
{
  delete mCache;
}

bool IndeViewSlides::structureOK()const
{
  return mRootSlide && mLastSlide;
}

QStringList IndeViewSlides::slideNames()const
{
  QStringList list;
  const IndeViewSlide* p = mRootSlide;
  const IndeViewSlide* oldP = 0;
  while( p && oldP != mLastSlide ){
    list << p->sPageName;
    oldP = p;
    p = p->pNext;
  }
  return list;
}

const IndeViewSlide* IndeViewSlides::rootSlide()const
{
  return mRootSlide;
}
const IndeViewSlide* IndeViewSlides::lastSlide()const
{
  return mLastSlide;
}
