/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2004-07-09
 * Description : a kio-slave to process tag query on
 *               digiKam albums.
 *
 * Copyright (C) 2004 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
 *
 * This program 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, or (at your option)
 * any later version.
 *
 * This program 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.
 *
 * ============================================================ */

// C Ansi includes.

extern "C"
{
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
}

// C++ includes.

#include <cstdlib>
#include <cstdio>
#include <ctime>

// TQt incudes.

#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqstring.h>
#include <tqdir.h>
#include <tqregexp.h>

// KDE includes.

#include <kinstance.h>
#include <kdebug.h>
#include <kurl.h>
#include <klocale.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kio/global.h>
#include <kfilemetainfo.h>

// Local includes.

#include "digikam_export.h"
#include "digikamtags.h"

kio_digikamtagsProtocol::kio_digikamtagsProtocol(const TQCString &pool_socket,
                                                 const TQCString &app_socket)
                       : SlaveBase("kio_digikamtags", pool_socket, app_socket)
{
}

kio_digikamtagsProtocol::~kio_digikamtagsProtocol()
{
}

static TQValueList<TQRegExp> makeFilterList( const TQString &filter )
{
    TQValueList<TQRegExp> regExps;
    if ( filter.isEmpty() )
        return regExps;

    TQChar sep( ';' );
    int i = filter.find( sep, 0 );
    if ( i == -1 && filter.find( ' ', 0 ) != -1 )
        sep = TQChar( ' ' );

    TQStringList list = TQStringList::split( sep, filter );
    TQStringList::Iterator it = list.begin();
    while ( it != list.end() ) {
        regExps << TQRegExp( (*it).stripWhiteSpace(), false, true );
        ++it;
    }
    return regExps;
}

static bool matchFilterList( const TQValueList<TQRegExp>& filters,
                             const TQString &fileName )
{
    TQValueList<TQRegExp>::ConstIterator rit = filters.begin();
    while ( rit != filters.end() ) {
        if ( (*rit).exactMatch(fileName) )
            return true;
        ++rit;
    }
    return false;
}

void kio_digikamtagsProtocol::special(const TQByteArray& data)
{
    bool folders = (metaData("folders") == "yes");

    TQString libraryPath;
    KURL    kurl;
    TQString url;
    TQString filter;
    int     getDimensions;
    int     tagID;
    int     recurseAlbums;
    int     recurseTags;

    TQDataStream ds(data, IO_ReadOnly);
    ds >> libraryPath;
    ds >> kurl;
    ds >> filter;
    ds >> getDimensions;
    ds >> recurseAlbums;
    ds >> recurseTags;

    url = kurl.path();

    TQValueList<TQRegExp> regex = makeFilterList(filter);

    if (m_libraryPath != libraryPath)
    {
        m_libraryPath = libraryPath;
        m_db.closeDB();
        m_db.openDB(libraryPath);
    }

    TQByteArray  ba;

    if (folders)       // Special mode to stats all tag items
    {
        TQMap<int, int> tagsStatMap;
        int            tagID, imageID;
        TQStringList    values, allTagIDs;

        // initialize allTagIDs with all existing tags from db to prevent
        // wrong tag counters
        m_db.execSql(TQString("SELECT id from Tags"), &allTagIDs);

        for ( TQStringList::iterator it = allTagIDs.begin(); it != allTagIDs.end(); ++it)
        {
            tagID = (*it).toInt();
            tagsStatMap.insert(tagID, 0);
        }

        // now we can count the tags assigned to images
        m_db.execSql(TQString("SELECT ImageTags.tagid, Images.name FROM ImageTags, Images "
                "WHERE ImageTags.imageid=Images.id"), &values);

        for ( TQStringList::iterator it = values.begin(); it != values.end(); )
        {
            tagID = (*it).toInt();
            ++it;

            if ( matchFilterList( regex, *it ) )
            {
                TQMap<int, int>::iterator it2 = tagsStatMap.find(tagID);
                if ( it2 == tagsStatMap.end() )
                    tagsStatMap.insert(tagID, 1);
                else
                    tagsStatMap.replace(tagID, it2.data() + 1);
            }

            ++it;
        }

        TQDataStream os(ba, IO_WriteOnly);
        os << tagsStatMap;
    }
    else
    {
        tagID = TQStringList::split('/',url).last().toInt();

        TQStringList values;

        if (recurseTags)
        {
            // Obtain all images with the given tag, or with this tag as a parent.
            m_db.execSql( TQString( "SELECT DISTINCT Images.id, Images.name, Images.dirid, \n "
                                "       Images.datetime, Albums.url \n "
                                " FROM Images, Albums \n "
                                " WHERE Images.id IN \n "
                                "       (SELECT imageid FROM ImageTags \n "
                                "        WHERE tagid=%1 \n "
                                "           OR tagid IN (SELECT id FROM TagsTree WHERE pid=%2)) \n "
                                "   AND Albums.id=Images.dirid \n " )
                        .arg(tagID)
                        .arg(tagID), &values );
        }
        else
        {
            // Obtain all images with the given tag
            m_db.execSql( TQString( "SELECT DISTINCT Images.id, Images.name, Images.dirid, \n "
                                    "       Images.datetime, Albums.url \n "
                                    " FROM Images, Albums \n "
                                    " WHERE Images.id IN \n "
                                    "       (SELECT imageid FROM ImageTags \n "
                                    "        WHERE tagid=%1) \n "
                                    "   AND Albums.id=Images.dirid \n " )
                        .arg(tagID), &values );
        }

        TQ_LLONG imageid;
        TQString name;
        TQString path;
        int     dirid;
        TQString date;
        TQString purl;
        TQSize   dims;

        int count = 0;
        TQDataStream* os = new TQDataStream(ba, IO_WriteOnly);

        struct stat stbuf;
        for (TQStringList::iterator it = values.begin(); it != values.end();)
        {
            imageid = (*it).toLongLong();
            ++it;
            name  = *it;
            ++it;
            dirid = (*it).toInt();
            ++it;
            date  = *it;
            ++it;
            purl  = *it;
            ++it;

            if (!matchFilterList(regex, name))
                continue;

            path = m_libraryPath + purl + '/' + name;
            if (::stat(TQFile::encodeName(path), &stbuf) != 0)
                continue;

            dims = TQSize();
            if (getDimensions)
            {
                KFileMetaInfo metaInfo(path);
                if (metaInfo.isValid())
                {
                    if (metaInfo.containsGroup("Jpeg EXIF Data"))
                    {
                        dims = metaInfo.group("Jpeg EXIF Data").
                            item("Dimensions").value().toSize();
                    }
                    else if (metaInfo.containsGroup("General"))
                    {
                        dims = metaInfo.group("General").
                            item("Dimensions").value().toSize();
                    }
                    else if (metaInfo.containsGroup("Technical"))
                    {
                        dims = metaInfo.group("Technical").
                            item("Dimensions").value().toSize();
                    }
                }
            }

            *os << imageid;
            *os << dirid;
            *os << name;
            *os << date;
            *os << static_cast<size_t>(stbuf.st_size);
            *os << dims;

            count++;

            if (count > 200)
            {
                delete os;
                os = 0;

                SlaveBase::data(ba);
                ba.resize(0);

                count = 0;
                os = new TQDataStream(ba, IO_WriteOnly);
            }
        }

        delete os;
    }

    SlaveBase::data(ba);

    finished();
}

/* KIO slave registration */

extern "C"
{
    DIGIKAM_EXPORT int kdemain(int argc, char **argv)
    {
        KLocale::setMainCatalogue("digikam");
        KInstance instance( "kio_digikamtags" );
        ( void ) KGlobal::locale();

        kdDebug() << "*** kio_digikamtag started ***" << endl;

        if (argc != 4) {
            kdDebug() << "Usage: kio_digikamtags  protocol domain-socket1 domain-socket2"
                      << endl;
            exit(-1);
        }

        kio_digikamtagsProtocol slave(argv[2], argv[3]);
        slave.dispatchLoop();

        kdDebug() << "*** kio_digikamtags finished ***" << endl;
        return 0;
    }
}

