#include <tdelocale.h>
#include <tqlayout.h>
#include <klineedit.h>
#include <tqlabel.h>
#include <tqptrlist.h>

#include <kmimetype.h>
#include <kicontheme.h>
#include <tqtooltip.h>
#include <kstringvalidator.h>
#include <tqspinbox.h>
#include <tqhbox.h>
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqcombobox.h>
#include <tqpushbutton.h>
#include <kdialogbase.h>
#include <kdebug.h>
#include <kseparator.h>
#include <tdefilemetainfo.h>
#include <tqvalidator.h>

#include "metatag.h"
#include "edit.h"

Editor::Editor()
:  KDialogBase((TQWidget*)0L, 0, false, i18n("Tag Editor"), Ok | Cancel)
{
    mMainWidget = makeMainWidget();
//    mMainWidget->setMinimumWidth(325);

    mGrid = new TQGridLayout(mMainWidget, 1, 1, 0, spacingHint(), "Editor::mGrid");

    mGrid->setColStretch(1, 1);
    mGrid->setColStretch(2, 1);

    TQHBox *heading = new TQHBox(mMainWidget, "Editor::heading");
    heading->setSpacing(4);
    mFileIcon = new TQLabel(heading);
    mFileIcon->setAlignment(AlignVCenter | AlignLeft);
    mFile = new TQLabel(heading);
    mFile->setAlignment(AlignVCenter | AlignLeft);
    heading->setStretchFactor(mFile, 2);
    mGrid->addMultiCellWidget(heading, 0, 0, 0, 2);

    KSeparator *sep = new KSeparator(KSeparator::HLine, mMainWidget);
    mGrid->addMultiCellWidget(sep, 1, 1, 0, 2);

    mControls.setAutoDelete(true);
    mNextRow = 2;

    connect(this, TQ_SIGNAL(closeClicked()), TQ_SLOT(delayedDestruct()));
    connect(this, TQ_SIGNAL(okClicked()), TQ_SLOT(save()));

    enableButtonSeparator(true);
    setFixedHeight(sizeHint().height());
}

void Editor::open(const PlaylistItem & file)
{
    KFileMetaInfo file_meta_info(file.file(), file.mimetype());
    KFileMetaInfoItem info_item;

    item = file;
    mDirty = false;

    mFile->setText("<nobr><b>" + file.url().fileName(false) + "</b></nobr>");
    TQToolTip::add(mFile, file.url().prettyURL());
    mFileIcon->
        setPixmap(KMimeType::pixmapForURL(file.url(), 0, TDEIcon::Small));

    if (file.url().isLocalFile()) {
        TQFileInfo file_info(file.file());
        mFileWritable = file_info.isWritable();
    }
    else {
        // KFileMetaInfo doesn't work on remote files
        mFileWritable = false;
    }

    if ( !file_meta_info.isValid() ) // go ahead people, nothing to see here
        return;

    mControls.append(createControl(file_meta_info, i18n("&Title"), "Title", TQVariant::String, false, mMainWidget));
    mControls.append(createControl(file_meta_info, i18n("&Artist"), "Artist", TQVariant::String, false, mMainWidget));
    mControls.append(createControl(file_meta_info, i18n("A&lbum"), "Album", TQVariant::String, false, mMainWidget));
    mControls.append(createControl(file_meta_info, i18n("&Date"), "Date", TQVariant::String, false, mMainWidget));
    mControls.append(createControl(file_meta_info, i18n("T&rack"), "Tracknumber", TQVariant::UInt, false, mMainWidget));
    mControls.append(createControl(file_meta_info, i18n("&Genre"), "Genre", TQVariant::String, false, mMainWidget));
    mControls.append(createControl(file_meta_info, i18n("Co&mment"), "Comment", TQVariant::String, false, mMainWidget));
}

void Editor::save()
{
    // Only write to disk if something actually changed
    if (!mDirty)
    {
        delayedDestruct();
        return;
    }

    KFileMetaInfo meta_info(item.file(), item.mimetype());
    if ( !meta_info.isValid() )
    {
        delayedDestruct();
        return;
    }

    for (MetaWidget *meta_widget = mControls.first(); meta_widget; meta_widget = mControls.next() )
        saveControl(meta_info, *meta_widget);

    meta_info.applyChanges();

    emit(saved(item));
    delayedDestruct();
}

void Editor::saveControl(KFileMetaInfo& meta_info, const MetaWidget &meta_widget) {
    TQVariant value;
    const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( meta_info.mimeType() );

     if (!meta_widget.widget->isEnabled())
         return;

     if (meta_widget.widget->inherits("TQSpinBox"))
         value = static_cast<TQSpinBox *>(meta_widget.widget)->value();
     else if (meta_widget.widget->inherits("TQComboBox"))
         value = static_cast<TQComboBox *>(meta_widget.widget)->currentText();
     else if (meta_widget.widget->inherits("TQLineEdit"))
         value = static_cast<TQLineEdit *>(meta_widget.widget)->text();

     TQString group = keyGroup(meta_info, meta_widget.key);

     if (group.isNull()) {
         kdWarning() << "Cannot find group for " << meta_widget.key << endl;
         return;
     }

     if (info->groupInfo(group)->itemInfo(meta_widget.key)) {
        if (info->groupInfo(group)->attributes() & KFileMimeTypeInfo::Addable) {
            kdDebug(66666) << "Adding group " << group << endl;
            if (!meta_info.addGroup(group))
                kdWarning() << "Adding group \"" << group << "\" failed!" << endl;
        }

        if (info->groupInfo(group)->itemInfo(meta_widget.key)->attributes() & KFileMimeTypeInfo::Addable) {
            kdDebug(66666) << "Adding key " << meta_widget.key << endl;
            if (!meta_info.group(group).addItem(meta_widget.key).isValid())
                kdWarning() << "Adding key \"" << meta_widget.key << "\" failed!" << endl;
        }
     }

    if (value.cast(meta_info.item(meta_widget.key).type())) {
        if (!meta_info.item(meta_widget.key).setValue(value))
            kdWarning() << "setValue() failed on " << group << "/" << meta_widget.key << endl;
    }
    else
         kdWarning() << "Cannot save " << meta_widget.key << " as required type." << endl;
}

MetaWidget* Editor::createControl(KFileMetaInfo& meta_info, const TQString &label, const TQString &key, TQVariant::Type default_type, bool optional, TQWidget *parent) {
    TQLabel *tmp_label = 0L;
    KFileMetaInfoItem info_item = meta_info.item(key);
    TQVariant::Type type;
    MetaWidget *meta_widget = 0L;
    TQValidator *validator = 0L;
    TQString groupName = keyGroup(meta_info, key);
    bool known_key = ((!groupName.isNull()) && meta_info.group(groupName).contains(key));
    bool addable = keyAddable(meta_info, key);
    const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( meta_info.mimeType() );

    // This key isn't a real thing, and we can't even create it
    if ((!info_item.isEditable()) && (!addable) && optional)
        return 0;

    if (!groupName.isNull()) {
         type = info->groupInfo(groupName)->itemInfo(key)->type();
    }
    else {
         type = default_type;
    }

    // Get the correct validator
    if ( info && !groupName.isNull() )
        validator = info->createValidator( groupName, key, parent );

    // meta_widget is used for book-keeping internally
    meta_widget = new MetaWidget;
    meta_widget->key = key;

    if ((type == TQVariant::Int) || (type == TQVariant::UInt)) {
        // We're an int, make a spin box
        TQSpinBox *box = new TQSpinBox(parent);

        // Well, normally metatag doesn't care that much about suffixes
        // and prefixes, but this is just too easy.
        box->setPrefix(info_item.prefix());
        box->setSuffix(info_item.suffix());
        // Kinda a hack... display " " instead of "0" (think track numbers)
        box->setSpecialValueText(" ");

        // Do we have a validator?
        if (validator) {
            box->setValidator(validator);

            // Is it an integer validator
            if (validator->inherits("TQIntValidator")) {
                TQIntValidator *int_validator = static_cast<TQIntValidator *>(validator);

                // FIXME: Why the -hell- doesn't TQSpinBox::setValidator() do this??
                box->setMinValue(int_validator->bottom());
                box->setMaxValue(int_validator->top());
            }
        }

        box->setValue(info_item.value().toInt());

        connect(box, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(modified()));
        meta_widget->widget = box;
    }
    else {
        // We're not an int, make a KLineEdit/TQComboBox, depending on our validator
        bool combo_box = false;

        if (validator)
           combo_box = validator->isA("KStringListValidator");

        if (combo_box) {
             TQComboBox *combo = new TQComboBox(parent);

             combo->clear();
             combo->insertStringList(static_cast<
                                     KStringListValidator *
                                     >(validator)->stringList());

             combo->setCurrentText(info_item.value().toString());
             connect(combo, TQ_SIGNAL(activated(int)), this, TQ_SLOT(modified()));

             meta_widget->widget = combo;
        }
        else {
             KLineEdit *edit;

             edit = new KLineEdit(parent);
             edit->setText(info_item.value().toString());
             edit->setValidator(validator);
             connect(edit, TQ_SIGNAL(textChanged(const TQString &)), this, TQ_SLOT(modified()));

             meta_widget->widget = edit;
        }
    }

    if (known_key)
        meta_widget->widget->setEnabled(info_item.isEditable() && mFileWritable);
    else
        meta_widget->widget->setEnabled(addable && mFileWritable);

    mGrid->addMultiCellWidget(meta_widget->widget, mNextRow, mNextRow, 1, 2);

    // Add our label. This is the easy part
    tmp_label = new TQLabel(meta_widget->widget, label + ":", parent);
    mGrid->addWidget(tmp_label, mNextRow, 0);


    mNextRow++;

    return meta_widget;
}

TQString Editor::keyGroup(const KFileMetaInfo& i, TQString key) {
    const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( i.mimeType() );
    TQStringList groups = info->supportedGroups();

    for (TQStringList::Iterator it = groups.begin();it != groups.end();++it) {
        if (info->groupInfo(*it)->itemInfo(key)) {
            return *it;
        }
    }

    return TQString();
}

bool Editor::keyAddable(const KFileMetaInfo &i, TQString key) {
    const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( i.mimeType() );
    TQStringList groups = info->supportedGroups();

    kdDebug(66666) << "Testing if " << key << " is addable" << endl;
    for (TQStringList::Iterator it = groups.begin();it != groups.end();++it) {
        if (info->groupInfo(*it)->supportsVariableKeys()) {
            kdDebug(66666) << "Group " << *it << " supports variable keys" << endl;
            return true;
        }

        if (info->groupInfo(*it)->itemInfo(key)) {
            if (info->groupInfo(*it)->attributes() & KFileMimeTypeInfo::Addable) {
                 kdDebug(66666) << "Group " << *it << " is addable" << endl;
                 return true;
            }

            if (info->groupInfo(*it)->itemInfo(key)->attributes() & KFileMimeTypeInfo::Addable)
                return true;
        }
    }

    return false;
}

void Editor::modified() {
	mDirty = true;
}

#include "edit.moc"
