/*
 * KSnapshot
 *
 * (c) Richard J. Moore 1997-2002
 * (c) Matthias Ettrich 2000
 * (c) Aaron J. Seigo 2002
 * (c) Nadeem Hasan 2003
 * This adaptation: (c) Boudewijn Rempt 2005 
 *
 * Released under the GPL see file LICENSE for details.
 */

#include <tdeapplication.h>
#include <tdelocale.h>
#include <kimageio.h>
#include <tdefiledialog.h>
#include <kimagefilepreview.h>
#include <tdemessagebox.h>
#include <kdebug.h>
#include <kprinter.h>
#include <tdeio/netaccess.h>
#include <ksavefile.h>
#include <tdetempfile.h>

#include <tqbitmap.h>
#include <tqdragobject.h>
#include <tqimage.h>
#include <tqclipboard.h>
#include <tqvbox.h>

#include <tdeaccel.h>
#include <knotifyclient.h>
#include <khelpmenu.h>
#include <tdepopupmenu.h>
#include <kpushbutton.h>
#include <tdestartupinfo.h>

#include <tqcursor.h>
#include <tqregexp.h>
#include <tqpainter.h>
#include <tqpaintdevicemetrics.h>
#include <tqwhatsthis.h>

#include <stdlib.h>

#include "ksnapshot.h"
#include "regiongrabber.h"
#include "ksnapshotwidget.h"

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

#include <config.h>

#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
#include <X11/extensions/shape.h>
#endif

#include <tdeglobal.h>

KSnapshot::KSnapshot(TQWidget *parent, const char *name)
    : super(parent, name, false, TQString(), Ok|Cancel)
{
    grabber = new TQWidget( 0, 0, WStyle_Customize | WX11BypassWM );
    TQ_CHECK_PTR(grabber);
    grabber->move( -1000, -1000 );
    grabber->installEventFilter( this );

#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
    int tmp1, tmp2;
    //Check whether the extension is available
    haveXShape = XShapeQueryExtension( tqt_xdisplay(), &tmp1, &tmp2 );
#endif

    TQVBox *vbox = makeVBoxMainWidget();
    mainWidget = new KSnapshotWidget( vbox, "mainWidget" );
    TQ_CHECK_PTR(mainWidget);

    mainWidget->btnSave->hide();
    mainWidget->btnPrint->hide();
    connect(mainWidget, TQ_SIGNAL(startImageDrag()), TQ_SLOT(slotDragSnapshot()));

    connect( mainWidget, TQ_SIGNAL( newClicked() ), TQ_SLOT( slotGrab() ) );
    connect( mainWidget, TQ_SIGNAL( printClicked() ), TQ_SLOT( slotPrint() ) );

    grabber->show();
    grabber->grabMouse( waitCursor );
    
    snapshot = TQPixmap::grabWindow( tqt_xrootwin() );
    updatePreview();
    grabber->releaseMouse();
    grabber->hide();

    TDEConfig *conf=TDEGlobal::config();
    conf->setGroup("GENERAL");
    mainWidget->setDelay(conf->readNumEntry("delay",0));
    mainWidget->setMode( conf->readNumEntry( "mode", 0 ) );
    mainWidget->setIncludeDecorations(conf->readBoolEntry("includeDecorations",true));

    connect( &grabTimer, TQ_SIGNAL( timeout() ), this, TQ_SLOT(  grabTimerDone() ) );

    TDEAccel* accel = new TDEAccel(this);
    TQ_CHECK_PTR(accel);
    accel->insert(TDEStdAccel::Print, this, TQ_SLOT(slotPrint()));
    accel->insert(TDEStdAccel::New, this, TQ_SLOT(slotGrab()));

    accel->insert( "Print2", TQt::Key_P, this, TQ_SLOT(slotPrint()));
    accel->insert( "New2", TQt::Key_N, this, TQ_SLOT(slotGrab()));
    accel->insert( "New3", TQt::Key_Space, this, TQ_SLOT(slotGrab()));

    mainWidget->btnNew->setFocus();
    
}

KSnapshot::~KSnapshot()
{
}

bool KSnapshot::save( const TQString &filename )
{
    return save( KURL::fromPathOrURL( filename ));
}

bool KSnapshot::save( const KURL& url )
{
    TQString type( KImageIO::type(url.path()) );
    if ( type.isNull() )
        type = "PNG";

    bool ok = false;

    if ( url.isLocalFile() ) {
        KSaveFile saveFile( url.path() );
        if ( saveFile.status() == 0 ) {
            if ( snapshot.save( saveFile.file(), type.latin1() ) )
                ok = saveFile.close();
        }
    }
    else {
        KTempFile tmpFile;
        tmpFile.setAutoDelete( true );
        if ( tmpFile.status() == 0 ) {
            if ( snapshot.save( tmpFile.file(), type.latin1() ) ) {
                if ( tmpFile.close() )
                    ok = TDEIO::NetAccess::upload( tmpFile.name(), url, this );
            }
        }
    }

    TQApplication::restoreOverrideCursor();
    if ( !ok ) {
        kdWarning() << "KSnapshot was unable to save the snapshot" << endl;

        TQString caption = i18n("Unable to Save Image");
        TQString text = i18n("KSnapshot was unable to save the image to\n%1.")
            .arg(url.prettyURL());
        KMessageBox::error(this, text, caption);
    }

    return ok;
}

void KSnapshot::slotCopy()
{
    TQClipboard *cb = TQApplication::clipboard();
    cb->setPixmap( snapshot );
}

void KSnapshot::slotDragSnapshot()
{
    TQDragObject *drobj = new TQImageDrag(snapshot.convertToImage(), this);
    TQ_CHECK_PTR(drobj);
    drobj->setPixmap(mainWidget->preview());
    drobj->dragCopy();
}

void KSnapshot::slotGrab()
{
    hide();
    if ( mainWidget->mode() == Region )
    {
        rgnGrab = new RegionGrabber();
        TQ_CHECK_PTR(rgnGrab);
        connect( rgnGrab, TQ_SIGNAL( regionGrabbed( const TQPixmap & ) ),
             TQ_SLOT( slotRegionGrabbed( const TQPixmap & ) ) );
    }
    else
    {
        if ( mainWidget->delay() )
            grabTimer.start( mainWidget->delay() * 1000, true );
        else {
            grabber->show();
            grabber->grabMouse( crossCursor );
        }
    }
}

void KSnapshot::slotPrint()
{
    KPrinter printer;
    if (snapshot.width() > snapshot.height())
        printer.setOrientation(KPrinter::Landscape);
    else
        printer.setOrientation(KPrinter::Portrait);

    tqApp->processEvents();

    if (printer.setup(this, i18n("Print Screenshot")))
    {
        tqApp->processEvents();

        TQPainter painter(&printer);
        TQPaintDeviceMetrics metrics(painter.device());

        float w = snapshot.width();
        float dw = w - metrics.width();
        float h = snapshot.height();
        float dh = h - metrics.height();
        bool scale = false;

        if ( (dw > 0.0) || (dh > 0.0) )
            scale = true;

        if ( scale ) {

            TQImage img = snapshot.convertToImage();
            tqApp->processEvents();

            float newh, neww;
            if ( dw > dh ) {
                neww = w-dw;
                newh = neww/w*h;
            }
            else {
                newh = h-dh;
                neww = newh/h*w;
            }

            img = img.smoothScale( int(neww), int(newh), TQImage::ScaleMin );
            tqApp->processEvents();

            int x = (metrics.width()-img.width())/2;
            int y = (metrics.height()-img.height())/2;

            painter.drawImage( x, y, img);
        }
        else {
            int x = (metrics.width()-snapshot.width())/2;
            int y = (metrics.height()-snapshot.height())/2;
            painter.drawPixmap( x, y, snapshot );
        }
    }

    tqApp->processEvents();
}

void KSnapshot::slotRegionGrabbed( const TQPixmap &pix )
{
    if ( !pix.isNull() )
    {
        snapshot = pix;
        updatePreview();
        modified = true;
    }

    delete rgnGrab;
    TQApplication::restoreOverrideCursor();
    show();
}

bool KSnapshot::eventFilter( TQObject* o, TQEvent* e)
{
    if ( o == grabber && e->type() == TQEvent::MouseButtonPress ) {
        TQMouseEvent* me = (TQMouseEvent*) e;
        if ( TQWidget::mouseGrabber() != grabber )
            return false;
        if ( me->button() == TQt::LeftButton )
            performGrab();
    }
    return false;
}

void KSnapshot::updatePreview()
{
    TQImage img = snapshot.convertToImage();
    double r1 = ((double) snapshot.height() ) / snapshot.width();
    if ( r1 * mainWidget->previewWidth()  < mainWidget->previewHeight() )
        img = img.smoothScale( mainWidget->previewWidth(),
                       int( mainWidget->previewWidth() * r1 ));
    else
        img = img.smoothScale( (int) (((double)mainWidget->previewHeight()) / r1),
                       (mainWidget->previewHeight() ) );

    TQPixmap pm;
    pm.convertFromImage( img );
    mainWidget->setPreview( pm );
}

void KSnapshot::grabTimerDone()
{
    performGrab();
    KNotifyClient::beep(i18n("The screen has been successfully grabbed."));
}

static 
Window findRealWindow( Window w, int depth = 0 )
{
    if( depth > 5 )
        return None;
    static Atom wm_state = XInternAtom( tqt_xdisplay(), "WM_STATE", False );
    Atom type;
    int format;
    unsigned long nitems, after;
    unsigned char* prop;
    if( XGetWindowProperty( tqt_xdisplay(), w, wm_state, 0, 0, False, AnyPropertyType,
                &type, &format, &nitems, &after, &prop ) == Success ) {
        if( prop != NULL )
            XFree( prop );
        if( type != None )
            return w;
    }
    Window root, parent;
    Window* children;
    unsigned int nchildren;
    Window ret = None;
    if( XQueryTree( tqt_xdisplay(), w, &root, &parent, &children, &nchildren ) != 0 ) {
        for( unsigned int i = 0;
             i < nchildren && ret == None;
             ++i )
            ret = findRealWindow( children[ i ], depth + 1 );
        if( children != NULL )
            XFree( children );
    }
    return ret;
}

void KSnapshot::performGrab()
{
    grabber->releaseMouse();
    grabber->hide();
    grabTimer.stop();
    XGrabServer( tqt_xdisplay());
    if ( mainWidget->mode() == WindowUnderCursor ) {
        Window root;
        Window child;
        uint mask;
        int rootX, rootY, winX, winY;
        XQueryPointer( tqt_xdisplay(), tqt_xrootwin(), &root, &child,
                   &rootX, &rootY, &winX, &winY,
                   &mask);
        if( child == None )
            child = tqt_xrootwin();
        if( !mainWidget->includeDecorations()) {
            Window real_child = findRealWindow( child );
            if( real_child != None ) // test just in case
                child = real_child;
        }
        int x, y;
        unsigned int w, h;
        unsigned int border;
        unsigned int depth;
        XGetGeometry( tqt_xdisplay(), child, &root, &x, &y,
                  &w, &h, &border, &depth );
        w += 2 * border;
        h += 2 * border;

        Window parent;
        Window* children;
        unsigned int nchildren;
        if( XQueryTree( tqt_xdisplay(), child, &root, &parent,
                &children, &nchildren ) != 0 ) {
            if( children != NULL )
                XFree( children );
            int newx, newy;
            Window dummy;
            if( XTranslateCoordinates( tqt_xdisplay(), parent, tqt_xrootwin(),
                           x, y, &newx, &newy, &dummy )) {
                x = newx;
                y = newy;
            }
        }

        snapshot = TQPixmap::grabWindow( tqt_xrootwin(), x, y, w, h );

#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
        //No XShape - no work.
        if (haveXShape) {
            TQBitmap mask(w, h);
            //As the first step, get the mask from XShape.
            int count, order;
            XRectangle* rects = XShapeGetRectangles( tqt_xdisplay(), child,
                                 ShapeBounding, &count, &order);
            //The ShapeBounding region is the outermost shape of the window;
            //ShapeBounding - ShapeClipping is defined to be the border.
            //Since the border area is part of the window, we use bounding
            // to limit our work region
            if (rects) {
                //Create a TQRegion from the rectangles describing the bounding mask.
                TQRegion contents;
                for (int pos = 0; pos < count; pos++)
                    contents += TQRegion(rects[pos].x, rects[pos].y,
                                rects[pos].width, rects[pos].height);
                XFree(rects);

                //Create the bounding box.
                TQRegion bbox(0, 0, snapshot.width(), snapshot.height());
                
                if( border > 0 ) {
                    contents.translate( border, border );
                    contents += TQRegion( 0, 0, border, h );
                    contents += TQRegion( 0, 0, w, border );
                    contents += TQRegion( 0, h - border, w, border );
                    contents += TQRegion( w - border, 0, border, h );
                }
                
                //Get the masked away area.
                TQRegion maskedAway = bbox - contents;
                TQMemArray<TQRect> maskedAwayRects = maskedAway.rects();

                //Construct a bitmap mask from the rectangles
                TQPainter p(&mask);
                p.fillRect(0, 0, w, h, TQt::color1);
                for (uint pos = 0; pos < maskedAwayRects.count(); pos++)
                    p.fillRect(maskedAwayRects[pos], TQt::color0);
                p.end();

                snapshot.setMask(mask);
            }
        }
#endif
    }
    else {
        snapshot = TQPixmap::grabWindow( tqt_xrootwin() );
    }
    XUngrabServer( tqt_xdisplay());
    updatePreview();
    TQApplication::restoreOverrideCursor();
    modified = true;
//     show();
    slotOk();
}

void KSnapshot::setTime(int newTime)
{
    mainWidget->setDelay(newTime);
}

void KSnapshot::setURL( const TQString &url )
{
    KURL newURL = KURL::fromPathOrURL( url );
    if ( newURL == filename )
        return;

    filename = newURL;
}

void KSnapshot::setGrabMode( int m )
{
    mainWidget->setMode( m );
}

void KSnapshot::slotMovePointer(int x, int y)
{
    TQCursor::setPos( x, y );
}

void KSnapshot::exit()
{

    TDEConfig *conf=TDEGlobal::config();
    conf->setGroup("GENERAL");
    conf->writeEntry("delay",mainWidget->delay());
    conf->writeEntry("mode",mainWidget->mode());
    conf->writeEntry("includeDecorations",mainWidget->includeDecorations());
    KURL url = filename;
    url.setPass( TQString() );
    conf->writePathEntry("filename",url.url());

    reject();
}

void KSnapshot::slotOk()
{

    TDEConfig *conf=TDEGlobal::config();
    conf->setGroup("GENERAL");
    conf->writeEntry("delay",mainWidget->delay());
    conf->writeEntry("mode",mainWidget->mode());
    conf->writeEntry("includeDecorations",mainWidget->includeDecorations());
    KURL url = filename;
    url.setPass( TQString() );
    conf->writePathEntry("filename",url.url());

    emit screenGrabbed();

    accept();
}

#include "ksnapshot.moc"
