// -*- c-basic-offset: 4 -*-

/*
    Rosegarden-4
    A sequencer and musical notation editor.

    This program is Copyright 2000-2006
        Guillaume Laurent   <glaurent@telegraph-road.org>,
        Chris Cannam        <cannam@all-day-breakfast.com>,
        Richard Bown        <bownie@bownie.com>

    The moral right of the authors to claim authorship of this work
    has been asserted.

    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 <kstdaction.h>
#include <kaction.h>

#include <qvbox.h>
#include <qlayout.h>
#include <qtooltip.h>
#include <qlineedit.h>
#include <qspinbox.h>

#include "controleditor.h"
#include "rosegardenguidoc.h"
#include "rosedebug.h"
#include "rosestrings.h"
#include "studiocommands.h"
#include "widgets.h"

#include "MidiTypes.h"

const QString notShowing(i18n("<not showing>"));

ControlEditorDialog::ControlEditorDialog(QWidget *parent,
                                         RosegardenGUIDoc *doc,
					 Rosegarden::DeviceId device):
    KMainWindow(parent, "controleditordialog"),
    m_studio(&doc->getStudio()),
    m_doc(doc),
    m_device(device),
    m_modified(false)
{
    RG_DEBUG << "ControlEditorDialog::ControlEditorDialog: device is " << m_device << endl;

    QVBox* mainFrame = new QVBox(this);
    setCentralWidget(mainFrame);

    setCaption(i18n("Manage Control Events"));

    QString deviceName(i18n("<no device>"));
    Rosegarden::MidiDevice *md =
	dynamic_cast<Rosegarden::MidiDevice *>(m_studio->getDevice(m_device));
    if (md) deviceName = strtoqstr(md->getName());

    // spacing hack!
    new QLabel("", mainFrame);
    new QLabel(i18n("  Control Events for %1 (device %2)").arg(deviceName).
	       arg(device), mainFrame);
    new QLabel("", mainFrame);

    m_listView = new KListView(mainFrame);
    m_listView->addColumn(i18n("Control Event name  "));
    m_listView->addColumn(i18n("Control Event type  "));
    m_listView->addColumn(i18n("Control Event value  "));
    m_listView->addColumn(i18n("Description  "));
    m_listView->addColumn(i18n("Min  "));
    m_listView->addColumn(i18n("Max  "));
    m_listView->addColumn(i18n("Default  "));
    m_listView->addColumn(i18n("Color  "));
    m_listView->addColumn(i18n("Position on instrument panel"));

    m_listView->setColumnAlignment(0, Qt::AlignLeft);

    // Align remaining columns centrally
    for (int i = 1; i < 9; ++i)
        m_listView->setColumnAlignment(i, Qt::AlignHCenter);

    m_listView->restoreLayout(kapp->config(), ControlEditorConfigGroup);

    QFrame* btnBox = new QFrame(mainFrame);

    btnBox->setSizePolicy(
            QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));

    QHBoxLayout* layout = new QHBoxLayout(btnBox, 4, 10);

    m_addButton = new QPushButton(i18n("Add"), btnBox);
    m_deleteButton = new QPushButton(i18n("Delete"), btnBox);

    m_closeButton = new QPushButton(i18n("Close"), btnBox);

    QToolTip::add(m_addButton,
                  i18n("Add a Control Parameter to the Studio"));

    QToolTip::add(m_deleteButton,
                  i18n("Delete a Control Parameter from the Studio"));

    QToolTip::add(m_closeButton,
                  i18n("Close the Control Parameter editor"));

    layout->addStretch(10);
    layout->addWidget(m_addButton);
    layout->addWidget(m_deleteButton);
    layout->addSpacing(30);

    layout->addWidget(m_closeButton);
    layout->addSpacing(5);

    connect(m_addButton, SIGNAL(released()),
            SLOT(slotAdd()));

    connect(m_deleteButton, SIGNAL(released()),
            SLOT(slotDelete()));

    setupActions();

    m_doc->getCommandHistory()->attachView(actionCollection());
    connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
            this, SLOT(slotUpdate()));

    connect(m_listView, SIGNAL(doubleClicked(QListViewItem *)),
            SLOT(slotEdit(QListViewItem *)));

    // Highlight all columns - enable extended selection mode
    //
    m_listView->setAllColumnsShowFocus(true);
    m_listView->setSelectionMode(QListView::Extended);

    initDialog();

    setAutoSaveSettings(ControlEditorConfigGroup, true);
}


ControlEditorDialog::~ControlEditorDialog()
{
    RG_DEBUG << "\n*** ControlEditorDialog::~ControlEditorDialog\n" << endl;

    m_listView->saveLayout(kapp->config(), ControlEditorConfigGroup);

    if (m_doc)
        m_doc->getCommandHistory()->detachView(actionCollection());
}

void 
ControlEditorDialog::initDialog()
{
    RG_DEBUG << "ControlEditorDialog::initDialog" << endl;
    slotUpdate();
}

void
ControlEditorDialog::slotUpdate()
{
    RG_DEBUG << "ControlEditorDialog::slotUpdate" << endl;

    //QPtrList<QListViewItem> selection = m_listView->selectedItems();

    Rosegarden::MidiDevice *md =
	dynamic_cast<Rosegarden::MidiDevice *>(m_studio->getDevice(m_device));
    if (!md) return;

    Rosegarden::ControlList::const_iterator it = md->beginControllers();
    QListViewItem *item;
    int i = 0;

    m_listView->clear();

    for (; it != md->endControllers(); ++it)
    {
        Rosegarden::Composition &comp =  m_doc->getComposition();

        QString colour = 
            strtoqstr(comp.getGeneralColourMap().getNameByIndex(it->getColourIndex()));

        if (colour == "") colour = i18n("<default>");

        QString position = QString("%1").arg(it->getIPBPosition());
        if (position.toInt() == -1) position = notShowing;

        QString value;
        value.sprintf("%d (0x%x)", it->getControllerValue(),
                                 it->getControllerValue());

        if (it->getType() == Rosegarden::PitchBend::EventType)
        {
            item = new ControlParameterItem(i++,
                                            m_listView,
                                            strtoqstr(it->getName()),
                                            strtoqstr(it->getType()),
                                            QString("-"),
                                            strtoqstr(it->getDescription()),
                                            QString("%1").arg(it->getMin()),
                                            QString("%1").arg(it->getMax()),
                                            QString("%1").arg(it->getDefault()),
                                            colour,
                                            position);
        }
        else
        {
            item = new ControlParameterItem(i++,
                                            m_listView,
                                            strtoqstr(it->getName()),
                                            strtoqstr(it->getType()),
                                            value,
                                            strtoqstr(it->getDescription()),
                                            QString("%1").arg(it->getMin()),
                                            QString("%1").arg(it->getMax()),
                                            QString("%1").arg(it->getDefault()),
                                            colour,
                                            position);
        }


        // create and set a colour pixmap
        //
        QPixmap colourPixmap(16, 16);
        Rosegarden::Colour c = comp.getGeneralColourMap().getColourByIndex(it->getColourIndex());
        colourPixmap.fill(QColor(c.getRed(), c.getGreen(), c.getBlue()));
        item->setPixmap(7, colourPixmap);

        m_listView->insertItem(item);
    }

    if (m_listView->childCount() == 0)
    {
        QListViewItem *item = new QListViewItem(m_listView, i18n("<none>"));
        m_listView->insertItem(item);

        m_listView->setSelectionMode(QListView::NoSelection);
    }
    else
    {
        m_listView->setSelectionMode(QListView::Extended);
    }


}
/*
void 
ControlEditorDialog::slotEditCopy()
{
    RG_DEBUG << "ControlEditorDialog::slotEditCopy" << endl;
}

void
ControlEditorDialog::slotEditPaste()
{
    RG_DEBUG << "ControlEditorDialog::slotEditPaste" << endl;
}
*/
void
ControlEditorDialog::slotAdd()
{
    RG_DEBUG << "ControlEditorDialog::slotAdd to device " << m_device << endl;

    AddControlParameterCommand *command =
        new AddControlParameterCommand(m_studio, m_device,
				       Rosegarden::ControlParameter());

    addCommandToHistory(command);
}


void
ControlEditorDialog::slotDelete()
{
    RG_DEBUG << "ControlEditorDialog::slotDelete" << endl;

    if (!m_listView->currentItem()) return;

    ControlParameterItem *item = 
        dynamic_cast<ControlParameterItem*>(m_listView->currentItem());

    if (item)
    {
        RemoveControlParameterCommand *command =
            new RemoveControlParameterCommand(m_studio, m_device, item->getId());

        addCommandToHistory(command);
    }
}

void
ControlEditorDialog::slotClose()
{
    RG_DEBUG << "ControlEditorDialog::slotClose" << endl;

    if (m_doc) m_doc->getCommandHistory()->detachView(actionCollection());
    m_doc = 0;

    close();
}

void
ControlEditorDialog::setupActions()
{
    KAction* close = KStdAction::close(this,
                                       SLOT(slotClose()),
                                       actionCollection());

    m_closeButton->setText(close->text());
    connect(m_closeButton, SIGNAL(released()), this, SLOT(slotClose()));

    // some adjustments
    new KToolBarPopupAction(i18n("Und&o"),
                            "undo",
                            KStdAccel::key(KStdAccel::Undo),
                            actionCollection(),
                            KStdAction::stdName(KStdAction::Undo));

    new KToolBarPopupAction(i18n("Re&do"),
                            "redo",
                            KStdAccel::key(KStdAccel::Redo),
                            actionCollection(),
                            KStdAction::stdName(KStdAction::Redo));

    createGUI("controleditor.rc");
}

void 
ControlEditorDialog::addCommandToHistory(KCommand *command)
{
    getCommandHistory()->addCommand(command);
    setModified(false);
}

MultiViewCommandHistory* 
ControlEditorDialog::getCommandHistory()
{
    return m_doc->getCommandHistory();
}


void
ControlEditorDialog::setModified(bool modified)
{
    RG_DEBUG << "ControlEditorDialog::setModified(" << modified << ")" << endl;

    if (modified)
    {
    }
    else
    {
    }

    m_modified = modified;
}

void
ControlEditorDialog::checkModified()
{
    RG_DEBUG << "ControlEditorDialog::checkModified(" << m_modified << ")" 
             << endl;

}


void
ControlEditorDialog::slotEdit()
{
}

void
ControlEditorDialog::slotEdit(QListViewItem *i)
{
    RG_DEBUG << "ControlEditorDialog::slotEdit" << endl;

    ControlParameterItem *item = 
        dynamic_cast<ControlParameterItem*>(i);

    Rosegarden::MidiDevice *md =
	dynamic_cast<Rosegarden::MidiDevice *>(m_studio->getDevice(m_device));

    if (item && md)
    {
        ControlParameterEditDialog dialog
	    (this,
	     md->getControlParameter(item->getId()), m_doc);

        if (dialog.exec() == QDialog::Accepted)
        {
            ModifyControlParameterCommand *command =
                new ModifyControlParameterCommand(m_studio,
						  m_device,
						  dialog.getControl(),
						  item->getId());

            addCommandToHistory(command);
        }
    }
}

void
ControlEditorDialog::closeEvent(QCloseEvent *e)
{
    emit closing();
    KMainWindow::closeEvent(e);
}

// Reset the document
//
void
ControlEditorDialog::setDocument(RosegardenGUIDoc *doc)
{
    // reset our pointers
    m_doc = doc;
    m_studio = &doc->getStudio();
    m_modified = false;

    slotUpdate();
}


const char* const ControlEditorDialog::ControlEditorConfigGroup = "Control Editor";


// ----------------- ControlParameterEditDialog ---------------
//
//

ControlParameterEditDialog::ControlParameterEditDialog(
            QWidget *parent,
            Rosegarden::ControlParameter *control,
            RosegardenGUIDoc *doc):
    KDialogBase(parent, 0, true,
                i18n("Edit Control Parameter"), Ok | Cancel),
    m_doc(doc),
    m_control(control)
{
    m_dialogControl = *control; // copy in the ControlParameter

    QVBox *vbox = makeVBoxMainWidget();

    QGroupBox *groupBox = new QGroupBox
        (1, Horizontal, i18n("Control Event Properties"), vbox);

    QFrame *frame = new QFrame(groupBox);

    QGridLayout *layout = new QGridLayout(frame, 4, 3, 10, 5);

    layout->addWidget(new QLabel(i18n("Name:"), frame), 0, 0);
    m_nameEdit = new QLineEdit(frame);
    layout->addWidget(m_nameEdit, 0, 1);

    layout->addWidget(new QLabel(i18n("Type:"), frame), 1, 0);
    m_typeCombo = new KComboBox(frame);
    layout->addMultiCellWidget(m_typeCombo, 1, 1, 1, 2);

    layout->addWidget(new QLabel(i18n("Description:"), frame), 2, 0);
    m_description = new QLineEdit(frame);
    layout->addMultiCellWidget(m_description, 2, 2, 1, 2);

    // hex value alongside decimal value
    m_hexValue = new QLabel(frame);
    layout->addWidget(m_hexValue, 3, 1);

    layout->addWidget(new QLabel(i18n("Control Event value:"), frame), 3, 0);
    m_controllerBox = new QSpinBox(frame);
    layout->addWidget(m_controllerBox, 3, 2);

    layout->addWidget(new QLabel(i18n("Minimum value:"), frame), 4, 0);
    m_minBox = new QSpinBox(frame);
    layout->addMultiCellWidget(m_minBox, 4, 4, 1, 2);

    layout->addWidget(new QLabel(i18n("Maximum value:"), frame), 5, 0);
    m_maxBox = new QSpinBox(frame);
    layout->addMultiCellWidget(m_maxBox, 5, 5, 1, 2);

    layout->addWidget(new QLabel(i18n("Default value:"), frame), 6, 0);
    m_defaultBox = new QSpinBox(frame);
    layout->addMultiCellWidget(m_defaultBox, 6, 6, 1, 2);

    layout->addWidget(new QLabel(i18n("Color:"), frame), 7, 0);
    m_colourCombo = new KComboBox(frame);
    layout->addMultiCellWidget(m_colourCombo, 7, 7, 1, 2);

    layout->addWidget(new QLabel(i18n("Instrument Parameter Box position:"), frame), 8, 0);
    m_ipbPosition = new KComboBox(frame);
    layout->addMultiCellWidget(m_ipbPosition, 8, 8, 1, 2);

    connect(m_nameEdit, SIGNAL(textChanged(const QString&)),
            SLOT(slotNameChanged(const QString&)));

    connect(m_typeCombo, SIGNAL(activated(int)),
            SLOT(slotTypeChanged(int)));

    connect(m_description, SIGNAL(textChanged(const QString&)),
            SLOT(slotDescriptionChanged(const QString &)));

    connect(m_controllerBox, SIGNAL(valueChanged(int)),
            SLOT(slotControllerChanged(int)));

    connect(m_minBox, SIGNAL(valueChanged(int)),
            SLOT(slotMinChanged(int)));

    connect(m_maxBox, SIGNAL(valueChanged(int)),
            SLOT(slotMaxChanged(int)));

    connect(m_defaultBox, SIGNAL(valueChanged(int)),
            SLOT(slotDefaultChanged(int)));

    connect(m_colourCombo, SIGNAL(activated(int)),
            SLOT(slotColourChanged(int)));

    connect(m_ipbPosition, SIGNAL(activated(int)),
            SLOT(slotIPBPositionChanged(int)));

    //m_nameEdit->selectAll();
    //m_description->selectAll();

    // set limits
    m_controllerBox->setMinValue(0);
    m_controllerBox->setMaxValue(127);

    m_minBox->setMinValue(INT_MIN);
    m_minBox->setMaxValue(INT_MAX);

    m_maxBox->setMinValue(INT_MIN);
    m_maxBox->setMaxValue(INT_MAX);

    m_defaultBox->setMinValue(INT_MIN);
    m_defaultBox->setMaxValue(INT_MAX);

    // populate combos
    m_typeCombo->insertItem(strtoqstr(Rosegarden::Controller::EventType));
    m_typeCombo->insertItem(strtoqstr(Rosegarden::PitchBend::EventType));
    /*
    m_typeCombo->insertItem(strtoqstr(Rosegarden::KeyPressure::EventType));
    m_typeCombo->insertItem(strtoqstr(Rosegarden::ChannelPressure::EventType));
    */

    // Populate colour combo
    //
    //
    Rosegarden::ColourMap &colourMap = m_doc->getComposition().getGeneralColourMap();
    Rosegarden::RCMap::const_iterator it;
    QPixmap colourPixmap(16, 16);

    for (it = colourMap.begin(); it != colourMap.end(); ++it)
    {
        Rosegarden::Colour c = it->second.first;
        colourPixmap.fill(QColor(c.getRed(), c.getGreen(), c.getBlue()));
        m_colourCombo->insertItem(colourPixmap, strtoqstr(it->second.second));
    }

    // Populate IPB position combo
    //
    m_ipbPosition->insertItem(notShowing);
    for (unsigned int i = 0; i < 32; i++)
        m_ipbPosition->insertItem(QString("%1").arg(i));

    if (m_control->getType() == Rosegarden::Controller::EventType)
        m_typeCombo->setCurrentItem(0);
    else if (m_control->getType() == Rosegarden::PitchBend::EventType)
        m_typeCombo->setCurrentItem(1);
    /*
    else if (m_control->getType() == Rosegarden::KeyPressure::EventType)
        m_typeCombo->setCurrentItem(2);
    else if (m_control->getType() == Rosegarden::ChannelPressure::EventType)
        m_typeCombo->setCurrentItem(3);
        */

    populate();
}

void 
ControlParameterEditDialog::populate()
{
    m_nameEdit->setText(strtoqstr(m_control->getName()));

    m_description->setText(strtoqstr(m_control->getDescription()));
    m_controllerBox->setValue(int(m_control->getControllerValue()));

    QString hexValue;
    hexValue.sprintf("(0x%x)", m_control->getControllerValue());
    m_hexValue->setText(hexValue);

    m_minBox->setValue(m_control->getMin());
    m_maxBox->setValue(m_control->getMax());
    m_defaultBox->setValue(m_control->getDefault());

    int pos = 0, setItem = 0;
    Rosegarden::ColourMap &colourMap = m_doc->getComposition().getGeneralColourMap();
    Rosegarden::RCMap::const_iterator it;
    for (it = colourMap.begin(); it != colourMap.end(); ++it)
        if (m_control->getColourIndex() == it->first) setItem = pos++;

    m_colourCombo->setCurrentItem(setItem);

    // set combo position
    m_ipbPosition->setCurrentItem(m_control->getIPBPosition() + 1);

    // If the type has changed and there are no defaults then we have to
    // supply some.
    //
    if (qstrtostr(m_typeCombo->currentText()) == Rosegarden::PitchBend::EventType ||
        qstrtostr(m_typeCombo->currentText()) == Rosegarden::KeyPressure::EventType ||
        qstrtostr(m_typeCombo->currentText()) == Rosegarden::ChannelPressure::EventType)
    {
        m_controllerBox->setEnabled(false);
        m_ipbPosition->setEnabled(false);
        m_colourCombo->setEnabled(false);
        m_hexValue->setEnabled(false);
        m_minBox->setEnabled(false);
        m_maxBox->setEnabled(false);
        m_defaultBox->setEnabled(false);
    }
    else if (qstrtostr(m_typeCombo->currentText()) == Rosegarden::Controller::EventType)
    {
        m_controllerBox->setEnabled(true);
        m_ipbPosition->setEnabled(true);
        m_colourCombo->setEnabled(true);
        m_hexValue->setEnabled(true);
        m_minBox->setEnabled(true);
        m_maxBox->setEnabled(true);
        m_defaultBox->setEnabled(true);
    }

}

void 
ControlParameterEditDialog::slotNameChanged(const QString &str)
{
    RG_DEBUG << "ControlParameterEditDialog::slotNameChanged" << endl;
    m_dialogControl.setName(qstrtostr(str));
}

void 
ControlParameterEditDialog::slotTypeChanged(int value)
{
    RG_DEBUG << "ControlParameterEditDialog::slotTypeChanged" << endl;
    m_dialogControl.setType(qstrtostr(m_typeCombo->text(value)));

    populate();
}

void 
ControlParameterEditDialog::slotDescriptionChanged(const QString &str)
{
    RG_DEBUG << "ControlParameterEditDialog::slotDescriptionChanged" << endl;
    m_dialogControl.setDescription(qstrtostr(str));
}

void
ControlParameterEditDialog::slotControllerChanged(int value)
{
    RG_DEBUG << "ControlParameterEditDialog::slotControllerChanged" << endl;
    m_dialogControl.setControllerValue(value);

    // set hex value
    QString hexValue;
    hexValue.sprintf("(0x%x)", value);
    m_hexValue->setText(hexValue);
}

void 
ControlParameterEditDialog::slotMinChanged(int value)
{
    RG_DEBUG << "ControlParameterEditDialog::slotMinChanged" << endl;
    m_dialogControl.setMin(value);
}

void 
ControlParameterEditDialog::slotMaxChanged(int value)
{
    RG_DEBUG << "ControlParameterEditDialog::slotMaxChanged" << endl;
    m_dialogControl.setMax(value);
}

void
ControlParameterEditDialog::slotDefaultChanged(int value)
{
    RG_DEBUG << "ControlParameterEditDialog::slotDefaultChanged" << endl;
    m_dialogControl.setDefault(value);
}

void 
ControlParameterEditDialog::slotColourChanged(int value)
{
    RG_DEBUG << "ControlParameterEditDialog::slotColourChanged" << endl;
    m_dialogControl.setColourIndex(value);
}


void
ControlParameterEditDialog::slotIPBPositionChanged(int value)
{
    RG_DEBUG << "ControlParameterEditDialog::slotIPBPositionChanged" << endl;
    m_dialogControl.setIPBPosition(value - 1);
}



#include "controleditor.moc"
