/******************************************************************************
*
* 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
{
    Debug::Throw( "XcbInterface::findIcons.\n" );

    #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;

    if( !XcbUtil::isX11() ) return;

    // connection and atom
    auto connection( XcbUtil::get().connection<xcb_connection_t>() );
    auto icon( XcbUtil::get().icon( window ) );
    if( !icon.isNull() )
    {
        auto atom( *XcbUtil::get().atom<xcb_atom_t>( XcbDefines::_NET_WM_PID ) );
        auto 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.get() ))[0] );

            // find matching job in list
            auto 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
                jobIter->setIconInitialized( true );
                jobIter->setIcon( icon );
            }
        }

    }

    {
        // parse children
        auto 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.get() ) );
        auto children( xcb_query_tree_children( reply.get() ) );
        for( int i=0; i < childCount; ++i ) _findIcons( children[i], jobs );
    }

    #endif
    return;

}
