#include "v4lwindow.h"
#include "v4lxif.h"
#include "vidcapreg.h"
#include "vidconf.h"
#include "picprop.h"
#include "avicapwnd.h"
#include "ccap.h"
#include <utils.h>
#include <qpopupmenu.h>
#include <qmessagebox.h>
#include <qpoint.h>
#include <qfiledialog.h>
#include <qlabel.h>
#include <qwindowdefs.h>
#include <X11/Xlib.h>
#include <image.h>
#include <assert.h>

static inline void default_dimensions(int& w, int& h)
{
    int norm=RI("ColorMode", 0);
    w=(norm==1)?320:384;
    h=(norm==1)?240:288;
}

void V4LWindow::force4x3()
{
    hidePopup();
    m_bForce4x3=!m_bForce4x3;
    if(m_bForce4x3)
	resize(width(), height());
}

void V4LWindow::setupDevice()
{
    if(!m_pDev)
	return;
//    int field_width, field_height;
    int norm=RI("ColorMode", 0);
//    default_dimensions(field_width, field_height);
//    v4l->setCapAClip(102,110,field_width,field_height);
//    v4l->addCapAClip(0,248,384,288);
//    v4l->applyCapAClip(0);
    m_pDev->setChannel(RI("Channel", 1));
    m_pDev->setChannelNorm(norm);

    m_pDev->setAudioMute(0);
    m_pDev->setAudioVolume(65535);//?
    m_pDev->setAudioBalance(100);
    m_pDev->setAudioMode(VIDEO_SOUND_MONO);
    
    m_pDev->setPicBrightness(RI("Image\\Brighness", 0));
    m_pDev->setPicConstrast(RI("Image\\Contrast", 255));
    m_pDev->setPicColor(RI("Image\\Color", 255));
    m_pDev->setPicHue(RI("Image\\Hue", 0));
}

V4LWindow::V4LWindow(v4lxif * pDev, QWidget * pParent=0)
    : QWidget(pParent, 0, 0), m_pDev(pDev), m_pPopup(0), m_pPicConfig(0),
    m_pCapDialog(0), m_pCC(0), m_bForce4x3(true), m_bSubtitles(false),
    known_pos(false), m_pLabel(0), visibility(VisibilityFullyObscured),
    oc_count(0)
{
    assert(pDev);
    pDev->setCapture(0);
    setupDevice();
    int w, h;
    default_dimensions(w, h);
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(timerStop()));
    connect(&m_cctimer, SIGNAL(timeout()), this, SLOT(updatesub()));
    resize(w, h);
}

void V4LWindow::timerStop()
{
    m_pDev->setCapture(1);
}

void V4LWindow::moveEvent( QMoveEvent * e)
{
    if(!isVisible())
	return;
//    static int state=0;
//    if(state==0){state=1;return;}
    m_pDev->setCapture(0);
    int norm=RI("ColorMode", 0);
//    QPoint pnt=mapToGlobal(QPoint(0,0));
//    int field_width, field_height;
//    default_dimensions(field_width, field_height);
//    m_pDev->setCapAClip(pnt.x()+(384-field_width)/2, pnt.y()+(288-field_height)/2, 
//	field_width, field_height);
//    m_pDev->setCapAClip(pnt.x(), pnt.y(), width(), height());
    setupPicDimensions();
    m_pDev->applyCapAClip(0);
    if(m_timer.isActive())
	m_timer.stop();
    m_timer.start(100, TRUE);
//    avi_usleep(100000);
//    if(!known_pos)
//	m_pDev->setCapture(1);
    known_pos=1;
    if(m_bAutoRecord)
    {
	captureAVI();
	m_bAutoRecord=false;
    }
}
void V4LWindow::mouseReleaseEvent( QMouseEvent * e)
{
    if(!(e->button() & Qt::RightButton))
	return;
    if (m_pPopup)
	return;
    m_pPopup = new QPopupMenu;
    int norm=RI("ColorMode", 0);
    m_pPopup->insertItem("Configure", this, SLOT(config()));
    m_pPopup->insertItem("Picture properties", this, SLOT(picprop()));
    m_pPopup->insertItem("Capture AVI", this, SLOT(captureAVI()));
    m_pPopup->insertItem("Capture BMP", this, SLOT(captureBMP()));
//    m_pPopup->insertItem("Capture JPG", this, SLOT(captureJPG()));
    m_pPopup->insertSeparator();
    int iItem=m_pPopup->insertItem("Force 4x3 ratio", this, SLOT(force4x3()));
    m_pPopup->setCheckable(true);
    m_pPopup->setItemChecked(iItem, m_bForce4x3);
    if(((norm==1) || (norm==3)) && m_pDev->hasvbi())//NTSC or AUTO 
    {
	iItem=m_pPopup->insertItem("Show subtitles", this, SLOT(showsub()));
	m_pPopup->setCheckable(true);
        m_pPopup->setItemChecked(iItem, m_bSubtitles);
    }
//    m_pPopup->setChecked(m_bForce4x3);
    m_pPopup->insertSeparator();
    m_pPopup->insertItem("Quit", this, SLOT(close()));
    connect(m_pPopup, SIGNAL(aboutToHide()), this, SLOT(hidePopup()));
//	popup->insertSeparator();
//	popup->insertItem("Fullscreen", this, SLOT(fullscreen()));
#ifdef HAVE_LIBXXF86VM
//	popup->insertItem("Maximize", this, SLOT(maximize()));
#endif
	//#endif
    Window root_return, child_return;
    int root_x_return, root_y_return;
    int win_x_return, win_y_return;
    unsigned int mask_return;

    XQueryPointer(x11Display(), handle(), &root_return, &child_return,
		  &root_x_return, &root_y_return,
		  &win_x_return, &win_y_return, &mask_return);

//    m_pDev->setCapture(0);
    m_pPopup->popup(QPoint(root_x_return, root_y_return));
}

void V4LWindow::hidePopup()
{
    m_pPopup=0;
//    m_pDev->setCapture(1);
}

void V4LWindow::config()
{
    m_pPopup=0;
    m_pDev->setCapture(0);
    if(m_timer.isActive())
	m_timer.stop();
    m_timer.start(100, TRUE);
	
    VidConfig conf(this, m_pDev);
    conf.exec();
    if(!m_pDev)
	return;
//    m_pDev->setCapture(1);
//    QPoint pnt=mapToGlobal(QPoint(0,0));
//    m_pDev->setCapAClip(pnt.x(), pnt.y(), width(), height());
    setupPicDimensions();
//    m_pDev->applyCapAClip(0);
    setupDevice();
    get_clips();
    m_pDev->setCapture(1);
}

void V4LWindow::setupPicDimensions(int* pdx=0, int* pdy=0)
{
    QPoint pnt=mapToGlobal(QPoint(0,0));
    int dxpos, dypos, pw, ph;
    int max_w, max_h;
    pw=width();
    ph=height();
    if(m_bSubtitles)
	ph-=80;
    if(ph<=0)
    {
	printf("Oops: ph<0\n");
	return;
    }
    default_dimensions(max_w, max_h);
    max_w*=2;
    max_h*=2;
    dxpos=dypos=0;
    if(pw>max_w)
    {
	dxpos=(pw-max_w)/2;
	pw=max_w;
    }
    if(ph>max_h)
    {
	dypos=(ph-max_h)/2;
	ph=max_h;
    }
    m_pDev->setCapAClip(pnt.x()+dxpos, pnt.y()+dypos, pw, ph);
    if(pdx)*pdx=dxpos;
    if(pdy)*pdy=dypos;
}


void V4LWindow::resizeEvent( QResizeEvent * e)
{
//    printf("resizeEvent: %d,%d -> %d,%d ( %d,%d )\n",
//	e->oldSize().width(), e->oldSize().height(), 
//	e->size().width(), e->size().height(), 
//	width(), height()); 
    if(!isVisible())
    {
//	printf("!visible\n");
	return;
    }
    if(!known_pos)
    {
//	printf("!known\n");
	return;
    }
    int w=e->size().width();
    int h=e->size().height();
    if(m_bForce4x3 && w!=4*(h-(m_bSubtitles?80:0))/3)
    {
//	printf("Recalc %d,%d\n", 4*(h-(m_bSubtitles?80:0))/3, h);
	resize(4*(h-(m_bSubtitles?80:0))/3, h);
	return;
    }
    if(m_pLabel)
    {
	m_pLabel->move(0, height()-80);
	m_pLabel->resize(width(), 80);
    }
    m_pDev->setCapture(0);
    setupPicDimensions();
    m_pDev->applyCapAClip(0);
//    m_pDev->setCapture(1);
    if(m_timer.isActive())
	m_timer.stop();
    m_timer.start(100, TRUE);
}

void V4LWindow::showEvent( QShowEvent * )
{
    if(!known_pos)return;
    if(!m_pDev)
	return;
//    avi_usleep(100000);
//    QPoint pnt=mapToGlobal(QPoint(0,0));
//    m_pDev->setCapAClip(pnt.x(), pnt.y(), width(), height());
    setupPicDimensions();
    m_pDev->applyCapAClip(0);
    m_pDev->setCapture(1);
    get_clips();
}
void V4LWindow::hideEvent( QHideEvent * )
{
//    printf("hideEvent()\n");
    if(!m_pDev)
	return;
    m_pDev->setCapture(0);
    avi_usleep(100000);
    get_clips();
}
void V4LWindow::focusInEvent( QFocusEvent * )
{
//    if(!known_pos)return;
//    m_pDev->setCapture(1);
//    avi_usleep(100000);
    get_clips();
}
void V4LWindow::focusOutEvent( QFocusEvent * )
{
//    printf("focusOutEvent()\n");
    m_pDev->setCapture(0);
//    avi_usleep(100000);
    get_clips();
    if(m_timer.isActive())
	m_timer.stop();
    m_timer.start(100, TRUE);
}

void V4LWindow::enterEvent( QEvent * )
{
//    get_clips();
}

void V4LWindow::leaveEvent( QEvent * )
{
//    printf("leaveEvent()\n");
//    get_clips();
}

void V4LWindow::captureAVI()
{
    if(m_pCapDialog)
	return;
    m_pCapDialog=new AviCapDialog(this);
//    QPoint pnt=mapToGlobal(QPoint(0,0));
//    m_pCapDialog->move(pnt.x(), pnt.y()+height()+40);
    m_pDev->setCapture(0);
    if(m_timer.isActive())
	m_timer.stop();
    m_timer.start(100, TRUE);
    m_pCapDialog->show();
    if(m_bAutoRecord)
	m_pCapDialog->start();
//    m_pDev->setCapture(0);
//    QMessageBox::information(this, "Sorry", "Not implemented yet!");
//    m_pDev->setCapture(1);
}

void V4LWindow::captureBMP()
{
//    int w=width();
//    int h=height();
    unsigned int w, h;
    int x, y;
    m_pDev->getCapAClip(&x, &y, &w, &h);
    w &= ~3;
    m_pDev->grabSetParams(w, h, VIDEO_PALETTE_RGB24);
    unsigned char* cp=(unsigned char*)m_pDev->grabCapture( true );
    CImage* im=new CImage(cp, w, -h);

    m_pDev->setCapture(0);
    if(m_timer.isActive())
	m_timer.stop();
    m_timer.start(100, TRUE);
    QString qs=QFileDialog::getSaveFileName(QString::null, "*.bmp", this, "Save as BMP file");
    if(!qs.isNull())
	im->Dump(qs);
    delete im;
}

void V4LWindow::captureJPG()
{
//    m_pDev->setCapture(0);
    QMessageBox::information(this, "Sorry", "Not implemented yet!");
//    m_pDev->setCapture(1);
}

void V4LWindow::keyPressEvent( QKeyEvent * e )
{
    switch(e->ascii())
    {
    case 'b':
    case 'B':
	captureBMP();
	break;
    case 'c':
    case 'C':
	config();
	break;
    case 'q':
    case 'Q':
	close();
	break;
    }
}

void V4LWindow::showsub()
{
    hidePopup();
    m_bSubtitles=!m_bSubtitles;
    if(m_bSubtitles)
    {
	m_pCC=new ClosedCaption(m_pDev);
	m_pLabel=new QLabel(this);
	int w=width();
	int h=height();
	resize(w, h+80);
	m_pLabel->move(0, h);
	m_pLabel->resize(w, 80);
	m_pLabel->setAlignment(Qt::AlignCenter);
	m_pLabel->show();
	m_cctimer.start(500);
    }
    else
    {
	m_cctimer.stop();	
	delete m_pCC;
	delete m_pLabel;
	int w=width();
	int h=height();
	resize(w, h-80);
	m_pCC=0;
    }
}

void V4LWindow::picprop()
{
//    if(m_pPicConfig)
//	return;
    if(!m_pDev)
	return;
    m_pDev->setCapture(0);
    if(m_timer.isActive())
	m_timer.stop();
    m_timer.start(100, TRUE);
    m_pPicConfig=new PicturePropDialog(m_pDev);
    QPoint pnt=mapToGlobal(QPoint(0,0));
    m_pPicConfig->move(pnt.x(), pnt.y()+height()+40);
    m_pPicConfig->show();
}
static int             x11_error = 0;
typedef int (*handlerproc) (Display*, XErrorEvent*);
static int x11_error_dev_null(Display * dpy, XErrorEvent * event)
{
    x11_error++;
//    if (debug > 1)
//	fprintf(stderr," x11-error\n");
    return 0;
}

void V4LWindow::add_clip(int x1, int y1, int x2, int y2)
{
    if(oc_count >= (int) (sizeof(oc)/sizeof(oc[0])))
	return;
    if (oc[oc_count].x1 != x1 || oc[oc_count].y1 != y1 ||
	oc[oc_count].x2 != x2 || oc[oc_count].y2 != y2) {
	conf = 1;
    }
    oc[oc_count].x1 = x1;
    oc[oc_count].y1 = y1;
    oc[oc_count].x2 = x2;
    oc[oc_count].y2 = y2;
    oc_count++;
} 

bool V4LWindow::x11Event( XEvent * e)
{
/*
    switch(e->type)
    {
    case VisibilityNotify:
	printf("VisibilityNotify\n");
        if (e->xvisibility.window == handle()) 
        {
	    visibility = e->xvisibility.state;
    //	    get_clips();
	}
	break;
    case MapNotify:
	printf("MapNotify\n");
        wmap=1;
        break;
    case UnmapNotify:
	printf("UnmapNotify\n");
        wmap=0;
        break;
    case KeyPress:
    case KeyRelease:
    case ButtonPress:
    case ButtonRelease:
    case MotionNotify:
    case EnterNotify:
    case LeaveNotify:
    case FocusIn:
    case FocusOut:
    case KeymapNotify:
    case Expose	:
    case GraphicsExpose:
    case NoExpose	:
    case CreateNotify:
    case DestroyNotify:
    case MapRequest:	
    case ReparentNotify:
    case ConfigureNotify:
    case ConfigureRequest:
    case GravityNotify:
    case ResizeRequest:
    case CirculateNotify:
    case CirculateRequest:
    case PropertyNotify:
    case SelectionClear:
    case SelectionRequest:
    case SelectionNotify:
    case ColormapNotify:
    case ClientMessage:
    case MappingNotify:
//    printf("Event %d\n", e->type);
    break;
    }
    get_clips();
*/
    return QWidget::x11Event(e);
}

void V4LWindow::get_clips()
{
    int x1,y1,x2,y2,lastcount;
    XWindowAttributes wts;
    Window root, me, rroot, parent, *children;
    uint nchildren, i;
    QPoint pnt=mapToGlobal(QPoint(0,0));
    int wx, wy;
    wx=pnt.x();
    wy=pnt.y();
    conf=0;
    Display * dpy=x11Display();
    root=DefaultRootWindow(dpy);
    me = (Window)handle();
    m_pDev->setCapture(0);
    XSelectInput(dpy, me, ~0);
    XSelectInput(dpy, root, VisibilityChangeMask | ExposureMask);
    /*
    XGetWindowAttributes(dpy, me, &wts);
    wts.your_event_mask=~0;
    */
//    XSetWindowAttributes xsw;
//    xsw.event_mask=~0;
//    XChangeWindowAttributes(dpy, me, CWEventMask, &xsw);
//    GC gc=qt_xget_readonly_gc(false);
//    XSetGraphicsExposures( dpy, gc, TRUE );
//    XGCValues xcg;
//    xcg.graphics_exposures=true;
//    gc=XDefaultGC(dpy, me, GCGraphicsExposures, &xcg);

/*
    if (wmap 
	&& visibility != VisibilityFullyObscured 
	&& visibility != VisibilityPartiallyObscured)
    {
        oc_count = 0;
        return;
    }
*/    
    handlerproc old_handler = XSetErrorHandler(x11_error_dev_null);

//    if (debug > 1)
//	fprintf(stderr," getclips");
    lastcount = oc_count;
    oc_count = 0;

    if (wx<0)
	add_clip(0, 0, (uint)(-wx), height());
    if (wy<0)
	add_clip(0, 0, width(), (uint)(-wy));
//    if ((wx+width()) > swidth)
//	add_clip(swidth-wx, 0, width(), height());
//    if ((wy+height()) > sheight)
//	add_clip(0, sheight-wy, width(), height());
    
    for (;;) {
	XQueryTree(dpy, me, &rroot, &parent, &children, &nchildren);
	XFree((char *) children);
	/* fprintf(stderr,"me=0x%x, parent=0x%x\n",me,parent); */
	if (root == parent)
	    break;
	me = parent;
    }
    XQueryTree(dpy, root, &rroot, &parent, &children, &nchildren);

//    printf("%d children to check\n", nchildren);
    for (i = 0; i < nchildren; i++)
	if (children[i]==me)
	    break;
//    printf("Starting with %d\n", i);
    for (i++; i<nchildren; i++) {
	XGetWindowAttributes(dpy, children[i], &wts);
	if (!(wts.map_state & IsViewable))
	{
//	    printf("child %d, (%d,%d)x(%d,%d): skipping\n",
//		i, wts.x, wts.y, wts.width, wts.height);
	    continue;
	}	
	x1=wts.x-wx;
	y1=wts.y-wy;
	x2=x1+wts.width+2*wts.border_width;
	y2=y1+wts.height+2*wts.border_width;
	if ((x2 < 0) || (x1 > (int)width()) ||
	    (y2 < 0) || (y1 > (int)height()))
	{
//	    printf("child %d, (%d,%d)x(%d,%d): not overlapping\n",
//		i, wts.x, wts.y, wts.width, wts.height);
	    continue;
	}
	if (x1<0)      	         x1=0;
	if (y1<0)                y1=0;
	if (x2>(int)width())  x2=width();
	if (y2>(int)height()) y2=height();
	add_clip(x1, y1, x2, y2);
    }
    XFree((char *) children);

    if (lastcount != oc_count)
	conf=1;
	
    if(conf)
    {
	m_pDev->resetCapAClip();
	int dx, dy;
	setupPicDimensions(&dx, &dy);
	for(int i=0; i<oc_count; i++)
	{
//	    printf("Adding clip (%d,%d)x(%d,%d)\n",
//		oc[i].x1, oc[i].y1, oc[i].x2, oc[i].y2);
	    m_pDev->addCapAClip(-dx+oc[i].x1, -dy+oc[i].y1, -dx+oc[i].x2, -dy+oc[i].y2);
	}
//	printf("Applying %d clips\n", oc_count);
	m_pDev->applyCapAClip(oc_count);
        m_pDev->setCapture(1);

//	if(m_timer.isActive())
//	    m_timer.stop();
//	m_timer.start(100, TRUE);
    }
    else
        m_pDev->setCapture(1);
    
    XSetErrorHandler(old_handler);
}

void V4LWindow::updatesub()
{
//    printf("updatesub()\n");
    if(!m_pCC)
	return;
    m_pCC->lock();
    char buf[256];
    strcpy(buf, m_pCC->getBuffer());
    m_pCC->unlock();
//    printf("%s\n", buf);
    assert(m_pLabel);
    assert(m_bSubtitles);
    m_pLabel->setText(buf);
}

#include "v4lwindow.moc"
