/*
    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 of the License, or
    (at your option) any later version.
*/

/*
  Copyright (C) 2007 Eike Hein <hein@kde.org>
*/


#include "skin_settings.h"
#include "skin_settings.moc"
#include "skin_list_item.h"
#include "settings.h"

#include <tqurl.h>
#include <tqheader.h>
#include <tqpixmap.h>
#include <tqlayout.h>
#include <tqpushbutton.h>
#include <tqlabel.h>
#include <tqfile.h>

#include <tdeapplication.h>
#include <tdeglobal.h>
#include <kstandarddirs.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kurl.h>
#include <tdeio/global.h>
#include <tdeio/job.h>
#include <tdeio/netaccess.h>
#include <tdefiledialog.h>
#include <ktar.h>
#include <klineedit.h>
#include <kcolorbutton.h>

#include <unistd.h> // unlink()


SkinSettings::SkinSettings(TQWidget* parent, const char* name, bool translucency)
 : SkinSettingsUI(parent, name)
{
    kcfg_skin->hide();

    skinbgcolor_label->setEnabled(!translucency);
    skinbgcolor_label->setHidden(translucency);

    kcfg_skinbgcolor->setEnabled(!translucency);
    kcfg_skinbgcolor->setHidden(translucency);

    skinbglayout->setEnabled(!translucency);
    skinbglayout->invalidate();

    skins_list->header()->hide();
    skins_list->setSelectionModeExt(TDEListView::Single);
    skins_list->setResizeMode(TDEListView::LastColumn);
    skins_list->setRootIsDecorated(false);
    skins_list->setDragEnabled(false);
    skins_list->setAcceptDrops(false);
    skins_list->setDropVisualizer(false);
    skins_list->setSortColumn(0);

    connect(skins_list, TQ_SIGNAL(selectionChanged()), this, TQ_SLOT(slotUpdateRemoveButton()));
    connect(skins_list, TQ_SIGNAL(selectionChanged()), this, TQ_SLOT(slotUpdateSkinSetting()));
    connect(kcfg_skin, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateSelection(const TQString&)));
    connect(install_button, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotInstallSkin()));
    connect(remove_button, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotRemoveSkin()));

    skins_dir = locateLocal("data", "yakuake/");

    selected = Settings::skin();

    slotPopulate();
}

SkinSettings::~SkinSettings()
{
}

void SkinSettings::showEvent(TQShowEvent* e)
{
    slotPopulate();
    SkinSettingsUI::showEvent(e);
}

void SkinSettings::slotPopulate()
{
    TQStringList skins_dirs;
    TQStringList titles_dirs = TDEGlobal::dirs()->findAllResources("data","yakuake/*/title.skin");
    TQStringList tabs_dirs = TDEGlobal::dirs()->findAllResources("data","yakuake/*/tabs.skin");

    for (TQStringList::Iterator it = titles_dirs.begin(); it != titles_dirs.end(); ++it)
    {
        if (tabs_dirs.contains((*it).section('/', 0, -2) + "/tabs.skin"))
            skins_dirs << (*it).section('/', 0, -2);
    }

    if (skins_dirs.count() > 0)
    {
        skins_list->clear();

        for (TQStringList::Iterator it = skins_dirs.begin(); it != skins_dirs.end(); ++it)
        {
            TQUrl titles_url = locate("appdata", (*it) + "/title.skin");
            TDEConfig titles_config(titles_url.path());
            titles_config.setGroup("Description");

            TQString titles_name(titles_config.readEntry("Skin", ""));
            TQString titles_author(titles_config.readEntry("Author", ""));
            TQString titles_icon_name(titles_config.readEntry("Icon", ""));

            TQUrl tabs_url = locate("appdata", (*it) + "/tabs.skin");
            TDEConfig tabs_config(tabs_url.path());
            tabs_config.setGroup("Description");

            TQString tabs_name(tabs_config.readEntry("Skin", ""));
            TQString tabs_author(tabs_config.readEntry("Author", ""));
            TQString tabs_icon_name(tabs_config.readEntry("Icon", ""));

            TQString skin_name = (*it).section('/', -1, -1);
            TQString skin_fancy_name = i18n("Unnamed");
            TQString skin_author = i18n("Unknown");
            TQString skin_icon_name;
            TQUrl skin_icon_url;
            TQPixmap skin_icon;

            if (!titles_name.isEmpty())
                skin_fancy_name = titles_name;
            else if (!tabs_name.isEmpty())
                skin_fancy_name = tabs_name;

            if (!titles_author.isEmpty())
                skin_author = titles_author;
            else if (!tabs_author.isEmpty())
                skin_author = tabs_author;

            if (!titles_icon_name.isEmpty())
                skin_icon_name = titles_icon_name;
            else if (!tabs_icon_name.isEmpty())
                skin_icon_name = tabs_icon_name;

            skin_icon_url = locate("appdata", (*it) + skin_icon_name);

            if (skin_icon_url.isValid())
                skin_icon.load(skin_icon_url.path());

            bool exists = false;
            TQListViewItemIterator it2(skins_list);

            while (it2.current())
            {
                SkinListItem* item = 0;
                item = static_cast<SkinListItem*>(it2.current());
                if (item && item->name() == skin_name) exists = true;
                ++it2;
            }

            if (!exists)
            {
                SkinListItem* skin = new SkinListItem(skins_list, skin_fancy_name, skin_author, skin_icon, skin_name, (*it));

                if (skin_name == selected) skins_list->setSelected(skin, true);
            }
        }
    }

    slotUpdateRemoveButton();
}

void SkinSettings::slotInstallSkin()
{
    KURL skin_url = KFileDialog::getOpenURL(TQString(),
        i18n("*.tar.gz *.tar.bz2 *.tar *.zip|Yakuake Skins"),
        NULL, i18n("Select Skin Archive"));

    if (skin_url.isEmpty()) return;

    if (!TDEIO::NetAccess::download(skin_url, install_skin_file, NULL))
    {
        KMessageBox::error(0L,
            TDEIO::NetAccess::lastErrorString(),
            i18n("Failed to Download Skin"),
            KMessageBox::Notify
            );

        return;
    }

    TQDir skin_dir(install_skin_file);

    if (!skin_dir.exists())
    {
        TDEIO::ListJob* job = TDEIO::listRecursive("tar:" + install_skin_file, false, false);

        connect(job, TQ_SIGNAL(entries(TDEIO::Job*, const TDEIO::UDSEntryList&)),
            this, TQ_SLOT(slotListSkinArchive(TDEIO::Job*, const TDEIO::UDSEntryList&)));

        connect(job, TQ_SIGNAL(result(TDEIO::Job*)),
            this, TQ_SLOT(slotValidateSkinArchive(TDEIO::Job*)));
    }
    else
        failInstall(i18n("The installer was given a directory, not a file."));
}

void SkinSettings::slotListSkinArchive(TDEIO::Job* /* job */, const TDEIO::UDSEntryList& list)
{
    TDEIO::UDSEntryList::const_iterator it = list.begin();

    for(; it != list.end(); ++it)
    {
        TDEIO::UDSEntry::const_iterator itUSDEntry = (*it).begin();

        for (; itUSDEntry != (*it).end(); ++itUSDEntry )
        {
            if((*itUSDEntry).m_uds == TDEIO::UDS_NAME)
            {
                install_skin_file_list.append((*itUSDEntry).m_str);
            }
        }
    }
}

void SkinSettings::slotValidateSkinArchive(TDEIO::Job* job)
{
    if (!job->error())
    {
        install_skin_name = install_skin_file_list.first();

        if (install_skin_file_list.contains(TQString(install_skin_name + "/title.skin"))
            && install_skin_file_list.contains(TQString(install_skin_name + "/tabs.skin")))
        {
            checkForExistingSkin();
        }
        else
            failInstall(i18n("Unable to locate required files in the skin archive.\n\n The archive appears to be invalid."));
    }
    else
        failInstall(i18n("Unable to list the skin archive contents.") + TQString("\n\n %1").arg(job->errorString()));
}

void SkinSettings::checkForExistingSkin()
{
    bool exists = false;
    SkinListItem* item = 0;

    TQListViewItemIterator it(skins_list);

    while (it.current())
    {
        item = static_cast<SkinListItem*>(it.current());
        if (item && item->name() == install_skin_name) exists = true;
        ++it;
    }

    if (exists)
    {
        TQFile skin(item->dir() + "/titles.skin");

        if (!skin.open(IO_ReadOnly | IO_WriteOnly))
        {
            failInstall(i18n("This skin appears to be already installed and you lack the required permissions to overwrite it."));
        }
        else
        {
            skin.close();

            int remove = KMessageBox::warningContinueCancel(0L,
            i18n("This skin appears to be already installed. Do you want to overwrite it?"),
            i18n("Skin Already Exists"),
            i18n("Reinstall Skin"));

            if (remove == KMessageBox::Continue)
            {
                unlink(TQFile::encodeName(item->dir()));
                TDEIO::DeleteJob* job = TDEIO::del(KURL(item->dir()), false, false);
                connect(job, TQ_SIGNAL(result(TDEIO::Job*)), this, TQ_SLOT(slotInstallSkinArchive(TDEIO::Job*)));
            }
            else
                cleanupAfterInstall();
        }
    }
    else
        slotInstallSkinArchive();
}

void SkinSettings::slotInstallSkinArchive(TDEIO::Job* delete_job)
{
    if (delete_job && delete_job->error())
    {
        KMessageBox::error(0L,
            delete_job->errorString(),
            i18n("Could Not Delete Skin"),
            KMessageBox::Notify
            );

        return;
    }

    KTar skin_archive(install_skin_file);

    if (skin_archive.open(IO_ReadOnly))
    {
        const KArchiveDirectory* skin_dir = skin_archive.directory();
        skin_dir->copyTo(skins_dir);
        skin_archive.close();

        slotPopulate();

        if (Settings::skin() == install_skin_name)
            emit settingsChanged();

        cleanupAfterInstall();
    }
    else
        failInstall(i18n("The skin archive file could not be opened."));
}

void SkinSettings::failInstall(const TQString& error)
{
    KMessageBox::error(0L, error,
        i18n("Cannot Install Skin"),
        KMessageBox::Notify
        );

    cleanupAfterInstall();
}

void SkinSettings::cleanupAfterInstall()
{
    TDEIO::NetAccess::removeTempFile(install_skin_file);
    install_skin_file = TQString();
    install_skin_name = TQString();
    install_skin_file_list.clear();
}

void SkinSettings::slotRemoveSkin()
{
    if (skins_list->childCount() <= 1)
        return;

    SkinListItem* selected_item = static_cast<SkinListItem*>(skins_list->selectedItem());

    if (!selected_item) return;

    int remove = KMessageBox::warningContinueCancel(0L,
        i18n("Do you want to remove \"%1\" by %2?").arg(selected_item->text(0)).arg(selected_item->author()),
        i18n("Remove Skin"),
        KStdGuiItem::del());

    if (remove == KMessageBox::Continue)
    {
        unlink(TQFile::encodeName(selected_item->dir()));
        TDEIO::DeleteJob* job = TDEIO::del(KURL(selected_item->dir()), false, false);
        connect(job, TQ_SIGNAL(result(TDEIO::Job *)), this, TQ_SLOT(slotPopulate()));

        if (selected_item->name() == Settings::skin())
        {
            Settings::setSkin("default");
            Settings::writeConfig();
            emit settingsChanged();
        }

        slotResetSelection();
    }
}

void SkinSettings::slotUpdateRemoveButton()
{
    if (skins_list->childCount() <= 1)
    {
        remove_button->setEnabled(false);
        return;
    }

    SkinListItem* selected_item = static_cast<SkinListItem*>(skins_list->selectedItem());

    if (!selected_item) return;

    if (selected_item->name() == "default")
    {
        remove_button->setEnabled(false);
        return;
    }

    TQFile skin(selected_item->dir() + "/title.skin");

    if (!skin.open(IO_ReadOnly | IO_WriteOnly))
        remove_button->setEnabled(false);
    else
        remove_button->setEnabled(true);

    skin.close();
}

void SkinSettings::slotUpdateSkinSetting()
{
    SkinListItem* selected_item = static_cast<SkinListItem*>(skins_list->selectedItem());

    if (selected_item)
    {
        selected = selected_item->name();
        kcfg_skin->setText(selected_item->name());
    }
}

void SkinSettings::slotResetSelection()
{
    selected = Settings::skin();
}

void SkinSettings::slotUpdateSelection(const TQString& selection)
{
    selected = selection;
    TQListViewItemIterator it(skins_list);

    while (it.current())
    {
        SkinListItem* skin = 0;
        skin = static_cast<SkinListItem*>(it.current());

        if (skin && skin->name() == selected)
            skins_list->setSelected(skin, true);

        ++it;
    }
}
