//
//  The contents of this file are subject to the Mozilla Public License
//  Version 1.0 (the "License"); you may not use this file except in
//  compliance with the License. You may obtain a copy of the License at
//  http://www.mozilla.org/MPL/
// 
//  Software distributed under the License is distributed on an "AS IS"
//  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
//  the License for the specific language governing rights and limitations
//  under the License.
// 
//  The Original Code is CPhone, a cross platform voip gui.
//
//  The Initial Developer of the Original Code is Derek Smithies.
//
//  Copyright (C) 2002 Indranet Technologies Ltd, 
//                                  http://www.indranet-technologies.com
//  All Rights Reserved.
//
//  Contributor(s): _______________


/*
 *
 *  $Log: vdisplay.cxx,v $
 *  Revision 1.6  2004/04/23 05:09:59  dereksmithies
 *  Fixes so it works with latest openh323 lib, and not lock X display.
 *
 *  Revision 1.5  2003/08/11 01:48:14  dereksmithies
 *  Fix windows compile errors
 *
 *  Revision 1.4  2003/08/06 05:04:25  dereksmithies
 *  Add feature to display bmp
 *
 *  Revision 1.3  2003/05/09 03:08:24  dereksmithies
 *  Fix Microtelco handling, and message display
 *
 *  Revision 1.2  2003/04/04 04:37:49  dereksmithies
 *  Major upgrade.
 *  Ixj & microtelco support added. Fix threading issues.
 *
 *  Revision 1.1.1.1  2002/05/12 22:55:05  dereksmithies
 *  Initial release.
 *
 *
 *
 *
 */

#include <ptlib.h>
#include <qdatetime.h>
#include "vdisplay.h"
#include <sys/types.h>
#include <unistd.h>
#include <qpainter.h>
#include <qevent.h>
#include <qapplication.h>

LabelDisplay::LabelDisplay( QWidget *parent, const char *name, WFlags f)
  : QLabel( parent, name, f)
{
  displayText = "";
}

void LabelDisplay::prepareDisplayText(QString s)
{ 
     PWaitAndSignal m(displayMutex);

    displayText = s;
}

void LabelDisplay::customEvent(QCustomEvent *e)
{
    PWaitAndSignal m(displayMutex);

    setText(displayText);
}

////////////////////////////////////////////////////////////////////////

VideoDisplay::VideoDisplay( QWidget *parent, const char *name, WFlags f)
    : QFrame(parent, name, f )
{
  blankImage    = NULL;
  incomingImage = NULL;
  displayImage  = NULL;
  bufferImage   = NULL;
  scaledImage   = NULL;

  displayFlipped = FALSE;
  newImageData   = FALSE;

  width =  0;
  height = 0;

  converter = NULL;
};

VideoDisplay::~VideoDisplay()
{
  PWaitAndSignal m(bufferMutex);

  if (bufferImage != NULL)
    delete bufferImage;
  bufferImage = NULL;

  if (displayImage != NULL)
    delete displayImage;
  displayImage = NULL;

  if (incomingImage != NULL)
    delete incomingImage;
  incomingImage = NULL;

  if (scaledImage != NULL)
    delete scaledImage;
  scaledImage = NULL;

  if (converter!=NULL)
    delete converter;
  converter = NULL;
}


void VideoDisplay::RemoveVideoImage()
{
  SwapImages( SWAP_BLANK );

  InitiateRepaint( TRUE );
}

void VideoDisplay::InitiateRepaint(BOOL doErase)
{
  QRect r = contentsRect();

  QThread::postEvent(this, new QPaintEvent(r, doErase) );
}

void VideoDisplay::CreateColourConverter()
{
  if (converter != NULL) {
    delete converter;
    converter = NULL;
  }

  if (converter == NULL)
    converter = PColourConverter::Create("YUV420P", "RGB32F", width, height);
}

void VideoDisplay::DisplayQImage(QImage *qImage)
{
  fixedImage = qImage;
  SwapImages(SWAP_FIXED);
  InitiateRepaint(FALSE );
}

//SetDisplayBuffer is only called by one thread. Thus, we do not need to
//protect incomingImage with a mutex.
void VideoDisplay::SetDisplayBuffer(const void *videoFrame, unsigned _width, unsigned _height, BOOL doFlip)
{
  width  = _width;
  height = _height;

  if (incomingImage == NULL) 
    incomingImage = new QImage(width, height, 32);

  if ((incomingImage->width() != width) || (incomingImage->height() != height) ) {  
    delete incomingImage;
    incomingImage = new QImage(width, height, 32);
    if (incomingImage == NULL)
      return;

    delete converter;    //There has been a size change. The colour converter
    converter = NULL;    //must be changed to the new size.
  }

  CreateColourConverter();

  if (converter == NULL)
    return;

  converter->SetVFlipState(doFlip);
 
  PINDEX bytesReturned;
  converter->Convert( (const BYTE *)videoFrame, incomingImage->bits(), &bytesReturned);

  SwapImages(SWAP_INCOMING );
  InitiateRepaint(FALSE );
}

void VideoDisplay::drawContents (QPainter *p)
{
  SwapImages ( SWAP_BUFFER );

  if (displayImage == NULL) 
    return;

  QRect r = contentsRect();

#if 1
  if (scaledImage != NULL) {
    if ((scaledImage->width() != r.width()) ||
	(scaledImage->height() != r.height())) {
      delete scaledImage;
      scaledImage = NULL;
    }
  }

  if (scaledImage == NULL)
    scaledImage = new QImage(r.width(), r.height(), 32);
    
  ResizeImage(scaledImage->bits(), r.width(), r.height(),
	      displayImage->bits(), displayImage->width(), displayImage->height());
  
  bitBlt(this, 0, 0, scaledImage);

#else
  QPixmap sourcePixmap;
  sourcePixmap.convertFromImage(*displayImage);

  QWMatrix m;                                    // transformation matrix
  double   sx,sy;
  sx = (double)r.width()/displayImage->width();  //get scale factors.
  sy = (double)r.height()/displayImage->height();
  m.scale(sx,sy);                                // scale coordinate system
  QPixmap sp = sourcePixmap.xForm( m );          // sp is the sourcepixmap after resizing.

  bitBlt(this, 0, 0, &sp);
#endif
}

void VideoDisplay::SwapImages(int actionToTake )
{
  PWaitAndSignal m(bufferMutex);

  if ((actionToTake == SWAP_BUFFER) && (newImageData == FALSE)) {
    return;
  }

  if (actionToTake == SWAP_BUFFER)
    newImageData = FALSE;
  else 
    newImageData = TRUE;
  

  QImage *temp;

  switch(actionToTake)
    {
    case SWAP_FIXED:
      temp         = fixedImage;
      fixedImage   = bufferImage;
      bufferImage  = temp;
      break;
    case SWAP_BUFFER:
      temp         = displayImage;
      displayImage = bufferImage;
      bufferImage  = temp;
      break;
    case SWAP_INCOMING:
      temp          = incomingImage;
      incomingImage = bufferImage;
      bufferImage   = temp;
      break;
    case SWAP_BLANK:
      temp          = blankImage;
      blankImage    = bufferImage;
      bufferImage   = temp;
      delete blankImage;
      blankImage = NULL;
      break;
    default:
      return;
    }
}
    
void VideoDisplay::ResizeImage(uchar *dest_data,   unsigned    dest_width,    unsigned dest_height,
			      uchar *source_data, unsigned    source_width,  unsigned source_height)
{
  // declare local variables
  unsigned source_x,source_y, dest_x,dest_y;
  unsigned source_yWidth,dest_yWidth;
  unsigned *dest_bitmap = (unsigned *)dest_data;
  unsigned *source_bitmap = (unsigned *)source_data;
	
  for(dest_y=0;dest_y<dest_height;dest_y++) {
    source_y      = dest_y   * source_height/dest_height;
    source_yWidth = source_y * source_width;
    dest_yWidth   = dest_y   * dest_width;
    
    for(dest_x=0;dest_x<dest_width; dest_x++) {
      source_x= dest_x*source_width/dest_width;	  
      
      dest_bitmap[dest_yWidth + dest_x] =
	source_bitmap[source_yWidth + source_x];
    }
  }
}

    
/////////////////////////////////////////////////
/////////////////////////////////////////////////
/////////////////////////////////////////////////
/////////////////////////////////////////////////
Rgb32Image::Rgb32Image(int x, int y, QString newMessage)
{
  image = NULL;
  image = new QImage(x,y,32);
  QPainter p;
  image->fill( 0xffeeeeee );

  frameWidth = x;
  frameHeight = y;
  textMessage = newMessage;

  QCustomEvent *ce = new QCustomEvent(QEvent::User);
  QApplication::postEvent(this, ce);  //Qt will delete "ce" when done.
}

Rgb32Image::~Rgb32Image()
{
  access.lock();

  if (image != NULL)
    delete image;

  access.unlock();
}

void Rgb32Image::CopyData(Q_UINT8 *dest)
{
  access.lock();

  if (image == NULL) 
    memset(dest, 64, frameWidth * frameHeight * 3);
  else
    memcpy(dest, image->bits(), frameWidth * frameHeight * 3);

  access.unlock();
  return ;
}

void Rgb32Image::MakeImage()
{
  access.lock();
  
  if (image != NULL)
    delete image;

  image = new QImage(frameWidth, frameHeight, 32);
  QPainter p;                          // our painter
  image->fill(0xffe0e0e0 ); //alpha r g b // initialize pixmap

  QPixmap pm;
  pm.convertFromImage(*image);

  p.begin(&pm );                           // start painting pixmap
  QFont    f( "Charter", 48 );              // use Charter 24pt font
  p.setFont(f );                           //   text rectangle

  p.setPen(QColor(10, 10, 210));

  QRect br;
  QFontMetrics fm(p.fontMetrics() );   // get widget font metrics
  br = fm.boundingRect(textMessage );   // rectangle covering text

  QPoint posn = (QPoint(frameWidth,frameHeight) - br.topRight()-br.bottomLeft() )/2;
  p.drawText(posn, textMessage);

  p.end();                            // painting done
    
  *image = pm.convertToImage();


  access.unlock();
}

BYTE *Rgb32Image::GetDataPointer() const
{ 
  return (BYTE *)image->bits();
}

bool Rgb32Image::event(QEvent *e)
{
  if (e->type() == QEvent::User) {
    MakeImage();
    return TRUE;
  }

  return FALSE;
}




syntax highlighted by Code2HTML, v. 0.9.1