/******************************************************************************
*
* Copyright (C) 2002 Hugo PEREIRA <mailto: hugo.pereira@free.fr>
*
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This software 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program.  If not, see <http://www.gnu.org/licenses/>.
*
*******************************************************************************/

#include "XcbInterface.h"

#include "Debug.h"
#include "XcbUtil.h"

#if HAVE_XCB
#include <xcb/xcb.h>
#endif

//_________________________________________________________
void XcbInterface::findIcons( Job::List& jobs ) const
{

    #if HAVE_XCB
    // check atoms
    if( !( XcbUtil::get().isSupported( XcbDefines::_NET_WM_PID ) && XcbUtil::get().isSupported( XcbDefines::_NET_WM_ICON ) ) )
    { return; }

    // find recursively, starting from root window
    _findIcons( XcbUtil::get().appRootWindow(), jobs );
    #else
    return;
    #endif
}

//_________________________________________________________
void XcbInterface::_findIcons( WId window, Job::List& jobs ) const
{

    #if HAVE_XCB
    Debug::Throw() << "XcbInterface::_findIcons - window: " << window << endl;

    // connection and atom
    xcb_connection_t* connection( XcbUtil::get().connection<xcb_connection_t>() );
    xcb_atom_t atom( *XcbUtil::get().atom<xcb_atom_t>( XcbDefines::_NET_WM_PID ) );

    xcb_get_property_cookie_t cookie( xcb_get_property( connection, 0, window, atom, XCB_ATOM_CARDINAL, 0, 1 ) );
    XcbUtil::ScopedPointer<xcb_get_property_reply_t> reply( xcb_get_property_reply( connection, cookie, nullptr ) );
    if( reply )
    {
        const uint32_t pid( reinterpret_cast<uint32_t*>(xcb_get_property_value( reply.data() ))[0] );

        // find matching job in list
        Job::List::iterator jobIter = std::find_if( jobs.begin(), jobs.end(), Job::SameIdFTor( pid ) );
        if( jobIter != jobs.end() )
        {
            // check job pixmap status
            // no need to go further, nor find children windows if jobIter already has a pixmap
            if( jobIter->iconInitialized() ) return;

            // find window icon
            QIcon icon( _icon( window ) );
            if( !icon.isNull() )
            {
                jobIter->setIconInitialized( true );
                jobIter->setIcon( icon );
                return;
            }
        }
    }

    {
        // parse children
        xcb_query_tree_cookie_t cookie = xcb_query_tree( connection, window );
        XcbUtil::ScopedPointer<xcb_query_tree_reply_t> reply( xcb_query_tree_reply( connection, cookie, nullptr ) );
        if( !reply ) return;

        const int childCount( xcb_query_tree_children_length( reply.data() ) );
        xcb_window_t* children( xcb_query_tree_children( reply.data() ) );
        for( int i=0; i < childCount; ++i )
        { _findIcons( children[i], jobs ); }
    }

    #endif
    return;

}

//_________________________________________________________
QIcon XcbInterface::_icon( WId window ) const
{

    QIcon result;

    #if HAVE_XCB
    Debug::Throw() << "XcbInterface::_icon - window: " << window << endl;

    // connection and atom
    xcb_connection_t* connection( XcbUtil::get().connection<xcb_connection_t>() );
    xcb_atom_t atom( *XcbUtil::get().atom<xcb_atom_t>( XcbDefines::_NET_WM_ICON ) );

    uint32_t offset(0);
    xcb_get_property_reply_t* reply( nullptr );
    forever
    {

        // width
        uint32_t width(0);
        xcb_get_property_cookie_t cookie( xcb_get_property( connection, 0, window, atom, XCB_ATOM_CARDINAL, offset, 1 ) );
        if( (reply = xcb_get_property_reply( connection, cookie, nullptr )) && xcb_get_property_value_length( reply ) > 0 )
        {
            width = reinterpret_cast<uint32_t*>( xcb_get_property_value(reply) )[0];
            free( reply );
            ++offset;

        } else break;

        // height
        uint32_t height(0);
        cookie = xcb_get_property( connection, 0, window, atom, XCB_ATOM_CARDINAL, offset, 1 );
        if( (reply = xcb_get_property_reply( connection, cookie, nullptr )) && xcb_get_property_value_length( reply ) > 0 )
        {
            height = reinterpret_cast<uint32_t*>( xcb_get_property_value(reply) )[0];
            free( reply );
            ++offset;

        } else break;

        // data
        const uint32_t length( width*height );
        cookie = xcb_get_property( connection, 0, window, atom, XCB_ATOM_CARDINAL, offset, length );
        if( (reply = xcb_get_property_reply( connection, cookie, nullptr )) && xcb_get_property_value_length( reply ) > 0 )
        {

            // get image data
            const uint32_t* imageData( reinterpret_cast<uint32_t*>( xcb_get_property_value( reply ) ) );

            // create image
            QImage image( width, height, QImage::Format_ARGB32);
            for(int i=0; i<image.byteCount()/4; ++i)
            { ((uint32_t*)image.bits())[i] = imageData[i]; }

            // add to icon
            result.addPixmap( QPixmap::fromImage( image ) );

            // free reply and increment offset
            free( reply );
            offset += length;

        } else break;
    }

    // free
    if( reply ) free( reply );

    #endif

    return result;
}
