/*
    Rosegarden
    A MIDI and audio sequencer and musical notation editor.
 
    This program is Copyright 2000-2008
        Guillaume Laurent   <glaurent@telegraph-road.org>,
        Chris Cannam        <cannam@all-day-breakfast.com>,
        Richard Bown        <richard.bown@ferventsoftware.com>
 
    The moral rights of Guillaume Laurent, Chris Cannam, and Richard
    Bown to claim authorship of this work have been asserted.
 
    Other copyrights also apply to some parts of this work.  Please
    see the AUTHORS file and individual file headers for details.
 
    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.  See the file
    COPYING included with this distribution for more information.
*/


#include "KeySignatureDialog.h"

#include <tdelocale.h>
#include "misc/Strings.h"
#include "base/NotationTypes.h"
#include "gui/editors/notation/NotePixmapFactory.h"
#include "gui/widgets/BigArrowButton.h"
#include <kcombobox.h>
#include <kdialogbase.h>
#include <tqbuttongroup.h>
#include <tqgroupbox.h>
#include <tqhbox.h>
#include <tqlabel.h>
#include <tqobject.h>
#include <tqpixmap.h>
#include <tqradiobutton.h>
#include <tqstring.h>
#include <tqtooltip.h>
#include <tqvbox.h>
#include <tqwidget.h>
#include <tqcheckbox.h>
#include <algorithm>

namespace Rosegarden
{

KeySignatureDialog::KeySignatureDialog(TQWidget *parent,
                                       NotePixmapFactory *npf,
                                       Clef clef,
                                       Rosegarden::Key defaultKey,
                                       bool showApplyToAll,
                                       bool showConversionOptions,
                                       TQString explanatoryText) :
        KDialogBase(parent, 0, true, i18n("Key Change"), Ok | Cancel | Help),
        m_notePixmapFactory(npf),
        m_key(defaultKey),
        m_clef(clef),
        m_valid(true),
        m_ignoreComboChanges(false),
        m_explanatoryLabel(0),
        m_applyToAllButton(0),
        m_noPercussionCheckBox(0)
{
    setHelp("nv-signatures-key");

    TQVBox *vbox = makeVBoxMainWidget();

    TQHBox *keyBox = 0;
    TQHBox *nameBox = 0;

    TQGroupBox *keyFrame = new TQGroupBox
                          (1, TQt::Horizontal, i18n("Key signature"), vbox);

    TQGroupBox *transposeFrame = new TQButtonGroup
                                (1, TQt::Horizontal, i18n("Key transposition"), vbox);

    TQGroupBox *buttonFrame = new TQButtonGroup
                             (1, TQt::Horizontal, i18n("Scope"), vbox);

    TQButtonGroup *conversionFrame = new TQButtonGroup
                                    (1, TQt::Horizontal, i18n("Existing notes following key change"), vbox);

    keyBox = new TQHBox(keyFrame);
    nameBox = new TQHBox(keyFrame);

    TQLabel *explanatoryLabel = 0;
    if (!explanatoryText.isNull()) {
        explanatoryLabel = new TQLabel(explanatoryText, keyFrame);
    }

    BigArrowButton *keyDown = new BigArrowButton(keyBox, TQt::LeftArrow);
    TQToolTip::add
        (keyDown, i18n("Flatten"));

    m_keyLabel = new TQLabel(i18n("Key"), keyBox);
    m_keyLabel->setAlignment(AlignVCenter | AlignHCenter);

    BigArrowButton *keyUp = new BigArrowButton(keyBox, TQt::RightArrow);
    TQToolTip::add
        (keyUp, i18n("Sharpen"));

    m_keyCombo = new KComboBox(nameBox);
    m_majorMinorCombo = new KComboBox(nameBox);
    m_majorMinorCombo->insertItem(i18n("Major"));
    m_majorMinorCombo->insertItem(i18n("Minor"));
    if (m_key.isMinor()) {
        m_majorMinorCombo->setCurrentItem(m_majorMinorCombo->count() - 1);
    }

    regenerateKeyCombo();
    redrawKeyPixmap();
    m_explanatoryLabel = explanatoryLabel;

    m_keyLabel->setMinimumWidth(m_keyLabel->pixmap()->width());
    m_keyLabel->setMinimumHeight(m_keyLabel->pixmap()->height());

    m_yesTransposeButton =
        new TQRadioButton(i18n("Transpose key according to segment transposition"),
                         transposeFrame);
    TQRadioButton *noTransposeButton =
        new TQRadioButton(i18n("Use specified key.  Do not transpose"), transposeFrame);
    m_yesTransposeButton->setChecked(true);

    // just to shut up the compiler warning about unused variable:
    noTransposeButton->setChecked(false);

    if (showApplyToAll) {
        TQRadioButton *applyToOneButton =
            new TQRadioButton(i18n("Apply to current segment only"),
                             buttonFrame);
        m_applyToAllButton =
            new TQRadioButton(i18n("Apply to all segments at this time"),
                             buttonFrame);
        applyToOneButton->setChecked(true);
        m_noPercussionCheckBox =
            new TQCheckBox(i18n("Exclude percussion segments"), buttonFrame);
        m_noPercussionCheckBox->setChecked(true);
        
    } else {
        m_applyToAllButton = 0;
        buttonFrame->hide();
    }

    if (showConversionOptions) {
        m_noConversionButton =
            new TQRadioButton
            (i18n("Maintain current pitches"), conversionFrame);
        m_convertButton =
            new TQRadioButton
            (i18n("Maintain current accidentals"), conversionFrame);
        m_transposeButton =
            new TQRadioButton
            (i18n("Transpose into this key"), conversionFrame);
        m_noConversionButton->setChecked(true);
    } else {
        m_noConversionButton = 0;
        m_convertButton = 0;
        m_transposeButton = 0;
        conversionFrame->hide();
    }

    TQObject::connect(keyUp, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotKeyUp()));
    TQObject::connect(keyDown, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotKeyDown()));
    TQObject::connect(m_keyCombo, TQ_SIGNAL(activated(const TQString &)),
                     this, TQ_SLOT(slotKeyNameChanged(const TQString &)));
    TQObject::connect(m_keyCombo, TQ_SIGNAL(textChanged(const TQString &)),
                     this, TQ_SLOT(slotKeyNameChanged(const TQString &)));
    TQObject::connect(m_majorMinorCombo, TQ_SIGNAL(activated(const TQString &)),
                     this, TQ_SLOT(slotMajorMinorChanged(const TQString &)));
}

KeySignatureDialog::ConversionType

KeySignatureDialog::getConversionType() const
{
    if (m_noConversionButton && m_noConversionButton->isChecked()) {
        return NoConversion;
    } else if (m_convertButton && m_convertButton->isChecked()) {
        return Convert;
    } else if (m_transposeButton && m_transposeButton->isChecked()) {
        return Transpose;
    }
    return NoConversion;
}

bool
KeySignatureDialog::shouldApplyToAll() const
{
    return m_applyToAllButton && m_applyToAllButton->isChecked();
}

bool
KeySignatureDialog::shouldBeTransposed() const
{
    return m_yesTransposeButton && m_yesTransposeButton->isChecked();
}

bool
KeySignatureDialog::shouldIgnorePercussion() const
{
    return m_noPercussionCheckBox && m_noPercussionCheckBox->isChecked();
}

void
KeySignatureDialog::slotKeyUp()
{
    bool sharp = m_key.isSharp();
    int ac = m_key.getAccidentalCount();
    if (ac == 0)
        sharp = true;
    if (sharp) {
        if (++ac > 7)
            ac = 7;
    } else {
        if (--ac < 1) {
            ac = 0;
            sharp = true;
        }
    }

    try {
        m_key = Rosegarden::Key(ac, sharp, m_key.isMinor());
        setValid(true);
    } catch (Rosegarden::Key::BadKeySpec s) {
        std::cerr << s.getMessage() << std::endl;
        setValid(false);
    }

    regenerateKeyCombo();
    redrawKeyPixmap();
}

void
KeySignatureDialog::slotKeyDown()
{
    bool sharp = m_key.isSharp();
    int ac = m_key.getAccidentalCount();
    if (ac == 0)
        sharp = false;
    if (sharp) {
        if (--ac < 0) {
            ac = 1;
            sharp = false;
        }
    } else {
        if (++ac > 7)
            ac = 7;
    }

    try {
        m_key = Rosegarden::Key(ac, sharp, m_key.isMinor());
        setValid(true);
    } catch (Rosegarden::Key::BadKeySpec s) {
        std::cerr << s.getMessage() << std::endl;
        setValid(false);
    }

    regenerateKeyCombo();
    redrawKeyPixmap();
}

struct KeyNameComparator
{
    bool operator()(const Rosegarden::Key &k1, const Rosegarden::Key &k2) {
        return (k1.getName() < k2.getName());
    }
};


void
KeySignatureDialog::regenerateKeyCombo()
{
    if (m_explanatoryLabel)
        m_explanatoryLabel->hide();

    m_ignoreComboChanges = true;
    TQString currentText = m_keyCombo->currentText();
    Rosegarden::Key::KeyList keys(Rosegarden::Key::getKeys(m_key.isMinor()));
    m_keyCombo->clear();

    std::sort(keys.begin(), keys.end(), KeyNameComparator());
    bool textSet = false;

    for (Rosegarden::Key::KeyList::iterator i = keys.begin();
            i != keys.end(); ++i) {

        TQString name(strtoqstr(i->getName()));
        int space = name.find(' ');
        if (space > 0)
            name = name.left(space);

        m_keyCombo->insertItem(name);

        if (m_valid && (*i == m_key)) {
            m_keyCombo->setCurrentItem(m_keyCombo->count() - 1);
            textSet = true;
        }
    }

    if (!textSet) {
        m_keyCombo->setEditText(currentText);
    }
    m_ignoreComboChanges = false;
}

bool
KeySignatureDialog::isValid() const
{
    return m_valid;
}

Rosegarden::Key
KeySignatureDialog::getKey() const
{
    return m_key;
}

void
KeySignatureDialog::redrawKeyPixmap()
{
    if (m_valid) {
        TQPixmap pmap =
            NotePixmapFactory::toTQPixmap(m_notePixmapFactory->makeKeyDisplayPixmap(m_key, m_clef));
        m_keyLabel->setPixmap(pmap);
    } else {
        m_keyLabel->setText(i18n("No such key"));
    }
}

void
KeySignatureDialog::slotKeyNameChanged(const TQString &s)
{
    if (m_ignoreComboChanges)
        return ;

    if (m_explanatoryLabel)
        m_explanatoryLabel->hide();

    std::string name(getKeyName(s, m_key.isMinor()));

    try {
        m_key = Rosegarden::Key(name);
        setValid(true);

        int space = name.find(' ');
        if (space > 0)
            name = name.substr(0, space);
        m_keyCombo->setEditText(strtoqstr(name));

    } catch (Rosegarden::Key::BadKeyName s) {
        std::cerr << s.getMessage() << std::endl;
        setValid(false);
    }

    redrawKeyPixmap();
}

void
KeySignatureDialog::slotMajorMinorChanged(const TQString &s)
{
    if (m_ignoreComboChanges)
        return ;

    std::string name(getKeyName(m_keyCombo->currentText(), s == i18n("Minor")));

    try {
        m_key = Rosegarden::Key(name);
        setValid(true);
    } catch (Rosegarden::Key::BadKeyName s) {
        std::cerr << s.getMessage() << std::endl;
        setValid(false);
    }

    regenerateKeyCombo();
    redrawKeyPixmap();
}

void
KeySignatureDialog::setValid(bool valid)
{
    m_valid = valid;
    enableButton(Ok, m_valid);
}

std::string
KeySignatureDialog::getKeyName(const TQString &s, bool minor)
{
    TQString u((s.length() >= 1) ? (s.left(1).upper() + s.right(s.length() - 1))
              : s);

    std::string name(qstrtostr(u));
    name = name + " " + (minor ? "minor" : "major");
    return name;
}

}
#include "KeySignatureDialog.moc"
