// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information

#define CODEINE_DEBUG_PREFIX "videoWindow"


#include <cstdlib>
#include <tqapplication.h> //sendEvent()
#include <tqvariant.h>
#include <tqcursor.h>
#include <tqevent.h>
#include <X11/Xlib.h>     //TODO this breaks compile for lots of people due to excessive macro content

#include "../debug.h"
#include "videoWindow.h"


namespace Codeine {


VideoWindow *VideoWindow::s_instance = nullptr;


namespace X
{
   Display *d;
   int s, w;
}


VideoWindow::VideoWindow( TQWidget *parent, const char *name )
      : TQWidget( parent, name )
      , m_osd( nullptr )
      , m_stream( nullptr )
      , m_eventQueue( nullptr )
      , m_videoPort( nullptr )
      , m_audioPort( nullptr )
      , m_xine( nullptr )
      , m_displayRatio( 1 )
{
   s_instance = this;

   // with this Konqueror would crash on exit
   // without this we may be unstable!
   //XInitThreads();

   show();

   setWFlags( TQt::WNoAutoErase );
   setMouseTracking( true );
   setAcceptDrops( true );
   setUpdatesEnabled( false ); //to stop TQt drawing over us
   setPaletteBackgroundColor( TQt::black );

   X::d = XOpenDisplay( std::getenv("DISPLAY") );
   X::s = DefaultScreen( X::d );
   X::w = winId();

   XLockDisplay( X::d );
   XSelectInput( X::d, X::w, ExposureMask );

   {
      using X::d; using X::s;

      //these are Xlib macros
      double w = DisplayWidth( d, s ) * 1000 / DisplayWidthMM( d, s );
      double h = DisplayHeight( d, s ) * 1000 / DisplayHeightMM( d, s );

      m_displayRatio = w / h;
   }

   XUnlockDisplay( X::d );

   connect( &m_timer, TQ_SIGNAL(timeout()), TQ_SLOT(hideCursor()) );
}

VideoWindow::~VideoWindow()
{
   DEBUG_BLOCK

   if( m_osd )        xine_osd_free( m_osd );
   if( m_stream )     xine_close( m_stream );
   if( m_eventQueue ) xine_event_dispose_queue( m_eventQueue );
   if( m_stream )     xine_dispose( m_stream );
   if( m_videoPort )  xine_close_video_driver( m_xine, m_videoPort );
   if( m_audioPort )  xine_close_audio_driver( m_xine, m_audioPort );
   if( m_xine )       xine_exit( m_xine );

   XCloseDisplay( X::d );
}

void*
VideoWindow::x11Visual() const
{
   x11_visual_t* visual = new x11_visual_t;

   visual->display          = X::d;
   visual->screen           = X::s;
   visual->d                = X::w;
   visual->dest_size_cb     = &VideoWindow::destSizeCallBack;
   visual->frame_output_cb  = &VideoWindow::frameOutputCallBack;
   visual->user_data        = (void*)this;

   return visual;
}

void
VideoWindow::destSizeCallBack(
      void* p, int /*video_width*/, int /*video_height*/,
      double /*video_aspect*/, int* dest_width,
      int* dest_height, double* dest_aspect )
{
   if( !p )
      return;

   #define vw static_cast<VideoWindow*>(p)

   *dest_width  = vw->width();
   *dest_height = vw->height();
   *dest_aspect = vw->m_displayRatio;
}

void
VideoWindow::frameOutputCallBack(
      void* p, int video_width, int video_height, double video_aspect,
      int* dest_x, int* dest_y, int* dest_width, int* dest_height,
      double* dest_aspect, int* win_x, int* win_y )
{
   if( !p )
      return;

   *dest_x = 0;
   *dest_y = 0 ;
   *dest_width  = vw->width();
   *dest_height = vw->height();
   *win_x = vw->x();
   *win_y = vw->y();
   *dest_aspect = vw->m_displayRatio;

   // correct size with video aspect
   // TODO what's this about?
   if( video_aspect >= vw->m_displayRatio )
      video_width  = int( double(video_width * video_aspect / vw->m_displayRatio + 0.5) );
   else
      video_height = int( double(video_height * vw->m_displayRatio / video_aspect) + 0.5 );

   #undef vw
}

bool
VideoWindow::event( TQEvent *e )
{
   switch( e->type() )
   {
   case TQEvent::MouseMove:
   case TQEvent::MouseButtonPress:
      unsetCursor();
      m_timer.start( CURSOR_HIDE_TIMEOUT, true );
      break;

   case TQEvent::Close:
   case TQEvent::Hide:
      xine_stop( m_stream );
      break;

   case TQEvent::Leave:
      m_timer.stop();
      break;

   default:
      ;
   }

   return TQWidget::event( e );
}

bool
VideoWindow::x11Event( XEvent *e )
{
   if( e->type == Expose && e->xexpose.count == 0 ) {
      xine_port_send_gui_data( m_videoPort, XINE_GUI_SEND_EXPOSE_EVENT, e );
      return true;
   }

   return false;
}

void
VideoWindow::hideCursor()
{
   setCursor( TQt::BlankCursor );
}

} //namespace Codeine
