/****************************************************************************
** ButtonPreview class
**
** Created: Tue Feb 02 22:06:51 2004
**      by: Varol Okan using Kate
**
** This class is the encapsulation of the ButtonPreview from the FileSelection
** Dialog.
**
****************************************************************************/

#include <qvaluelist.h>
#include <qpopupmenu.h>
#include <qtextedit.h>
#include <qpainter.h>
#include <qcursor.h>
#include <qlabel.h>
#include <qdom.h>
#include <qdir.h>

#include "global.h"
#include "undobuffer.h"
#include "dialogtextfont.h"
#include "dialogbutton.h"
#include "buttonpreview.h"
#include "dvdmenuundoobject.h"
#include "qimagehelper.h"
#include "dvdmenu.h"

ButtonPreview::ButtonPreview(QWidget * parent, const char * name, WFlags f)
  : QLabel (parent, name, f),
    m_pUndoBuffer   ( NULL ),
    m_pActiveObject ( NULL )
{
	// Here we have the undo buffer ...
	m_pUndoBuffer = new UndoBuffer(MAX_UNDO_DEPTH);

	m_iCursorStyle = MousePointerCursor;
	m_pActiveObject = NULL;
	m_bMouseEventDone = false;
	m_bDrawVisibleRegion = false;	// This is set in the derived calss MenuPreview - only ... (No need for the buttonPreview class to play with)
}

ButtonPreview::~ButtonPreview()
{
	if (m_pUndoBuffer)
		delete m_pUndoBuffer;
}

ButtonPreview &ButtonPreview::operator= ( ButtonPreview &theOther )
{
  // Unclonable parameters ...
  //    UndoBuffer *m_pUndoBuffer;
  //  MenuObject *m_pActiveObject;
  // tells the process to skip further handling of the MouseEvents since they were already taken care of.
  //  bool        m_bMouseEventDone;

  int t;
  MenuObject *pObject;
  for ( t=0; t<(int)m_listMenuObjects.count ( ); t++ )
    delete m_listMenuObjects.at ( t );
  m_listMenuObjects.clear ( );

  for ( t=0; t<(int)theOther.m_listMenuObjects.count ( ); t++ ) {
    pObject = theOther.m_listMenuObjects.at ( t );
    if ( ! pObject )
      continue;
    pObject = pObject->clone ( this );
    // At this point we cloned a child for theOther - parent, so we need to 
    // re-parent the clone to the new owner 
    //    pObject->reParent ( this );
    addMenuObject ( pObject );
  }

  m_rectViewport       = theOther.m_rectViewport;     // This is the viewport set in drawContents()
  m_backgroundPixmap   = theOther.m_backgroundPixmap;
  m_bDrawVisibleRegion = theOther.m_bDrawVisibleRegion;
  m_iCursorStyle       = theOther.m_iCursorStyle;

  return *this;
}

bool ButtonPreview::removeObject(MenuObject *pTheObject)
{
	bool bReturn = m_listMenuObjects.remove(pTheObject);
	update ();
	return bReturn;
}

void ButtonPreview::slotDeleteObject(MenuObject *pTheObject)
{
	m_listMenuObjects.remove(pTheObject);
	delete pTheObject;
	update ();
}

void ButtonPreview::addMenuObject(MenuObject *pNewObject)
{
	// Adds the menuObject to the list of MenuObjects.
	m_listMenuObjects.append(pNewObject);
	update ();
}

void ButtonPreview::setViewport(QRect &rectViewport)
{
	m_rectViewport = rectViewport;
}

MenuObject *ButtonPreview::childContains (QPoint &pos)
{
	// Here we check if the mouse click appeared withtin one of the MenuObjects,
	// in which case the object will take over ...
	int t;
	QPoint theCorrectedPos = pos;
	if (m_rectViewport.width() > 10)	{
		theCorrectedPos.setX(pos.x()-m_rectViewport.x());
		theCorrectedPos.setY(pos.y()-m_rectViewport.y());
	}

	for (t=m_listMenuObjects.count()-1;t>=0;t--)	{
		if (m_listMenuObjects.at(t)->contains (theCorrectedPos))	{
			return m_listMenuObjects.at(t);
		}
	}
	return NULL;
}

MenuObject *ButtonPreview::menuObject (uint iNr)
{
	if (iNr < m_listMenuObjects.count())
		return m_listMenuObjects.at(iNr);
	else 
		return NULL;
}

uint ButtonPreview::objectCount()
{
	return m_listMenuObjects.count();
}

UndoBuffer *ButtonPreview::undoBuffer()
{
	return m_pUndoBuffer;
}

void ButtonPreview::toTop(MenuObject *pObject)
{
  // This function pushes the currently selected child to the top of the stack as to draw it last (on the top).
  m_listMenuObjects.remove (pObject);
  m_listMenuObjects.append (pObject);
}

void ButtonPreview::setObjectCursor ( MenuObject::enState theState )
{
  QCursor theCursor;
  switch ( theState ) {
  case MenuObject::StateScaleTLXY: // >< - cross
  case MenuObject::StateScaleBRXY: // >< - cross
    setMouseCursor ( MouseLZoomCursor );
    break;
  case MenuObject::StateScaleTRXY: // >< - cross
  case MenuObject::StateScaleBLXY: // >< - cross
    setMouseCursor ( MouseRZoomCursor );
    break;
  case MenuObject::StateScaleLeftX: // Horizontal
  case MenuObject::StateScaleRightX: // Horizontal
    setMouseCursor ( MouseXResizeCursor );
    break;
  case MenuObject::StateScaleTopY: // vertical
  case MenuObject::StateScaleBottomY: // vertical
    setMouseCursor ( MouseYResizeCursor );
    break;
  case MenuObject::StateMoveObject: // moving
    setMouseCursor ( MouseMoveCursor    );
    break;
  case MenuObject::StateRotate: // 
    setMouseCursor ( MouseRotateCursor );
    break;

  default:
    setMouseCursor ( MousePointerCursor );
    break;
  }
}

void ButtonPreview::setMouseCursor ( enPointerShape iPointerShape )
{
  QCursor theCursor;
  theCursor.setShape ( iPointerShape );
  setCursor ( theCursor );
  m_iCursorStyle = iPointerShape;
}

void ButtonPreview::slotMoveOnStack(MenuObject *pObject, int iHowMany)
{
	// This function pushes the currently selected child on the stack by iHowMany steps
	if ( ! pObject ) 
		return;

	int iIndex = m_listMenuObjects.find (pObject);
	iIndex += iHowMany;
	if (iIndex < 0)
		iIndex = 0;
	if (iIndex >(int)m_listMenuObjects.count ()-1)
		iIndex = m_listMenuObjects.count()-1;

	m_listMenuObjects.remove (pObject);

	m_listMenuObjects.insert (iIndex, pObject);
	updatePixmap ();
}

void ButtonPreview::mousePressEvent (QMouseEvent *pEvent)
{
  // Here we check if the mouse click appeared withtin one of the MenuObjects,
  // in which case the Object will take over the mouse handling ...
  QPoint thePos  = pEvent->pos();
  MenuObject *pObject = childContains(thePos);
  if (pObject != NULL)	{
    MenuObject::enState currentState = pObject->setObjectState ( thePos );
    setObjectCursor ( currentState );
    m_bMouseEventDone = pObject->mousePressEvent ( pEvent );
    // Here we protect the action toTop for creation of Buttons, which delete the returned pObject
    if (!m_bMouseEventDone)	{
      toTop(pObject);
      undoBuffer()->push (new DVDMenuUndoObject(DVDMenuUndoObject::MOVED_OBJECT, pObject));
    }
  }
}

void ButtonPreview::mouseReleaseEvent (QMouseEvent *pEvent)
{
  // Here we check if the mouse click appeared withtin one of the MenuObjects,
  // in which case the object will take over ...
  //  QPoint thePos = pEvent->pos();
  //  MenuObject *pObject = childContains(thePos);
  //  if (pObject != NULL)	{
  //    pObject->setObjectState ( MenuObject::StateInactive );
  //    m_bMouseEventDone = pObject->mouseReleaseEvent(pEvent);
  //    // here we get the last object on the undoBuffer
  //    DVDMenuUndoObject *pUndo = (DVDMenuUndoObject *) undoBuffer()->last();
  //    if (pUndo && !pUndo->hasMoved())	{
  //      undoBuffer()->removeLast();
  //      delete pUndo;
  //    }
  //  }
  for ( uint t=0; t<m_listMenuObjects.count();t++ ) {
    if ( m_listMenuObjects.at ( t )->isActive ( ) ) {
      m_bMouseEventDone = m_listMenuObjects.at ( t )->mouseReleaseEvent(pEvent);
      // here we get the last object on the undoBuffer
      DVDMenuUndoObject *pUndo = (DVDMenuUndoObject *) undoBuffer()->last();
      if (pUndo && !pUndo->hasMoved())	{
	undoBuffer()->removeLast();
	delete pUndo;
      }
    }
  }
  unsetCursor  ( );
}

void ButtonPreview::mouseDoubleClickEvent	(QMouseEvent *pEvent)
{
	// Here we check if the mouse click appeared withtin one of the buttons,
	// in which case the button will take over ...
	QPoint thePos = pEvent->pos();
	MenuObject *pObject = childContains(thePos);
	if (pObject != NULL)
		m_bMouseEventDone = pObject->mouseDoubleClickEvent(pEvent);
}

QPixmap &ButtonPreview::renderMenu (CDVDMenuInterface *pInterface, long iRenderFrameNumber, long iTotalFramesToRender)
{
	if (m_backgroundPixmap.isNull())
		return m_backgroundPixmap;
	if (!pInterface)
		return m_backgroundPixmap;

	uint        t, iCurrentBackgroundFile;
	QString     qsFileName, qsFullPath, qsMenuName;
	MenuObject *pMenuObject;
	QFileInfo   fileInfo;
	QFile       theFile;
	QDir        theDir;
	// ////////////////////////////////////////////////////////////////////////////////////////////
	// This function renders the frame number iRenderFrameNumber and spits the generated file out as :
	// /TMP_PATH/PROJECT_NAME/MENU_NAME/background/rendered_$(iRenderFrameNumber).jpg
	//
	// This function requires that the extracted frames of the background are located under 
	// /TMP_PATH/PROJECT_NAME/MENU_NAME/background/$(iRenderFrameNumber).jpg
	// And also that all movieObjects (imageObjects = movieButton with 1 frame) frames are found under :
	// /TMP_PATH/PROJECT_NAME/MENU_NAME/$(MovieFileName)/$(iRenderFrameNumber).jpg
	// /////////////////////////////////////////////////////////////////////////////////////////////
	qsMenuName = pInterface->qsMenuName; 
	qsFullPath = QString ("%1/%2/%3/background/").arg(Global::qsTempPath) .arg(Global::qsProjectName).arg(qsMenuName);

	// Okay here we get the Directory list of the extracted image files from the directory
	theDir.setPath(qsFullPath);
	QStringList listFiles = theDir.entryList ("0*.jpg");
	//QStringList listFiles = theDir.entryList ("rendered_*.png");
	if (listFiles.count () == 0)	{
		qsFullPath = QString ("%1/%2/").arg(Global::qsTempPath).arg(Global::qsProjectName);
		theDir.setPath (qsFullPath);
		// Okay nothing extracted, let us assume we have a backgournd image and NOT a movie.
		qsFileName = QString ("%1/%2/%3/"BACKGROUND_NAME).arg(Global::qsTempPath).arg(Global::qsProjectName).arg(qsMenuName);
		// We should however check if the file exists ...
		fileInfo.setFile(qsFileName);
		if (!fileInfo.exists())
			return m_backgroundPixmap;
		// Okay the file exists so let us add it to the empty list ...
		listFiles.append (QString ("%1/"BACKGROUND_NAME).arg(qsMenuName));
	}

	// And here we get the index of the current background image.
	// Now please note that we might have completely different number of images for any 
	// of the media files (background / movie-clips etc.)

	// Version 1 will stretch the found files to fit exactly to the required framesToRender
	iCurrentBackgroundFile = (int)((float)listFiles.count() * (float)iRenderFrameNumber/(float)iTotalFramesToRender);
	// second alternative : render each frame 1:1 and at the end stay at the last frame
	if (0)	{
		iCurrentBackgroundFile = iRenderFrameNumber-1;
		if (iCurrentBackgroundFile > theDir.count())
			iCurrentBackgroundFile = theDir.count();
	}

	// Here we determine the fileName ...
	qsFileName = QString ("%1/%2").arg(theDir.absPath()).arg(listFiles[iCurrentBackgroundFile]);
	theFile.setName (qsFileName);

	if (!theFile.exists())	{
		//error ...
		return m_backgroundPixmap;
	}

	QPixmap thePixmap(qsFileName);
	// This step is necessary to get the attributes for the backround right if the image / movie does
	// not fit the requested Menu size.
	if (	( (m_backgroundPixmap.width() > 30) || (m_backgroundPixmap.height() > 30) ) && 
		( (thePixmap.width() != m_backgroundPixmap.width()) || (thePixmap.height() != m_backgroundPixmap.height()) ) )
			fitDVDMenuBackground (&thePixmap, 0, pInterface->iStretchType, pInterface->posOffset, pInterface->iWidth, pInterface->iHeight);
//			thePixmap.resize (m_backgroundPixmap.width(), m_backgroundPixmap.height());

	QPainter thePainter(&thePixmap);
	// And here we draw the objects ...
	for (t=0;t<m_listMenuObjects.count();t++)	{
	  pMenuObject = m_listMenuObjects.at(t);
	  // Filter out if StopFrame is > 0(property is used) AND the current frame is within the limits
	  if ( ( pMenuObject->modifiers()->iStopFrame < 1) || 
	       (  ( iRenderFrameNumber > pMenuObject->modifiers()->iStartFrame ) &&
		  ( iRenderFrameNumber < pMenuObject->modifiers()->iStopFrame  ) ) )
	    pMenuObject->drawContents (&thePainter, iRenderFrameNumber, iTotalFramesToRender);
      }

	float fProgress = (float)iRenderFrameNumber / iTotalFramesToRender;
	emit (signalRenderProgress (fProgress));

	m_backgroundPixmap = thePixmap;
	return m_backgroundPixmap;
}

void ButtonPreview::fitDVDMenuBackground (QPixmap *pPixmap, int iResizeAlgorithm, int iStretchType, QSize posOffset, int iWidth, int iHeight)
{
	// Okay to tackle the background image generation
	QImageHelper theImage;
	((QImage&)theImage) = *pPixmap;
	// set orig Pixmap to zero to save memory ...
	*pPixmap = QPixmap();
	// Next we handle the resizing pending the checked RadioButton algo ...
	if (iStretchType > CDVDMenuInterface::StretchToFit)	{
		// we create a tempImage 
		QImage tempImage;
		int iDeltaX, iDeltaY, iResizeAlgorithm;
		iDeltaX = iDeltaY = iResizeAlgorithm = 0;

		if (iStretchType == CDVDMenuInterface::StretchCutOff)	{	// Cut Off
			tempImage = theImage.smoothScale (iWidth, iHeight, QImage::ScaleMax);
			iDeltaX = posOffset.width ();
			iDeltaY = posOffset.height();
		}
		else	{	// m_DVDMenuInterface::StretchBlackBorder
			tempImage = theImage.smoothScale (iWidth, iHeight, QImage::ScaleMin);
			// Note: The tempImage does not need to have the same extensions due to the ScaleMin flag.
			iDeltaX = iWidth - tempImage.width();
			if (iDeltaX > 0)
				iDeltaX = (int)((float)iDeltaX / 2.0);
			iDeltaY = iHeight - tempImage.height();
			if (iDeltaY > 0)
				iDeltaY = (int)((float)iDeltaY / 2.0);
		}

		theImage.fill (0);  //Qt::black);
		theImage.resize (iWidth, iHeight, iResizeAlgorithm);
		bitBlt (&theImage, iDeltaX, iDeltaY, &tempImage, 0, 0, tempImage.width(), tempImage.height(), Qt::CopyROP);
	}
	else	// StretchToFit
		theImage.resize (iWidth, iHeight, iResizeAlgorithm);

	*pPixmap = QPixmap (iWidth, iHeight);

	// and here we set the final pixmap.	
	pPixmap->convertFromImage (theImage);
}

void ButtonPreview::drawContents(QPainter *painter)
{
	QLabel::drawContents (painter);
	// We use the viewport to draw the Object in the Center for the ButtonDialog.
	// The Viewport is not used in the MenuPreview for the main window, thus drawing the button
	// on the right location ...
	if (m_rectViewport.width() > 10)
		painter->setViewport(m_rectViewport);
	// In case the user has not yet defined a background Image, we want to show the Objects anyways ...
	// Note: we draw usually all objects into the pixmap, which we have set as a background see updatePixmap()
	if (m_backgroundPixmap.isNull())	{
		uint t;
		erase(5, 5, width()-10, height()-10);
		drawVisibleRegion(painter);
		for (t=0;t<m_listMenuObjects.count();t++)	{
			m_listMenuObjects.at(t)->drawContents (painter);
		}
		return;
	}
	// This will draw the currentObject on top of the 'copy' we already painted into the pixmap. 
	if (m_pActiveObject)
		m_pActiveObject->drawContents(painter);
}

void ButtonPreview::drawVisibleRegion(QPainter *pPainter)
{
	// Take the visible region at 7% of the total extension (2 * 3.5%)...
	if (m_bDrawVisibleRegion)	{
		uint iXOffset, iYOffset;
		QPen thePen (QColor (255, 30,30), 2, Qt::DashDotLine);
		iXOffset = (int)(3.5/100.0*pPainter->viewport().width());
		iYOffset = (int)(3.5/100.0*pPainter->viewport().height());
		pPainter->setPen(thePen);
		QRect visibleRect (iXOffset, iYOffset, pPainter->viewport().width() - iXOffset*2, pPainter->viewport().height() - iYOffset*2);
		pPainter->drawRect (visibleRect);
	}
}

void ButtonPreview::setPixmap (const QPixmap &pixmap)
{
	// This is an overloaded function from QLabel
	// We are going to handle the double buffer here ...
	m_backgroundPixmap = pixmap;
	// Here we get rid of the checkered - background pixmap, No need after a real pixmap has been set ...
//	if (!pixmap.isNull())
//		setPaletteBackgroundPixmap (NULL);

	updatePixmap ();
}

void ButtonPreview::updatePixmap()
{
	if (m_backgroundPixmap.isNull())
		return;
	// Set the waiting cursor ...
	QCursor myCursor (QCursor::WaitCursor);
	setCursor(myCursor);

	// This is the function, which updates the background Pixmap
	QPixmap thePixmap;
	drawBackground ( thePixmap );

	// This will check if we load the transparent background (extensions are much smaller)
	if ( (thePixmap.width() < 100) || (thePixmap.height() < 100) )
		setFixedSize(720, 480);
	else	// Otherwise we set the original image size.
		setFixedSize(thePixmap.width(), thePixmap.height());

	setBackground ( thePixmap );
	// re-set the cursor ...
	myCursor = QCursor(QCursor::ArrowCursor);
	setCursor(myCursor);
}

void ButtonPreview::setBackground ( QPixmap &thePixmap )
{
  // This change might affect some areas I am not currently considering. (2004 Oct 20)
  if ( parentWidget ( )->parentWidget ( )->name ( ) == QString ( "usingScrollView" ) )
    setPaletteBackgroundPixmap ( thePixmap );
  else
    QLabel::setPixmap ( thePixmap );
}

void ButtonPreview::drawBackground ( QPixmap &thePixmap )
{
  uint t;
  thePixmap = QPixmap ( m_backgroundPixmap );
  QPainter thePainter ( &thePixmap  );
  // First lets imprint the visibleRegionR - rect (if neccesarry).
  drawVisibleRegion   ( &thePainter );
  // And here we draw the objects ...
  for (t=0;t<m_listMenuObjects.count();t++)
    m_listMenuObjects.at(t)->drawContents (&thePainter);
}
