/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2009-05-22
 * Description : a control widget for the AdvancedRename utility
 *
 * Copyright (C) 2009-2010 by Andi Clemens <andi dot clemens at gmx dot net>
 *
 * 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.
 *
 * ============================================================ */

#include "advancedrenamewidget.moc"

// Qt includes

#include <QAction>
#include <QGridLayout>
#include <QMenu>
#include <QPushButton>
#include <QRegExp>
#include <QToolButton>

// KDE includes

#include <kconfig.h>
#include <kconfiggroup.h>
#include <kicon.h>
#include <kiconloader.h>
#include <klocale.h>

// LibKDcraw includes

#include <libkdcraw/rexpanderbox.h>

// Local includes

#include "advancedrenameinput.h"
#include "defaultrenameparser.h"
#include "dynamiclayout.h"
#include "tooltipcreator.h"
#include "tooltipdialog.h"

using namespace KDcrawIface;

namespace Digikam
{

class AdvancedRenameWidgetPriv
{
    typedef AdvancedRenameWidget::ControlWidgets CWMask;
    typedef AdvancedRenameWidget::LayoutStyle    LStyle;

public:

    AdvancedRenameWidgetPriv() :
        configGroupName("AdvancedRename Widget"),
        configExpandedStateEntry("Options are expanded"),
        configExpandedStateDefault(true),

        tooltipToggleButton(0),
        modifiersToolButton(0),
        optionsButton(0),
        modifiersButton(0),
        btnContainer(0),
        tooltipDialog(0),
        renameInput(0),
        parser(0),
        optionsLabel(0),
        controlWidgetsMask(AdvancedRenameWidget::DefaultControls),
        layoutStyle(AdvancedRenameWidget::LayoutNormal)
    {}

    const QString        configGroupName;
    const QString        configExpandedStateEntry;
    bool                 configExpandedStateDefault;

    QToolButton*         tooltipToggleButton;
    QToolButton*         modifiersToolButton;

    QPushButton*         optionsButton;
    QPushButton*         modifiersButton;

    QWidget*             btnContainer;

    TooltipDialog*       tooltipDialog;
    AdvancedRenameInput* renameInput;
    Parser*              parser;
    RLabelExpander*      optionsLabel;

    CWMask               controlWidgetsMask;
    LStyle               layoutStyle;
};

AdvancedRenameWidget::AdvancedRenameWidget(QWidget* parent)
                    : QWidget(parent), d(new AdvancedRenameWidgetPriv)
{
    setupWidgets();
}

AdvancedRenameWidget::~AdvancedRenameWidget()
{
    writeSettings();
    delete d;
}

QString AdvancedRenameWidget::parseString() const
{
    return d->renameInput->text();
}

void AdvancedRenameWidget::setParseString(const QString& text)
{
    d->renameInput->setText(text);
}

void AdvancedRenameWidget::clearParseString()
{
    d->renameInput->slotClearText();
}

void AdvancedRenameWidget::clear()
{
    d->renameInput->slotClearTextAndHistory();
}

QString AdvancedRenameWidget::parse(ParseSettings& settings) const
{
    if (!d->parser)
    {
        return QString();
    }

    settings.parseString = d->renameInput->text();

    QString parsed;
    parsed = d->parser->parse(settings);

    return parsed;
}

void AdvancedRenameWidget::createToolTip()
{
    d->tooltipDialog->clearTooltip();

    if (d->parser)
    {
        d->tooltipDialog->setTooltip(TooltipCreator::getInstance().tooltip(d->parser));
    }
}

void AdvancedRenameWidget::slotToolTipButtonToggled(bool checked)
{
    Q_UNUSED(checked)
    if (!d->tooltipDialog->isVisible())
    {
        d->tooltipDialog->show();
    }
    d->tooltipDialog->raise();
}

void AdvancedRenameWidget::setLayoutStyle(LayoutStyle style)
{
    d->layoutStyle = style;
    calculateLayout();
}

void AdvancedRenameWidget::setControlWidgets(ControlWidgets mask)
{
    // we need a parser and at least one renaming option to successfully use
    // this widget.
    bool enable = d->parser && !(d->parser->options().isEmpty());

    // enable the modifier toolbutton if environment has been set up correctly
    bool enableModBtn = enable && !(d->parser->modifiers().isEmpty());

    d->renameInput->setEnabled(enable);
    d->tooltipToggleButton->setVisible(enable && (mask & ToolTipButton));

    // layout specific
    d->optionsLabel->setVisible(enable && (mask & TokenButtons) && (d->layoutStyle == LayoutNormal));
    d->optionsButton->setVisible(enableModBtn && (mask & TokenButtons) && (d->layoutStyle == LayoutCompact));
    d->modifiersButton->setVisible(enableModBtn && (mask & ModifierToolButton) && (d->layoutStyle == LayoutCompact));
    d->modifiersToolButton->setVisible(enableModBtn && (mask & ModifierToolButton) && (d->layoutStyle == LayoutNormal));

    d->controlWidgetsMask = mask;
}

template<class T>
QMenu* AdvancedRenameWidget::createControlsMenu(QList<T*>& list)
{
    QMenu* menu     = new QMenu();
    QAction* action = 0;

    foreach (T* ctrl, list)
    {
        action = ctrl->registerMenu(menu);
        if (!action)
        {
            continue;
        }

        connect(ctrl, SIGNAL(signalTokenTriggered(const QString&)),
                d->renameInput, SLOT(slotAddToken(const QString&)));
    }

    return menu;
}

void AdvancedRenameWidget::registerParserControls()
{
   if (d->parser)
   {
       setupWidgets();

       QList<Option*> optionsList     = d->parser->options();
       QList<Modifier*> modifiersList = d->parser->modifiers();

       switch (d->layoutStyle)
       {
           case LayoutCompact:
           {
               // register options
               QMenu* optionsMenu   = createControlsMenu(optionsList);
               d->optionsButton->setMenu(optionsMenu);

               // register modifiers
               QMenu* modifiersMenu = createControlsMenu(modifiersList);
               d->modifiersButton->setMenu(modifiersMenu);
               break;
           }
           default:
           {
               // register options
               QPushButton* btn      = 0;
               DynamicLayout* layout = new DynamicLayout(KDialog::marginHint(), KDialog::marginHint());
               foreach (Option* option, d->parser->options())
               {
                   btn = option->registerButton(this);

                   if (!btn)
                   {
                       continue;
                   }
                   // set button tooltip
                   btn->setToolTip(option->description());

                   layout->addWidget(btn);

                   connect(option, SIGNAL(signalTokenTriggered(const QString&)),
                           d->renameInput, SLOT(slotAddToken(const QString&)));
               }
               d->btnContainer->setLayout(layout);
               setMinimumWidth(d->btnContainer->layout()->sizeHint().width());

               // register modifiers
               QMenu* modifiersMenu = createControlsMenu(modifiersList);
               d->modifiersToolButton->setMenu(modifiersMenu);
               break;
           }
       }

       // --------------------------------------------------------

       d->renameInput->setParser(d->parser);
       createToolTip();
   }
}

Parser* AdvancedRenameWidget::parser()
{
    return d->parser;
}

void AdvancedRenameWidget::setParser(Parser* parser)
{
    if (!parser)
    {
        return;
    }

    d->parser = parser;

    calculateLayout();
}

void AdvancedRenameWidget::calculateLayout()
{
    registerParserControls();
    setControlWidgets(d->controlWidgetsMask);
}

void AdvancedRenameWidget::setupWidgets()
{
    /*
     * This methods needs to delete all main widgets, do not remove the delete lines!
     * If a new parser is set or the layout has changed, we need to call setupWidgets() again.
     * So any widget that is created in here needs to be removed first, to avoid memory leaks and
     * duplicate signal/slot connections.
     */
    delete d->tooltipDialog;
    d->tooltipDialog = new TooltipDialog(this);
    d->tooltipDialog->resize(650, 530);

    delete d->renameInput;
    d->renameInput = new AdvancedRenameInput;
    d->renameInput->setToolTip(i18n("<p>Enter your renaming pattern here. Use the access buttons to quickly add renaming "
                                    "options and modifiers. For further explanations, use the information toolbutton.</p>"));

    // --------------------------------------------------------

    delete d->tooltipToggleButton;
    d->tooltipToggleButton = new QToolButton;
    d->tooltipToggleButton->setIcon(SmallIcon("dialog-information"));
    d->tooltipToggleButton->setToolTip(i18n("Show a list of all available options"));

    // --------------------------------------------------------

    delete d->btnContainer;
    d->btnContainer = new QWidget(this);

    delete d->optionsLabel;
    d->optionsLabel = new RLabelExpander(this);
    d->optionsLabel->setText(i18n("Renaming Options"));
    d->optionsLabel->setWidget(d->btnContainer);
    d->optionsLabel->setLineVisible(false);

    // --------------------------------------------------------

    delete d->optionsButton;
    d->optionsButton = new QPushButton;
    d->optionsButton->setText(i18n("Options"));
    d->optionsButton->setIcon(SmallIcon("configure"));
    d->optionsButton->setToolTip(i18n("<p>Add renaming options to the parse string.</p>"));

    // --------------------------------------------------------

    QString modifiersStr     = i18n("Modifiers");
    QString modifiersTooltip = i18n("<p>Add a modifier to a renaming option. "
                                    "To activate this button, place the cursor behind a renaming option "
                                    "or an already assigned modifier.</p>");
    QPixmap modifiersIcon    = SmallIcon("document-edit");

    delete d->modifiersButton;
    d->modifiersButton = new QPushButton;
    d->modifiersButton->setText(modifiersStr);
    d->modifiersButton->setIcon(modifiersIcon);
    d->modifiersButton->setToolTip(modifiersTooltip);

    delete d->modifiersToolButton;
    d->modifiersToolButton = new QToolButton;
    d->modifiersToolButton->setPopupMode(QToolButton::InstantPopup);
    d->modifiersToolButton->setText(modifiersStr);
    d->modifiersToolButton->setIcon(modifiersIcon);
    d->modifiersToolButton->setToolTip(modifiersTooltip);

    // --------------------------------------------------------

    delete layout();
    QGridLayout* mainLayout = new QGridLayout;

    switch (d->layoutStyle)
    {
        case LayoutCompact:
            mainLayout->addWidget(d->renameInput,           0, 0, 1,-1);
            mainLayout->addWidget(d->optionsButton,         1, 0, 1, 1);
            mainLayout->addWidget(d->modifiersButton,       1, 1, 1, 1);
            mainLayout->addWidget(d->tooltipToggleButton,   1, 3, 1, 1);
            mainLayout->setColumnStretch(2, 10);
            break;
        default:
            mainLayout->addWidget(d->renameInput,           0, 0, 1, 1);
            mainLayout->addWidget(d->modifiersToolButton,   0, 1, 1, 1);
            mainLayout->addWidget(d->tooltipToggleButton,   0, 2, 1, 1);
            mainLayout->addWidget(d->optionsLabel,          1, 0, 1,-1);
            mainLayout->setColumnStretch(0, 10);
            break;
    }
    mainLayout->setMargin(0);
    mainLayout->setSpacing(KDialog::marginHint());
    setLayout(mainLayout);

    // --------------------------------------------------------

    connect(d->tooltipToggleButton, SIGNAL(clicked(bool)),
            this, SLOT(slotToolTipButtonToggled(bool)));

    connect(d->renameInput, SIGNAL(signalTextChanged(const QString&)),
            this, SIGNAL(signalTextChanged(const QString&)));

    connect(d->renameInput, SIGNAL(signalTokenMarked(bool)),
            this, SLOT(slotTokenMarked(bool)));

    connect(d->renameInput, SIGNAL(signalReturnPressed()),
            this, SIGNAL(signalReturnPressed()));

    slotTokenMarked(false);
    readSettings();
}

void AdvancedRenameWidget::slotTokenMarked(bool marked)
{
    bool enable    = marked && d->parser;
    bool enableMod = enable && !(d->parser->modifiers().isEmpty());
    d->modifiersButton->setEnabled(enableMod);
    d->modifiersToolButton->setEnabled(enableMod);
}

void AdvancedRenameWidget::focusLineEdit()
{
    d->renameInput->slotSetFocus();
}

void AdvancedRenameWidget::highlightLineEdit()
{
    d->renameInput->slotHighlightLineEdit();
}

void AdvancedRenameWidget::highlightLineEdit(const QString& word)
{
    d->renameInput->slotHighlightLineEdit(word);
}

void AdvancedRenameWidget::readSettings()
{
    KSharedConfig::Ptr config = KGlobal::config();
    KConfigGroup group        = config->group(d->configGroupName);
    d->optionsLabel->setExpanded(group.readEntry(d->configExpandedStateEntry, d->configExpandedStateDefault));
}

void AdvancedRenameWidget::writeSettings()
{
    KSharedConfig::Ptr config = KGlobal::config();
    KConfigGroup group        = config->group(d->configGroupName);

    // remove duplicate entries and save pattern history, omit empty strings
    QString pattern = d->renameInput->text();
    group.writeEntry(d->configExpandedStateEntry, d->optionsLabel
                                                  ? d->optionsLabel->isExpanded()
                                                  : d->configExpandedStateDefault);
}

}  // namespace Digikam
