/****************************************************************************
** listviewmedia - class
**
**   Created :
**        by : Varol Okan 
** Copyright : (c) Varol Okan
**   License : GPL v 2.0
**
** This file re-implements the QListView class
** and handles the drag event by re-implementing
** the dragObject function.
**
*****************************************************************************/
#include <qimage.h>
#include <qpoint.h>
#include <qlistview.h>
#include <qfileinfo.h>
#include <qdragobject.h>

#include "global.h"
#include "qdvdauthor.h"
#include "listviewmedia.h"
#include "sourcefileentry.h"
#include "listviewitemmedia.h"

ListViewMedia::ListViewMedia (QWidget *pParent, const char *pName, WFlags f)
	: QListView (pParent, pName, f)
{
	setAcceptDrops(true);
	setSorting (-1);
	setAllColumnsShowFocus(true);
	m_pDraggingSourceFileInfo  = NULL;
	m_pDraggingSourceFileEntry = NULL;
	connect (this, SIGNAL (clicked (QListViewItem *, const QPoint &, int)), this , SLOT (slotItemClicked (QListViewItem *, const QPoint &, int)));
}

ListViewMedia::~ListViewMedia ()
{

}

void ListViewMedia::resetDragging ()
{
	// Okay here we should re-set the sourceFileInfo for the next drag...
	m_pDraggingSourceFileEntry = NULL;
	m_pDraggingSourceFileInfo  = NULL;
}

SourceFileEntry *ListViewMedia::draggingSourceFileEntry()
{
	return m_pDraggingSourceFileEntry;
}

SourceFileInfo *ListViewMedia::draggingSourceFileInfo()
{
	return m_pDraggingSourceFileInfo;
}

void ListViewMedia::dragEnterEvent ( QDragEnterEvent *pEvent )
{
	// Tell the Widget that we accept ImageDrops ...
	// This is necessary otherwise the dropEvent does not occur.
	bool bAccept = QImageDrag::canDecode ( pEvent );
	bAccept     |= QTextDrag::canDecode  ( pEvent );

	pEvent->accept ( bAccept );
}

void ListViewMedia::dropEvent ( QDropEvent *pDropEvent )
{
	// This function handles the re-sorting of the SourceFileEntries.
	// Meaning if the user can drag'n drop a SourceFileInfo and drop it where he want's to.
	// This also means that we need to handle different SourceFileEntries for the SourceFileInfo.
	// First we check if the source came from this listView, otherwise we don't really care.
	QString qsText;
	if ( QTextDrag::decode ( pDropEvent, qsText ) )  {
		// Lets see if we have a video file
		Global::pApp->addDroppedSource ( qsText );
		return;
	}
	if ( pDropEvent->source ( ) != this )
		return;

	SourceFileEntry *pSourceFileEntry = draggingSourceFileEntry ( );
	SourceFileInfo  *pSourceFileInfo  = draggingSourceFileInfo  ( );  // Could be NULL if we dragged a Header

	// If a slideshow is in the makinng, then stop right here ...
	if ( pSourceFileEntry->bIsSlideshow )
		return;

	// Okay we have the source SourceFileEntry and SourceFileInfo
	// now we need to find the target insertion point
	ListViewItemMedia *pSourceItem = (ListViewItemMedia *)currentItem ();
	ListViewItemMedia *pSourceHeader = pSourceItem;	// We assume the source is a header
	if (!pSourceHeader->sourceFileEntry())
		pSourceHeader = (ListViewItemMedia *)pSourceItem->parent();

	QPoint p = pDropEvent->pos();
	// and since we actually are only looking for a height comparison,
	// we set x to always hit a entry (if there is one).
	p.setX(50);
	ListViewItemMedia *pTargetItem = (ListViewItemMedia *)itemAt (p);
	ListViewItemMedia *pTargetHeader = pTargetItem;	// We assume for now that the Item is a header.
	if (!pTargetItem)
		return;
	// If we drag'n Drop the same item we don't do nothin'
	if (pTargetItem == pSourceItem)
		return;
	SourceFileEntry *pTargetFileEntry = pTargetItem->sourceFileEntry();
	SourceFileInfo  *pTargetFileInfo  = pTargetItem->sourceFileInfo ();
	if ( pSourceFileEntry == pTargetFileEntry ) {
	  resetDragging ( );
	  return;
	}
	if (!pTargetFileEntry)	{
		// If we dragged onto a child (normal case) then we ought to obtain the entry from its parent.
		pTargetHeader = ((ListViewItemMedia *)pTargetItem->parent());
		pTargetFileEntry = pTargetHeader->sourceFileEntry();
	}
	if (!pTargetFileInfo)
		pTargetFileInfo = pTargetFileEntry->listFileInfos[0];
	// Okay at this point we have all we need.
	if (pSourceFileInfo)	{
		// REMOVE and
		pSourceFileEntry->listFileInfos.remove (pSourceFileInfo);
		// Next we check if we removed the last SourceFileInfo from the SourceFileEntry.
		if ( pSourceFileEntry->listFileInfos.count ( ) < 1 )  {
		  emit (signalRemoveEntry (pSourceFileEntry));	// Go back to SourceToolBar::slotRemoveEntry()
		  delete pSourceFileEntry;
		  pSourceFileEntry = NULL;
		}
		// INSERT
		QValueList<SourceFileInfo *>::iterator it;
		for (it = pTargetFileEntry->listFileInfos.begin(); it != pTargetFileEntry->listFileInfos.end(); ++it)	{
			if ((*it) == pTargetFileInfo)	{
				pTargetFileEntry->listFileInfos.insert (it, pSourceFileInfo);
				break;
			}
		}

		// In case we jump from one entry to another, we need to build two new entries
		if (pTargetFileEntry != pSourceFileEntry)	{
		  // We do also need to adjust the audio tracks
		  handleAudioTracks    ( pTargetFileEntry );
		  // and not to forget the subtitles ...
		  handleSubtitleTracks ( pTargetFileEntry );
		  // Create only if there is something to create ...
		  if (pSourceFileEntry)
		    new ListViewItemMedia (this, pSourceItem, pSourceFileEntry, pSourceItem->backgroundColor());
		  delete pSourceHeader;
		}
		new ListViewItemMedia (this, pTargetItem, pTargetFileEntry, pTargetItem->backgroundColor());
		delete pTargetHeader;
	}
	else 	{	// If pSourceFileInfo = NULL then the user only wants
			//to reposition the SourceFileEntry in the list.
		new ListViewItemMedia (this, pSourceItem, pTargetFileEntry, pSourceItem->backgroundColor());
		delete pTargetHeader;
	}
	resetDragging ();
//	triggerUpdate ();
	return;
}

void ListViewMedia::handleSubtitleTracks ( SourceFileEntry *pEntry )
{
  // Here we rectify the subtitleEntry - list in the Entry as well as in the Infos.
  int t, i, j;
  SourceFileInfo *pInfo;
  Subtitles *arrayTarget[MAX_SUBTITLES], *pSubtitle;

  for ( t=0; t<(int)pEntry->listFileInfos.count ( ); t++ ) {
    pInfo = pEntry->listFileInfos[ t ];
    for ( i=0; i<MAX_SUBTITLES; i++ )
      arrayTarget[i] = NULL;

    for ( i=0; i<MAX_SUBTITLES; i++ ) {
      pSubtitle = pInfo->arraySubtitles [ i ];
      if ( pSubtitle ) {
	// Check if object is valid ...
	if ( ( pSubtitle->m_iSubtitleNumber < 0 ) || ( pSubtitle->m_iSubtitleNumber >= MAX_SUBTITLES ) ) {
	  delete pSubtitle;
	  pInfo->arraySubtitles [ i ] = NULL;
	  continue;
	}
	// At this point we drilled down to the subtitle - level
	if ( pSubtitle->m_qsIso639 != pEntry->arraySubtitleEntries[ pSubtitle->m_iSubtitleNumber ].m_qsIso639 ) {
	  // First we see if the language is already in the Entry ...
	  for ( j=1; j<MAX_SUBTITLES; j++ ) {
	    if ( ( pEntry->arraySubtitleEntries[i].m_qsIso639 == pSubtitle->m_qsIso639 ) && // Found same Language ...
		 ( pEntry->arraySubtitleEntries[i].m_iSubtitleNumber != -1 ) &&           // seems a valid number
		 ( !arrayTarget[i] ) )                         // which is not yet used ...
	      break;
	  }
	  // If we could not find this language in the Entry, then let us search for the first empty slot ...
	  if ( j>=MAX_SUBTITLES ) {
	    for ( j=0; j<MAX_SUBTITLES; j++ ) {
	      if ( pEntry->arraySubtitleEntries [ j ].m_iSubtitleNumber == -1 ) // found empty slot ...
		break;
	    }
	  }
	  if ( j>=MAX_SUBTITLES ) {
	    delete pSubtitle;
	    pInfo->arraySubtitles [ i ] = NULL;
	  }
	  else { // And finally we can create the new entry and shift around ...
	    pEntry->arraySubtitleEntries [ j ].m_iSubtitleNumber = j;
	    pEntry->arraySubtitleEntries [ j ].m_qsIso639 = pSubtitle->m_qsIso639;
	    pSubtitle->m_iSubtitleNumber = j;
	    arrayTarget[ j ] = pSubtitle;
	    // store in a temp buffer as pInfo->arraySubtitles[j] might hold some other entry
	    pInfo->arraySubtitles [ i ] = NULL;
	  }
	}
      }
    }
    // And if the buffer holds anything then copy it over
    for ( i=0; i<MAX_SUBTITLES; i++ ) {
      if ( arrayTarget[i] ) 
	pInfo->arraySubtitles [i] = arrayTarget[i];
    }
  }
}

void ListViewMedia::handleAudioTracks ( SourceFileEntry *pEntry )
{
  // Here we rectify the audioEntry - list in the Entry as well as in the Infos.
  int t, i, j;
  SourceFileInfo *pInfo;
  Audio *arrayTarget[MAX_AUDIO], *pAudio;
  

  for ( t=0; t<(int)pEntry->listFileInfos.count ( ); t++ ) {
    pInfo = pEntry->listFileInfos[ t ];
    for ( i=0; i<MAX_AUDIO; i++ )
      arrayTarget[i] = NULL;

    for ( i=0; i<MAX_AUDIO; i++ ) {
      pAudio = pInfo->arrayAudio [ i ];
      if ( pAudio ) {
	// Check if object is valid ...
	if ( ( pAudio->iAudioNumber < 0 ) || ( pAudio->iAudioNumber >= MAX_AUDIO ) ) {
	  delete pAudio;
	  pInfo->arrayAudio [ i ] = NULL;
	  continue;
	}
	// At this point we drilled down to the audio - level
	if ( pAudio->qsIso639 != pEntry->arrayAudioEntries[ pAudio->iAudioNumber ].qsIso639 ) {
	  // First we see if the language is already in the Entry ...
	  for ( j=1; j<MAX_AUDIO; j++ ) {
	    if ( ( pEntry->arrayAudioEntries[i].qsIso639 == pAudio->qsIso639 ) && // Found same Language ...
		 ( pEntry->arrayAudioEntries[i].iAudioNumber != -1 ) &&           // seems a valid number
		 ( !arrayTarget[i] ) )                         // which is not yet used ...
	      break;
	  }
	  // If we could not find this language in the Entry, then let us search for the first empty slot ...
	  if ( j>=MAX_AUDIO ) {
	    for ( j=0; j<MAX_AUDIO; j++ ) {
	      if ( pEntry->arrayAudioEntries [ j ].iAudioNumber == -1 ) // found empty slot ...
		break;
	    }
	  }
	  if ( j>=MAX_AUDIO ) {
	    delete pAudio;
	    pInfo->arrayAudio [ i ] = NULL;
	  }
	  else { // And finally we can cfreate the new entry and shift around ...
	    pEntry->arrayAudioEntries [ j ].iAudioNumber = j;
	    pEntry->arrayAudioEntries [ j ].qsIso639 = pAudio->qsIso639;
	    pAudio->iAudioNumber = j;
	    arrayTarget[ j ] = pAudio;
	    // store in a temp buffer as pInfo-arrayAudio[j] might hold some other entry
	    pInfo->arrayAudio [ i ] = NULL;
	  }
	}
      }
    }
    // And if the buffer holds anything then copy it over
    for ( i=0; i<MAX_AUDIO; i++ ) {
      if ( arrayTarget[i] ) 
	pInfo->arrayAudio[i] = arrayTarget[i];
    }
  }
}

QDragObject *ListViewMedia::dragObject()
{
	ListViewItemMedia *pCurrentItem     = (ListViewItemMedia *)currentItem ();
	SourceFileEntry   *pSourceFileEntry = NULL;
	SourceFileInfo    *pSourceFileInfo  = NULL;
	if (!pCurrentItem)
		return NULL;

	QImage  theImage;
	QPixmap thePixmap;
	QSize sizeThumbnail;
	bool bResetSourceFileInfo=false;

	// Check if we drag the header (SourceFileEntry)
	pSourceFileEntry = pCurrentItem->sourceFileEntry();
	if (pSourceFileEntry)	{
		pSourceFileInfo = pSourceFileEntry->listFileInfos[0];
		sizeThumbnail   = pSourceFileEntry->sizeThumbnail;
		bResetSourceFileInfo=true;
	}
	else	{	// Or if we drag the child (SourceFileInfo)
		pSourceFileInfo = pCurrentItem->sourceFileInfo ();
		// Quick sanity check.
		if (pSourceFileInfo)	{
			if (pCurrentItem->pixmap(0))
				sizeThumbnail   = pCurrentItem->pixmap(0)->size();
			// Okay we also need the SourceFileEntry to this ...
			pCurrentItem = (ListViewItemMedia *)pCurrentItem->parent();
			pSourceFileEntry = pCurrentItem->sourceFileEntry();
		}
	}
	// error. We should never get here
	if (!pSourceFileInfo)
		return NULL;

	m_pDraggingSourceFileEntry = pSourceFileEntry;
	m_pDraggingSourceFileInfo  = pSourceFileInfo;
	if (bResetSourceFileInfo)	// NULL is a sign for the receiver (MenuPreview) that we dragged the whole thing ...
		m_pDraggingSourceFileInfo = NULL;

	theImage  = QImage  (*pSourceFileInfo->pPreview);
	thePixmap = QPixmap ( theImage.smoothScale (sizeThumbnail, QImage::ScaleMin) );

	QDragObject *pDragObject = new QImageDrag (theImage, this);
	pDragObject->setPixmap (thePixmap);
//	pDragObject->drag();
//	pDragObject->dragMove();
//	pDragObject->dragCopy();
	return pDragObject;
}

void ListViewMedia::slotItemClicked (QListViewItem *pItem, const QPoint &thePoint, int)
{
	if (!pItem)
		return;
	ListViewItemMedia *pMediaItem = (ListViewItemMedia *)pItem;
	pMediaItem->itemClicked((QPoint &)thePoint);
}

