/*
    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 "EventEditDialog.h"

#include <tdelocale.h>
#include "misc/Strings.h"
#include "base/Event.h"
#include "base/MidiTypes.h"
#include "base/NotationTypes.h"
#include "base/PropertyName.h"
#include "base/RealTime.h"
#include "gui/editors/notation/NotePixmapFactory.h"
#include <kdialogbase.h>
#include <tdemessagebox.h>
#include <tqcheckbox.h>
#include <tqfont.h>
#include <tqgrid.h>
#include <tqgroupbox.h>
#include <tqhbox.h>
#include <tqlabel.h>
#include <tqlineedit.h>
#include <tqobject.h>
#include <tqobjectlist.h>
#include <tqpushbutton.h>
#include <tqscrollview.h>
#include <tqsize.h>
#include <tqspinbox.h>
#include <tqstring.h>
#include <tqtooltip.h>
#include <tqvbox.h>
#include <tqwidget.h>


namespace Rosegarden
{

EventEditDialog::EventEditDialog(TQWidget *parent,
                                 const Event &event,
                                 bool editable) :
        KDialogBase(parent, 0, true, i18n(editable ? "Advanced Event Edit" : "Advanced Event Viewer"),
                    (editable ? (Ok | Cancel) : Ok)),
        m_durationDisplay(0),
        m_durationDisplayAux(0),
        m_persistentGrid(0),
        m_nonPersistentGrid(0),
        m_nonPersistentView(0),
        m_originalEvent(event),
        m_event(event),
        m_type(event.getType()),
        m_absoluteTime(event.getAbsoluteTime()),
        m_duration(event.getDuration()),
        m_subOrdering(event.getSubOrdering()),
        m_modified(false)
{
    TQVBox *vbox = makeVBoxMainWidget();

    TQGroupBox *intrinsicBox = new TQGroupBox
                              (1, TQt::Horizontal, i18n("Intrinsics"), vbox);

    TQGrid *intrinsicGrid = new TQGrid(4, TQt::Horizontal, intrinsicBox);

    new TQLabel(i18n("Event type: "), intrinsicGrid);
    new TQLabel("", intrinsicGrid);
    new TQLabel("", intrinsicGrid);
    TQLineEdit *lineEdit = new TQLineEdit(intrinsicGrid);
    lineEdit->setText(strtoqstr(event.getType()));

    new TQLabel(i18n("Absolute time: "), intrinsicGrid);
    new TQLabel("", intrinsicGrid);
    new TQLabel("", intrinsicGrid);
    TQSpinBox *absoluteTime = new TQSpinBox
                             (INT_MIN, INT_MAX, Note(Note::Shortest).getDuration(), intrinsicGrid);
    absoluteTime->setValue(event.getAbsoluteTime());
    TQObject::connect(absoluteTime, TQ_SIGNAL(valueChanged(int)),
                     this, TQ_SLOT(slotAbsoluteTimeChanged(int)));
    slotAbsoluteTimeChanged(event.getAbsoluteTime());

    new TQLabel(i18n("Duration: "), intrinsicGrid);
    m_durationDisplay = new TQLabel("(note)", intrinsicGrid);
    m_durationDisplay->setMinimumWidth(20);
    m_durationDisplayAux = new TQLabel("(note)", intrinsicGrid);
    m_durationDisplayAux->setMinimumWidth(20);

    TQSpinBox *duration = new TQSpinBox
                         (0, INT_MAX, Note(Note::Shortest).getDuration(), intrinsicGrid);
    duration->setValue(event.getDuration());
    TQObject::connect(duration, TQ_SIGNAL(valueChanged(int)),
                     this, TQ_SLOT(slotDurationChanged(int)));
    slotDurationChanged(event.getDuration());

    new TQLabel(i18n("Sub-ordering: "), intrinsicGrid);
    new TQLabel("", intrinsicGrid);
    new TQLabel("", intrinsicGrid);

    TQSpinBox *subOrdering = new TQSpinBox( -100, 100, 1, intrinsicGrid);
    subOrdering->setValue(event.getSubOrdering());
    TQObject::connect(subOrdering, TQ_SIGNAL(valueChanged(int)),
                     this, TQ_SLOT(slotSubOrderingChanged(int)));
    slotSubOrderingChanged(event.getSubOrdering());

    TQGroupBox *persistentBox = new TQGroupBox
                               (1, TQt::Horizontal, i18n("Persistent properties"), vbox);
    m_persistentGrid = new TQGrid(4, TQt::Horizontal, persistentBox);

    TQLabel *label = new TQLabel(i18n("Name"), m_persistentGrid);
    TQFont font(label->font());
    font.setItalic(true);
    label->setFont(font);

    label = new TQLabel(i18n("Type"), m_persistentGrid);
    label->setFont(font);
    label = new TQLabel(i18n("Value"), m_persistentGrid);
    label->setFont(font);
    label = new TQLabel("", m_persistentGrid);
    label->setFont(font);

    Event::PropertyNames p = event.getPersistentPropertyNames();

    for (Event::PropertyNames::iterator i = p.begin();
            i != p.end(); ++i) {
        addPersistentProperty(*i);
    }

    p = event.getNonPersistentPropertyNames();

    if (p.begin() == p.end()) {
        m_nonPersistentView = 0;
        m_nonPersistentGrid = 0;
    } else {

        TQGroupBox *nonPersistentBox = new TQGroupBox
                                      (1, TQt::Horizontal, i18n("Non-persistent properties"), vbox);
        new TQLabel(i18n("These are cached values, lost if the event is modified."),
                   nonPersistentBox);

        m_nonPersistentView = new TQScrollView(nonPersistentBox);
        //m_nonPersistentView->setHScrollBarMode(TQScrollView::AlwaysOff);
        m_nonPersistentView->setResizePolicy(TQScrollView::AutoOneFit);

        m_nonPersistentGrid = new TQGrid
                              (4, TQt::Horizontal, m_nonPersistentView->viewport());
        m_nonPersistentView->addChild(m_nonPersistentGrid);

        m_nonPersistentGrid->setSpacing(4);
        m_nonPersistentGrid->setMargin(5);

        label = new TQLabel(i18n("Name       "), m_nonPersistentGrid);
        label->setFont(font);
        label = new TQLabel(i18n("Type       "), m_nonPersistentGrid);
        label->setFont(font);
        label = new TQLabel(i18n("Value      "), m_nonPersistentGrid);
        label->setFont(font);
        label = new TQLabel("", m_nonPersistentGrid);
        label->setFont(font);

        for (Event::PropertyNames::iterator i = p.begin();
                i != p.end(); ++i) {

            new TQLabel(strtoqstr(*i), m_nonPersistentGrid, strtoqstr(*i).ascii());
            new TQLabel(strtoqstr(event.getPropertyTypeAsString(*i)), m_nonPersistentGrid, strtoqstr(*i).ascii());
            new TQLabel(strtoqstr(event.getAsString(*i)), m_nonPersistentGrid, strtoqstr(*i).ascii());
            TQPushButton *button = new TQPushButton("P", m_nonPersistentGrid, strtoqstr(*i).ascii());
            button->setFixedSize(TQSize(24, 24));
            TQToolTip::add
                (button, i18n("Make persistent"));
            TQObject::connect(button, TQ_SIGNAL(clicked()),
                             this, TQ_SLOT(slotPropertyMadePersistent()));
        }
    }
}

void
EventEditDialog::addPersistentProperty(const PropertyName &name)
{
    TQLabel *label = new TQLabel(strtoqstr(name), m_persistentGrid, strtoqstr(name).ascii());
    label->show();
    label = new TQLabel(strtoqstr(m_originalEvent.getPropertyTypeAsString(name)),
                       m_persistentGrid, strtoqstr(name).ascii());
    label->show();

    PropertyType type(m_originalEvent.getPropertyType(name));
    switch (type) {

    case Int: {
                int min = INT_MIN, max = INT_MAX;
                // DMM - constrain program changes to a useful range of values
                // Might other types have a similar need for such limits?
                if (m_originalEvent.isa(ProgramChange::EventType)) {
                min = 0;
                max = 127;
            }
        TQSpinBox *spinBox = new TQSpinBox
                            (min, max, 1, m_persistentGrid, strtoqstr(name).ascii());
        spinBox->setValue(m_originalEvent.get<Int>(name));
        TQObject::connect(spinBox, TQ_SIGNAL(valueChanged(int)),
                         this, TQ_SLOT(slotIntPropertyChanged(int)));
        spinBox->show();
        break;
    }
case UInt: {
        int min = 0;
        int max = UINT_MAX;
        if (m_originalEvent.isa(ProgramChange::EventType)) {
                min = 0;
                max = 65535;
            }
            TQSpinBox *spinBox = new TQSpinBox
                                (min, max, 1, m_persistentGrid, strtoqstr(name).ascii());
            spinBox->setValue(m_originalEvent.get<UInt>(name));
            TQObject::connect(spinBox, TQ_SIGNAL(valueChanged(int)),
                             this, TQ_SLOT(slotIntPropertyChanged(int)));
            spinBox->show();
            break;
        }
    case RealTimeT: {
            RealTime realTime = m_originalEvent.get<RealTimeT>(name);

            TQHBox* hbox = new TQHBox(m_persistentGrid);

            // seconds
            //
            TQSpinBox *spinBox = new TQSpinBox
                                (INT_MIN, INT_MAX, 1,
                                 hbox, TQString(strtoqstr(name) + "%sec").ascii());
            spinBox->setValue(realTime.sec);

            TQObject::connect(spinBox, TQ_SIGNAL(valueChanged(int)),
                             this, TQ_SLOT(slotRealTimePropertyChanged(int)));

            // nseconds
            //
            spinBox = new TQSpinBox
                      (INT_MIN, INT_MAX, 1,
                       hbox, TQString(strtoqstr(name) + "%nsec").ascii());
            spinBox->setValue(realTime.nsec);

            TQObject::connect(spinBox, TQ_SIGNAL(valueChanged(int)),
                             this, TQ_SLOT(slotRealTimePropertyChanged(int)));
            spinBox->show();
            break;
        }

    case Bool: {
            TQCheckBox *checkBox = new TQCheckBox
                                  ("", m_persistentGrid, strtoqstr(name).ascii());
            checkBox->setChecked(m_originalEvent.get<Bool>(name));
            TQObject::connect(checkBox, TQ_SIGNAL(activated()),
                             this, TQ_SLOT(slotBoolPropertyChanged()));
            checkBox->show();
            break;
        }

    case String: {
            TQLineEdit *lineEdit = new TQLineEdit
                                  (strtoqstr(m_originalEvent.get<String>(name)),
                                   m_persistentGrid,
                                   strtoqstr(name).ascii());
            TQObject::connect(lineEdit, TQ_SIGNAL(textChanged(const TQString &)),
                             this, TQ_SLOT(slotStringPropertyChanged(const TQString &)));
            lineEdit->show();
            break;
        }
    }

    TQPushButton *button = new TQPushButton("X", m_persistentGrid,
                                          strtoqstr(name).ascii());
    button->setFixedSize(TQSize(24, 24));
    TQToolTip::add
        (button, i18n("Delete this property"));
    TQObject::connect(button, TQ_SIGNAL(clicked()),
                     this, TQ_SLOT(slotPropertyDeleted()));
    button->show();
}

Event
EventEditDialog::getEvent() const
{
    return Event(m_event, m_absoluteTime, m_duration, m_subOrdering);
}

void
EventEditDialog::slotEventTypeChanged(const TQString &type)
{
    std::string t(qstrtostr(type));
    if (t != m_type) {
        m_modified = true;
        m_type = t;
    }
}

void
EventEditDialog::slotAbsoluteTimeChanged(int value)
{
    if (value == m_absoluteTime)
        return ;
    m_modified = true;
    m_absoluteTime = value;
}

void
EventEditDialog::slotDurationChanged(int value)
{
    timeT error = 0;
    m_durationDisplay->setPixmap
    (NotePixmapFactory::toTQPixmap(m_notePixmapFactory.makeNoteMenuPixmap(timeT(value), error)));

    if (error >= value / 2) {
        m_durationDisplayAux->setText("++ ");
    } else if (error > 0) {
        m_durationDisplayAux->setText("+ ");
    } else if (error < 0) {
        m_durationDisplayAux->setText("- ");
    } else {
        m_durationDisplayAux->setText(" ");
    }

    if (timeT(value) == m_duration)
        return ;

    m_modified = true;
    m_duration = value;
}

void
EventEditDialog::slotSubOrderingChanged(int value)
{
    if (value == m_subOrdering)
        return ;
    m_modified = true;
    m_subOrdering = value;
}

void
EventEditDialog::slotIntPropertyChanged(int value)
{
    const TQObject *s = sender();
    const TQSpinBox *spinBox = dynamic_cast<const TQSpinBox *>(s);
    if (!spinBox)
        return ;

    m_modified = true;
    TQString propertyName = spinBox->name();
    m_event.set<Int>(qstrtostr(propertyName), value);
}

void
EventEditDialog::slotRealTimePropertyChanged(int value)
{
    const TQObject *s = sender();
    const TQSpinBox *spinBox = dynamic_cast<const TQSpinBox *>(s);
    if (!spinBox)
        return ;

    m_modified = true;
    TQString propertyFullName = spinBox->name();

    TQString propertyName = propertyFullName.section('%', 0, 0),
                           nsecOrSec = propertyFullName.section('%', 1, 1);

    RealTime realTime = m_event.get<RealTimeT>(qstrtostr(propertyName));

    if (nsecOrSec == "sec")
        realTime.sec = value;
    else
        realTime.nsec = value;

    m_event.set<Int>(qstrtostr(propertyName), value);
}

void
EventEditDialog::slotBoolPropertyChanged()
{
    const TQObject *s = sender();
    const TQCheckBox *checkBox = dynamic_cast<const TQCheckBox *>(s);
    if (!checkBox)
        return ;

    m_modified = true;
    TQString propertyName = checkBox->name();
    bool checked = checkBox->isChecked();

    m_event.set<Bool>(qstrtostr(propertyName), checked);
}

void
EventEditDialog::slotStringPropertyChanged(const TQString &value)
{
    const TQObject *s = sender();
    const TQLineEdit *lineEdit = dynamic_cast<const TQLineEdit *>(s);
    if (!lineEdit)
        return ;

    m_modified = true;
    TQString propertyName = lineEdit->name();
    m_event.set<String>(qstrtostr(propertyName), qstrtostr(value));
}

void
EventEditDialog::slotPropertyDeleted()
{
    const TQObject *s = sender();
    const TQPushButton *pushButton = dynamic_cast<const TQPushButton *>(s);
    if (!pushButton)
        return ;

    TQString propertyName = pushButton->name();

    if (KMessageBox::warningContinueCancel
            (this,
             i18n("Are you sure you want to delete the \"%1\" property?\n\n"
                  "Removing necessary properties may cause unexpected behavior.").
             arg(propertyName),
             i18n("Edit Event"),
             i18n("&Delete")) != KMessageBox::Continue)
        return ;

    m_modified = true;
    TQObjectList *list = m_persistentGrid->queryList(0, propertyName.ascii(), false);
    TQObjectListIt i(*list);
    TQObject *obj;
    while ((obj = i.current()) != 0) {
        ++i;
        delete obj;
    }
    delete list;

    m_event.unset(qstrtostr(propertyName));
}

void
EventEditDialog::slotPropertyMadePersistent()
{
    const TQObject *s = sender();
    const TQPushButton *pushButton = dynamic_cast<const TQPushButton *>(s);
    if (!pushButton)
        return ;

    TQString propertyName = pushButton->name();

    if (KMessageBox::warningContinueCancel
            (this,
             i18n("Are you sure you want to make the \"%1\" property persistent?\n\n"
                  "This could cause problems if it overrides a different "
                  "computed value later on.").
             arg(propertyName),
             i18n("Edit Event"),
             i18n("Make &Persistent")) != KMessageBox::Continue)
        return ;

    TQObjectList *list = m_nonPersistentGrid->queryList(0, propertyName.ascii(), false);
    TQObjectListIt i(*list);
    TQObject *obj;
    while ((obj = i.current()) != 0) {
        ++i;
        delete obj;
    }
    delete list;

    m_modified = true;
    addPersistentProperty(qstrtostr(propertyName));

    PropertyType type =
        m_originalEvent.getPropertyType(qstrtostr(propertyName));

    switch (type) {

    case Int:
        m_event.set<Int>
        (qstrtostr(propertyName),
         m_originalEvent.get<Int>
         (qstrtostr(propertyName)));
        break;

    case UInt:
        m_event.set<UInt>
        (qstrtostr(propertyName),
         m_originalEvent.get<UInt>
         (qstrtostr(propertyName)));
        break;

    case RealTimeT:
        m_event.set<RealTimeT>
        (qstrtostr(propertyName),
         m_originalEvent.get<RealTimeT>
         (qstrtostr(propertyName)));
        break;

    case Bool:
        m_event.set<Bool>
        (qstrtostr(propertyName),
         m_originalEvent.get<Bool>
         (qstrtostr(propertyName)));
        break;

    case String:
        m_event.set<String>
        (qstrtostr(propertyName),
         m_originalEvent.get<String>
         (qstrtostr(propertyName)));
        break;
    }
}

}
#include "EventEditDialog.moc"
