/* This file is part of the KDE project
   Copyright (C) 2000 Werner Trobin <trobin@kde.org>

   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 <KoTemplates.h>

#include <tqdir.h>
#include <tqimage.h>
#include <tqprinter.h>

#include <kdesktopfile.h>
#include <ksimpleconfig.h>
#include <kdebug.h>
#include <tdeinstance.h>
#include <ksavefile.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <tdeio/netaccess.h>
#include <tdelocale.h>

#include <stdlib.h>


KoTemplate::KoTemplate(const TQString &name, const TQString &description, const TQString &file,
                       const TQString &picture, const TQString &fileName, const TQString &_measureSystem,
                       bool hidden, bool touched) :
    m_name(name), m_descr(description), m_file(file), m_picture(picture), m_fileName(fileName),
    m_hidden(hidden), m_touched(touched), m_cached(false), m_measureSystem(_measureSystem)
{
}

const TQPixmap &KoTemplate::loadPicture( TDEInstance* instance ) {

    if(m_cached)
        return m_pixmap;
    m_cached=true;
    if ( m_picture[ 0 ] == '/' )
    {
        // ### TODO: use the class KoPicture instead of TQImage to support non-image pictures
        TQImage img( m_picture );
        if (img.isNull()) {
            kdWarning() << "Couldn't find icon " << m_picture << endl;
            m_pixmap=TQPixmap();
            return m_pixmap;
        }
        const int maxHeightWidth = 128; // ### TODO: some people would surely like to have 128x128
        if (img.width() > maxHeightWidth || img.height() > maxHeightWidth) {
            img = img.smoothScale( maxHeightWidth, maxHeightWidth, TQImage::ScaleMax );
        }
        m_pixmap.convertFromImage(img);
        return m_pixmap;
    } else { // relative path
        m_pixmap = instance->iconLoader()->loadIcon( m_picture, TDEIcon::Desktop, 128 );
        return m_pixmap;
    }
}


KoTemplateGroup::KoTemplateGroup(const TQString &name, const TQString &dir,
                                 int _sortingWeight, bool touched) :
    m_name(name), m_touched(touched), m_sortingWeight(_sortingWeight)
{
    m_dirs.append(dir);
    m_templates.setAutoDelete(true);
}

bool KoTemplateGroup::isHidden() const {

    TQPtrListIterator<KoTemplate> it(m_templates);
    bool hidden=true;
    while(it.current()!=0L && hidden) {
        hidden=it.current()->isHidden();
        ++it;
    }
    return hidden;
}

void KoTemplateGroup::setHidden(bool hidden) const {

    TQPtrListIterator<KoTemplate> it(m_templates);
    for( ; it.current()!=0L; ++it)
        it.current()->setHidden(hidden);
    m_touched=true;
}

bool KoTemplateGroup::add(KoTemplate *t, bool force, bool touch) {

    KoTemplate *myTemplate=find(t->name());
    if(myTemplate==0L) {
        m_templates.append(t);
        m_touched=touch;
        return true;
    }
    else if(myTemplate && force) {
        //kdDebug() << "removing :" << myTemplate->fileName() << endl;
        TQFile::remove( myTemplate->fileName()  );
        TQFile::remove( myTemplate->picture() );
        TQFile::remove( myTemplate->file() );
        m_templates.removeRef(myTemplate);
        m_templates.append(t);
        m_touched=touch;
        return true;
    }
    return false;
}

KoTemplate *KoTemplateGroup::find(const TQString &name) const {

    TQPtrListIterator<KoTemplate> it(m_templates);
    while(it.current() && it.current()->name()!=name)
        ++it;
    return it.current();
}


KoTemplateTree::KoTemplateTree(const TQCString &templateType,
                               TDEInstance *instance, bool readTree) :
    m_templateType(templateType), m_instance(instance), m_defaultGroup(0L),
    m_defaultTemplate(0L) {

    m_groups.setAutoDelete(true);
    if(readTree)
        readTemplateTree();
}

void KoTemplateTree::readTemplateTree() {

    readGroups();
    readTemplates();
}

void KoTemplateTree::writeTemplateTree() {
    TQString localDir=m_instance->dirs()->saveLocation(m_templateType);

    for(KoTemplateGroup *group=m_groups.first(); group!=0L; group=m_groups.next()) {
        //kdDebug() << "---------------------------------" << endl;
        //kdDebug() << "group: " << group->name() << endl;

        bool touched=false;
        for(KoTemplate *t=group->first(); t!=0L && !touched && !group->touched(); t=group->next())
            touched=t->touched();

        if(group->touched() || touched) {
            //kdDebug() << "touched" << endl;
            if(!group->isHidden()) {
                //kdDebug() << "not hidden" << endl;
                TDEStandardDirs::makeDir(localDir+group->name()); // create the local group dir
            }
            else {
                //kdDebug() << "hidden" << endl;
                if(group->dirs().count()==1 && !group->dirs().grep(localDir).isEmpty()) {
                    //kdDebug() << "local only" << endl;
                    TDEIO::NetAccess::del(group->dirs().first(), 0);
                    //kdDebug() << "removing: " << group->dirs().first() << endl;
                }
                else {
                    //kdDebug() << "global" << endl;
                    TDEStandardDirs::makeDir(localDir+group->name());
                }
            }
        }
        for(KoTemplate *t=group->first(); t!=0L; t=group->next()) {
            if(t->touched()) {
                //kdDebug() << "++template: " << t->name() << endl;
                writeTemplate(t, group, localDir);
            }
            if(t->isHidden() && t->touched() ) {
                //kdDebug() << "+++ delete local template ##############" << endl;
                writeTemplate(t, group, localDir);
                TQFile::remove(t->file());
                TQFile::remove(t->picture());
            }
        }
    }
}

void KoTemplateTree::add(KoTemplateGroup *g) {

    KoTemplateGroup *group=find(g->name());
    if(group==0L)
        m_groups.append(g);
    else
        group->addDir(g->dirs().first()); // "...there can be only one..." (Queen)
}

KoTemplateGroup *KoTemplateTree::find(const TQString &name) const {

    TQPtrListIterator<KoTemplateGroup> it(m_groups);
    while(it.current() && it.current()->name()!=name)
        ++it;
    return it.current();
}

void KoTemplateTree::readGroups() {

    TQStringList dirs = m_instance->dirs()->resourceDirs(m_templateType);
    for(TQStringList::ConstIterator it=dirs.begin(); it!=dirs.end(); ++it) {
        //kdDebug() << "dir: " << *it << endl;
        TQDir dir(*it);
        // avoid the annoying warning
        if(!dir.exists())
            continue;
        dir.setFilter(TQDir::Dirs);
        TQStringList templateDirs=dir.entryList();
        for(TQStringList::ConstIterator tdirIt=templateDirs.begin(); tdirIt!=templateDirs.end(); ++tdirIt) {
            if(*tdirIt=="." || *tdirIt=="..") // we don't want to check those dirs :)
                continue;
            TQDir templateDir(*it+*tdirIt);
            TQString name=*tdirIt;
            TQString defaultTab;
            int sortingWeight = 1000;
            if(templateDir.exists(".directory")) {
                KSimpleConfig config(templateDir.absPath()+"/.directory", true);
                config.setDesktopGroup();
                name=config.readEntry("Name");
                defaultTab=config.readEntry("X-TDE-DefaultTab");
                sortingWeight=config.readNumEntry("X-TDE-SortingWeight", 1000);
                //kdDebug() << "name: " << name <<endl;
            }
            KoTemplateGroup *g=new KoTemplateGroup(name, *it+*tdirIt+TQChar('/'), sortingWeight);
            add(g);
            if(defaultTab=="true")
                m_defaultGroup=g;
        }
    }
}

void KoTemplateTree::readTemplates() {
    TQString dontShow = "imperial";

    if(TDEGlobal::locale()->pageSize() == TQPrinter::Letter) {
        dontShow = "metric";
    }

    TQPtrListIterator<KoTemplateGroup> groupIt(m_groups);
    for( ; groupIt.current()!=0L; ++groupIt) {
        TQStringList dirs=groupIt.current()->dirs();
        for(TQStringList::ConstIterator it=dirs.begin(); it!=dirs.end(); ++it) {
            TQDir d(*it);
            if( !d.exists() )
                continue;
            TQStringList files=d.entryList( TQDir::Files | TQDir::Readable, TQDir::Name );
            for(unsigned int i=0; i<files.count(); ++i) {
                TQString filePath = *it + files[i];
                //kdDebug() << "filePath: " << filePath << endl;
                TQString icon;
                TQString text;
                TQString description;
                TQString hidden_str;
                TQString fileName;
                bool hidden=false;
                bool defaultTemplate = false;
                TQString templatePath;
                TQString measureSystem;
                // If a desktop file, then read the name from it.
                // Otherwise (or if no name in it?) use file name
                if (KDesktopFile::isDesktopFile(filePath)) {
                    KSimpleConfig config(filePath, true);
                    config.setDesktopGroup();
                    if (config.readEntry("Type")=="Link") {
                        text=config.readEntry("Name");
                        fileName=filePath;
                        description=config.readEntry("Comment");
                        //kdDebug() << "name: " << text << endl;
                        icon=config.readEntry("Icon");
                        if(icon[0]!='/' && // allow absolute paths for icons
                           TQFile::exists(*it+icon)) // allow icons from icontheme
                            icon=*it+icon;
                        //kdDebug() << "icon2: " << icon << endl;
                        hidden=config.readBoolEntry("X-TDE-Hidden", false);
                        defaultTemplate = config.readBoolEntry("X-TDE-DefaultTemplate", false);
                        measureSystem=config.readEntry("X-TDE-MeasureSystem").lower();

                        // Don't add a template that is for the wrong measure system
                        if(measureSystem == dontShow)
                            continue;

                        //kdDebug() << "hidden: " << hidden_str << endl;
                        templatePath=config.readPathEntry("URL");
                        //kdDebug() << "Link to : " << templatePath << endl;
                        if(templatePath[0]!='/') {
                            if(templatePath.left(6)=="file:/") // I doubt this will happen
                                templatePath=templatePath.right(templatePath.length()-6);
                            //else
                            //  kdDebug() << "dirname=" << *it << endl;
                            templatePath=*it+templatePath;
                            //kdDebug() << "templatePath: " << templatePath << endl;
                        }
                    } else
                        continue; // Invalid
                }
                // The else if and the else branch are here for compat. with the old system
                else if ( files[i].right(4) != ".png" )
                    // Ignore everything that is not a PNG file
                    continue;
                else {
                    // Found a PNG file - the template must be here in the same dir.
                    icon = filePath;
                    TQFileInfo fi(filePath);
                    text = fi.baseName();
                    templatePath = filePath; // Note that we store the .png file as the template !
                    // That's the way it's always been done. Then the app replaces the extension...
                }
                KoTemplate *t=new KoTemplate(text, description, templatePath, icon, fileName,
                                             measureSystem, hidden);
                groupIt.current()->add(t, false, false); // false -> we aren't a "user", false -> don't
                                                         // "touch" the group to avoid useless
                                                         // creation of dirs in .kde/blah/...
                if ( defaultTemplate )
                    m_defaultTemplate = t;
            }
        }
    }
}

void KoTemplateTree::writeTemplate(KoTemplate *t, KoTemplateGroup *group,
                                   const TQString &localDir) {
    TQString fileName;
    if ( t->isHidden() )
    {
        fileName = t->fileName();
        // try to remove the file
        if ( TQFile::remove(fileName) || !TQFile::exists(fileName) )
        {
            TQFile::remove( t->name() );
            TQFile::remove( t->picture() );
            return;
        }
    }
    // be sure that the template's file name is unique so we don't overwrite an other
    TQString const path = localDir + group->name() + '/';
    TQString const name = KoTemplates::stripWhiteSpace( t->name() );
    fileName = path + name + ".desktop";
    if ( t->isHidden() && TQFile::exists(fileName) )
        return;
    TQString fill;
    while ( TDEIO::NetAccess::exists( fileName, true, 0 ) )
    {
        fill += '_';
        fileName = path + fill + name + ".desktop";
    }

    KSimpleConfig config( fileName );
    config.setDesktopGroup();
    config.writeEntry("Type", "Link");
    config.writePathEntry("URL", t->file());
    config.writeEntry("Name", t->name());
    config.writeEntry("Icon", t->picture());
    config.writeEntry("X-TDE-Hidden", t->isHidden());
}

namespace KoTemplates {
TQString stripWhiteSpace(const TQString &string) {

    TQString ret;
    for(unsigned int i=0; i<string.length(); ++i) {
        TQChar tmp(string[i]);
        if(!tmp.isSpace())
            ret+=tmp;
    }
    return ret;
}
}
