/*
    This file is part of the KDE libraries
    Copyright (C) 2008 Marijn Kruisselbrink (m.kruisselbrink@student.tue.nl)

    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 Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include "kwindowinfo_mac_p.h"
#include "kwindowinfo.h"
#include "kwindowsystem.h"

#include <kiconloader.h>
#include <klocale.h>
#include <kuniqueapplication.h>
#include <kdebug.h>
#include <kxerrorhandler.h>
#include <netwm.h>
#include <QtGui/QBitmap>
#include <QDesktopWidget>
#include <QtGui/QDialog>
#include <QtDBus/QtDBus>

KWindowInfo::Private::Private()
    : win(0), isLocal(false), m_axWin(0), m_pid(-1), loadedData(false), parent()
{
}

void KWindowInfo::Private::setAxElement(const AXUIElementRef& axWin)
{
    m_axWin = axWin;
    CFRetain(m_axWin);
}

void KWindowInfo::Private::setProcessSerialNumber(const ProcessSerialNumber& psn)
{
    m_psn = psn;
    GetProcessPID(&psn, &m_pid);
}

KWindowInfo::Private::~Private()
{
    CFRelease(m_axWin);
}

void KWindowInfo::Private::updateData()
{
    ProcessInfoRec pinfo;
    char processName[512];
    FSSpec appSpec;
    pinfo.processInfoLength = sizeof pinfo;
    pinfo.processName = (unsigned char*) processName;
    pinfo.processAppSpec = &appSpec;
    GetProcessInformation(&m_psn, &pinfo);
    name = QString::fromAscii(processName+1, processName[0]);

    if (m_axWin) {
        CFStringRef title;
        if (AXUIElementCopyAttributeValue(m_axWin, kAXTitleAttribute, (CFTypeRef*)&title) == noErr) {
            CFStringGetCString(title, processName, sizeof processName, kCFStringEncodingUTF8);
            name = QString::fromUtf8(processName);
        }
    }

    iconSpec = appSpec;

    FSRef ref;
    FSpMakeFSRef(&appSpec, &ref);
    // check if it is in an application bundle (foo.app/Contents/MacOS/plasma)
    HFSUniStr255 name;
    FSRef parentRef;
    FSGetCatalogInfo(&ref, kFSCatInfoNone, 0, &name, 0, &parentRef);
    ref = parentRef;
    FSGetCatalogInfo(&ref, kFSCatInfoNone, 0, &name, 0, &parentRef);
    if (QString::fromUtf16(name.unicode, name.length) == "MacOS") {
        ref = parentRef;
        FSGetCatalogInfo(&ref, kFSCatInfoNone, 0, &name, 0, &parentRef);
        if (QString::fromUtf16(name.unicode, name.length) == "Contents") {
            FSSpec spec;
            ref = parentRef;
            FSGetCatalogInfo(&ref, kFSCatInfoNone, 0, &name, &spec, &parentRef);
            iconSpec = spec;
        }
    }

    loadedData = true;
}

KWindowInfo::KWindowInfo( WId win, unsigned long, unsigned long ) : d(new Private)
{
    d->ref = 1;
    d->win = win;
    d->isLocal = true;
    if (!win) {
        d->win = (WId) d;
        d->isLocal = false;
    }
}


// this one is only to make QList<> or similar happy
KWindowInfo::KWindowInfo()
    : d( NULL )
{
}

KWindowInfo::~KWindowInfo()
{
    if( d != NULL ) {
        if( --d->ref == 0 ) {
            delete d;
        }
    }
}

KWindowInfo::KWindowInfo( const KWindowInfo& wininfo )
    : d( wininfo.d )
{
    if( d != NULL )
	++d->ref;
}

KWindowInfo& KWindowInfo::operator=( const KWindowInfo& wininfo )
{
    if( d != wininfo.d ) {
        if( d != NULL )
            if( --d->ref == 0 )
        delete d;
        d = wininfo.d;
        if( d != NULL )
            ++d->ref;
    }
    return *this;
}

bool KWindowInfo::valid( bool withdrawn_is_valid ) const
{
    return d->pid() >= 0;
}

WId KWindowInfo::win() const
{
    return d->win;
}

unsigned long KWindowInfo::state() const
{
    return 0;
}

bool KWindowInfo::hasState( unsigned long s ) const
{
    return false;
}

bool KWindowInfo::isMinimized() const
{
    if (d->axElement()) {
        CFBooleanRef val;
        if (AXUIElementCopyAttributeValue(d->axElement(), kAXMinimizedAttribute, (CFTypeRef*)&val) == noErr) {
            return CFBooleanGetValue(val);
        } else {
            return false;
        }
    } else {
        return false;
    }
}

NET::MappingState KWindowInfo::mappingState() const
{
    return (NET::MappingState) 0;
}

NETExtendedStrut KWindowInfo::extendedStrut() const
{
    NETExtendedStrut ext;
    return ext;
}

NET::WindowType KWindowInfo::windowType( int supported_types ) const
{
    return (NET::WindowType) 0;
}

QString KWindowInfo::visibleNameWithState() const
{
    QString s = visibleName();
    if ( isMinimized() ) {
	s.prepend(QLatin1Char('('));
	s.append(QLatin1Char(')'));
    }
    return s;
}

QString KWindowInfo::visibleName() const
{
    return name();
}

QString KWindowInfo::name() const
{
    if (!d->loadedData) {
        d->updateData();
    }
    return d->name;
}

QString KWindowInfo::visibleIconNameWithState() const
{
    QString s = visibleIconName();
    if ( isMinimized() ) {
	s.prepend(QLatin1Char('('));
	s.append(QLatin1Char(')'));
    }
    return s;
}

QString KWindowInfo::visibleIconName() const
{
    return visibleName();
}

QString KWindowInfo::iconName() const
{
    return name();
}

bool KWindowInfo::isOnCurrentDesktop() const
{
    return isOnDesktop( KWindowSystem::currentDesktop());
}

bool KWindowInfo::isOnDesktop( int _desktop ) const
{
    return true;
}

bool KWindowInfo::onAllDesktops() const
{
    return false;
}

int KWindowInfo::desktop() const
{
    return 0;
}

QRect KWindowInfo::geometry() const
{
    return QRect();
}

QRect KWindowInfo::frameGeometry() const
{
    return QRect();
}

bool KWindowInfo::actionSupported( NET::Action action ) const
{
    return true; // no idea if it's supported or not -> pretend it is
}

#if 0
WId KWindowInfo::transientFor() const
{
    kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2TransientFor ) == 0, 176 )
        << "Pass NET::WM2TransientFor to KWindowInfo" << endl;
    return d->info->transientFor();
}

WId KWindowInfo::groupLeader() const
{
    kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2GroupLeader ) == 0, 176 )
        << "Pass NET::WM2GroupLeader to KWindowInfo" << endl;
    return d->info->groupLeader();
}

QByteArray KWindowInfo::windowClassClass() const
{
    kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2WindowClass ) == 0, 176 )
        << "Pass NET::WM2WindowClass to KWindowInfo" << endl;
    return d->info->windowClassClass();
}

QByteArray KWindowInfo::windowClassName() const
{
    kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2WindowClass ) == 0, 176 )
        << "Pass NET::WM2WindowClass to KWindowInfo" << endl;
    return d->info->windowClassName();
}

QByteArray KWindowInfo::windowRole() const
{
    kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2WindowRole ) == 0, 176 )
        << "Pass NET::WM2WindowRole to KWindowInfo" << endl;
    return d->info->windowRole();
}

QByteArray KWindowInfo::clientMachine() const
{
    kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2ClientMachine ) == 0, 176 )
        << "Pass NET::WM2ClientMachine to KWindowInfo" << endl;
    return d->info->clientMachine();
}

bool KWindowInfo::actionSupported( NET::Action action ) const
{
    kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2AllowedActions ) == 0, 176 )
        << "Pass NET::WM2AllowedActions to KWindowInfo" << endl;
    if( KWindowSystem::allowedActionsSupported())
        return d->info->allowedActions() & action;
    else
        return true; // no idea if it's supported or not -> pretend it is
}

// see NETWM spec section 7.6
bool KWindowInfo::isMinimized() const
{
    if( mappingState() != NET::Iconic )
        return false;
    // NETWM 1.2 compliant WM - uses NET::Hidden for minimized windows
    if(( state() & NET::Hidden ) != 0
	&& ( state() & NET::Shaded ) == 0 ) // shaded may have NET::Hidden too
        return true;
    // older WMs use WithdrawnState for other virtual desktops
    // and IconicState only for minimized
    return KWindowSystem::icccmCompliantMappingState() ? false : true;
}
#endif
