/*
 *
 * Copyright (C) 2004 Dirk Ziegelmeier <dziegel@gmx.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include <kapplication.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <qwidget.h>
#include <qtimer.h>
#include <qevent.h>

#include <klocale.h>
#include <kdebug.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include "overlaycontroller.h"

OverlayController::OverlayController(QWidget *w)
    : QWidget(w, "OverlayController"),
      _filterRefresh(false),
      _visibility(VisibilityUnobscured),
      _w(w)
{
    _repaintTimer = new QTimer();
    connect(_repaintTimer, SIGNAL( timeout() ),
            this, SLOT( doRepaintScreen() ));

    _reclipTimer = new QTimer();
    connect(_reclipTimer, SIGNAL( timeout() ),
            this, SLOT( doReclip() ));

    _top  = topLevelWidget();

    int scn = QApplication::desktop()->screenNumber(_w);
    _root   = QApplication::desktop()->screen(scn);

    // tv window -- need visibility, structure
    XSelectInput(qt_xdisplay(),
                 _w->winId(),
                 ButtonPressMask      |
                 ButtonReleaseMask    |
                 ExposureMask         |
                 PointerMotionMask    |
                 VisibilityChangeMask |
                 StructureNotifyMask  |
                 SubstructureNotifyMask);

    // root window -- need visibility, sub-, structure
    XSelectInput(qt_xdisplay(),
                 _root->winId(),
                 VisibilityChangeMask |
                 FocusChangeMask      |
                 StructureNotifyMask  |
                 SubstructureNotifyMask);

    kapp->installX11EventFilter(this);
    _w->installEventFilter(this);
    _top->installEventFilter(this);

    scheduleRepaintScreen();
}

OverlayController::~OverlayController()
{
    delete _repaintTimer;
    delete _reclipTimer;
    emit enableVideo(false);
    doRepaintScreen();
}

bool OverlayController::eventFilter(QObject* obj, QEvent* e)
{
    //    kdDebug() << "event: " << e->type() << " for: " << obj->name() << endl;

    // ---------------------------------- Shell and view widget
    if(e->type() == QEvent::Move) {
        // The main window or the widget has moved
        kdDebug() << "Overlay: View moved" << endl;
        emit moved();
        scheduleRepaintScreen();

    } else if(e->type() == QEvent::Show) {
        // Show main window or tv widget
        kdDebug() << "Overlay: View shown" << endl;
        emit enableVideo(true);
        scheduleRepaintScreen();

    } else if(e->type() == QEvent::Hide) {
        // Hide main window or tv widget
        kdDebug() << "Overlay: View hidden" << endl;
        emit enableVideo(false);
        scheduleRepaintScreen();
    }

    // ---------------------------------- View widget only
    if (obj == _w) {
        if(e->type() == QEvent::Resize) {
            // The widget has changed its size
            // and maybe as a consequence its position (-> aspect ratio)
            //            kdDebug() << "Overlay: View resized" << endl;
            emit resized();
            emit moved();
            scheduleRepaintScreen();

        } else if(e->type() == QEvent::Paint) {
            // The widget is being unobscured (unmap, expose)
            //            kdDebug() << "Overlay: Paint (expose)" << endl;
            scheduleReclip();
        }
    }

    return false;
}

void OverlayController::scheduleRepaintScreen()
{
    //    kdDebug() << "Overlay: Schedule Repaint" << endl;

    _filterRefresh = true;
    _repaintTimer->start(0, true);
}

void OverlayController::scheduleReclip()
{
    //    kdDebug() << "Overlay: Schedule Reclip" << endl;

    _reclipTimer->start(0, true);
}

void OverlayController::doRepaintScreen()
{
    //    kdDebug() << "Overlay: Repaint Screen" << endl;

    _filterRefresh = true;
    emit updateClipping();
    emit repaintScreen();
}

void OverlayController::doReclip()
{
    //    kdDebug() << "Overlay: Reclip" << endl;

    emit updateClipping();
}

bool OverlayController::x11Event(XEvent* e)
{
    /*
    switch (e->type) {
    case Expose:
        kdDebug() << "Expose" << endl;
        break;
    case GraphicsExpose:
        kdDebug() << "Graphics expose" << endl;
        break;
    case VisibilityNotify:
        switch (e->xvisibility.state) {
        case VisibilityFullyObscured:
            kdDebug() << "Visibility obscured" << endl;
            break;
        case VisibilityPartiallyObscured:
            kdDebug() << "Visibility" << endl;
            break;
        case VisibilityUnobscured:
            kdDebug() << "Visibility unobscured" << endl;
            break;
        default:
            break;
        }
    break;
    case ConfigureNotify:
        kdDebug() << "Configure notify" << endl;
        break;
    case ConfigureRequest:
        kdDebug() << "Configure request notify" << endl;
        break;
    case MapNotify:
        kdDebug() << "Map notify" << endl;
        break;
    case UnmapNotify:
        kdDebug() << "Unmap notify" << endl;
        break;
    case MappingNotify:
        kdDebug() << "Mapping notify" << endl;
        break;
    case PropertyNotify:
        kdDebug() << "Property notify" << endl;
        break;
    case EnterNotify:
        kdDebug() << "Enter notify" << endl;
        break;
    case LeaveNotify:
        kdDebug() << "Leave notify" << endl;
        break;
    default:
        kdDebug() << "UNINTERESTING X11 Event: " << e->type << " for window: " << e->xany.window << endl;
        return false;
    }
    if (e->xany.window == _w->winId()) {
        kdDebug() << "X11 Event for TV widget" << endl;
    } else if (e->xany.window == _root->winId()) {
        kdDebug() << "X11 Event for desktop" << endl;
    } else {
        kdDebug() << "X11 Event for unknown window: " << e->xany.window << endl;
    }

    kdDebug() << "Will filter: " << _filterRefresh << endl;
    */

    // --------------------------------------- Events for the TV widget
    if ( e->xany.window == _w->winId() ) {
        if (e->type == VisibilityNotify) {
            _visibility = e->xvisibility.state;
            if (!_filterRefresh) {
                scheduleRepaintScreen();
            } else if (_visibility != VisibilityFullyObscured) {
                _filterRefresh = false;
            }
        } else if (e->type == MapNotify) {
            // A child widget is shown, reclip immediately
            emit updateClipping();
        } else if (e->type == ConfigureNotify) {
            // A child widget changes size
            emit updateClipping();
            scheduleRepaintScreen();
        }

    // --------------------------------------- Events for the desktop
    } else if ( e->xany.window == _root->winId() ) {
        if (e->type == ConfigureNotify) {
            // A window is moved into the TV widget and obscures it some more
            if (!_filterRefresh && 
                (_visibility == VisibilityPartiallyObscured)) {
                scheduleRepaintScreen();
            }
        }
    }

    return false;
}

#include "overlaycontroller.moc"
