//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: mixer.cpp,v 1.1.1.1 2003/10/29 10:05:24 wschweer Exp $
//  (C) Copyright 1999 Werner Schweer (ws@seh.de)
//=========================================================

#include <stdio.h>

#include "mixer.h"
#include <qapplication.h>
#include <qlayout.h>
#include <qslider.h>
#include <qlabel.h>
#include <qsignalmapper.h>
#include <qpixmap.h>
#include <qpushbutton.h>
#include <qtoolbar.h>
#include <assert.h>

#include "intlabel.h"
#include "song.h"
#include "lcombo.h"
#include "midiport.h"
#include "icons.h"
#include "utils.h"
#include "../xml.h"
#include "midictrl.h"

#define CTRL  4         // number of controller values in mixer

//---------------------------------------------------------
//   Mixer
//---------------------------------------------------------

Mixer::Mixer() : TopWin(0, "MidiMixer", WType_TopLevel)
      {
      setCaption(tr("MusE: Midi Mixer"));
      const char* ctrlLabels[CTRL] = { 
            QT_TR_NOOP("Rev"), 
            QT_TR_NOOP("Cho"), 
            QT_TR_NOOP("Var"),
            QT_TR_NOOP("Pan") 
            };
      port = 0;

      TrackList* tracks = song->tracks();
      bool used[MIDI_CHANNELS*MIDI_PORTS];
      for (int i = 0; i < MIDI_CHANNELS*MIDI_PORTS; ++i)
            used[i] = false;
      for (iTrack it = tracks->begin(); it != tracks->end(); ++it) {
            MidiTrack* track = dynamic_cast<MidiTrack*>(*it);
            if (track == 0)
                  continue;
            int port = track->outPort();
            int channel = track->outChannel();
            used[channel + MIDI_CHANNELS * port] = true;
            }
      int channels = 0;
      for (int port = 0; port < MIDI_PORTS; ++port) {
            for (int channel = 0; channel < MIDI_CHANNELS; ++channel) {
                  if (used[channel + MIDI_CHANNELS * port]) {
                        MixerLine* line = new MixerLine;
                        line->port = port;
                        line->channel = channel;
                        line->index = channels++;
                        lines.push_back(line);
                        }
                  }
            }

      QWidget* w = new QWidget(this, "main");
      setCentralWidget(w);

      QSignalMapper* mapper = new QSignalMapper(this);
      connect(mapper, SIGNAL(mapped(int)), this, SLOT(valChanged(int)));

      QGridLayout* grid = new QGridLayout(w);
      grid->setRowStretch(4+CTRL*2, 20);

      grid->addMultiCellWidget(hLine(w),  0, 0, 0, channels*2);
      grid->addMultiCellWidget(hLine(w),  2, 2, 0, channels*2);

      for (iMixerLine i = lines.begin(); i != lines.end(); ++i) {
            MixerLine* l = *i;

            int index = l->index;
            char buffer[16];
            sprintf(buffer, "Ch%d", (*i)->channel + 1);
            QLabel* cl = new QLabel(QString(buffer), w);
            cl->setAlignment(AlignCenter);
            grid->addWidget(cl,       1, index*2);
            grid->addWidget(vLine(w), 1, index*2+1);

            sprintf(buffer, "P %d", (*i)->port + 1);
            QLabel* pl = new QLabel(QString(buffer), w);
            pl->setAlignment(AlignCenter);
            grid->addWidget(pl,       3, index*2);
            grid->addWidget(hLine(w), 4, index*2);

            grid->addMultiCellWidget(vLine(w), 3, 6+CTRL*2, index*2+1, index*2+1);

            IntLabel** dials[CTRL] = {
                  &l->reverb, &l->chorus, &l->variation, &l->panpos
                  };
            for (int k = 0; k < CTRL; ++k) {
                  *dials[k] = new IntLabel(-1, -1, 127, w, -1);
                  mapper->setMapping(*dials[k], (k<<8)+index);
                  connect(*dials[k], SIGNAL(valueChanged(int)), mapper, SLOT(map()));
                  grid->addWidget(*dials[k], 5+k*2, index*2);
                  grid->addWidget(hLine(w), 6+k*2, index*2);
                  }
            l->mute = new QPushButton(w);
            l->mute->setPixmap(*greendotIcon);
            l->mute->setToggleButton(true);
            grid->addWidget(l->mute, 5+CTRL*2, index*2);
            mapper->setMapping(l->mute, 4*256+index);
            connect(l->mute, SIGNAL(toggled(bool)), mapper, SLOT(map()));

            l->volume = new QSlider(0, 127, 1, 127, Vertical, w);
            l->volume->setTickmarks(QSlider::Right);
            l->volume->setTickInterval(32);
            mapper->setMapping(l->volume, 5*256+index);
            connect(l->volume, SIGNAL(valueChanged(int)), mapper, SLOT(map()));
            grid->addWidget(l->volume, 6 + CTRL*2, index*2);
            }

      //---------------------------------------------------
      //    master
      //---------------------------------------------------

      QLabel* cl = new QLabel(tr("Master"), w, "chl");
      grid->addWidget(cl, 1, channels*2);

      QLabel* cp = new QLabel(tr("Port"), w);
      cp->setAlignment(AlignCenter);
      grid->addWidget(cp, 3, channels*2);
      grid->addWidget(hLine(w), 4, channels*2);

      for (int k = 0; k < CTRL; ++k) {
            QLabel* l1  = new QLabel(tr(ctrlLabels[k]), w);
            l1->setAlignment(AlignCenter);
            grid->addWidget(l1, 5+k*2, channels*2);
            grid->addWidget(hLine(w), 6+k*2, channels*2);
            }
      QLabel* l2 =new QLabel(tr("Mute"), w);
      grid->addWidget(l2, 5+CTRL*2, channels*2);

      master = new QSlider(0, 16383, 1, 16383, Vertical, w);
      master->setTickmarks(QSlider::Right);
      master->setTickInterval(2048);
      master->setValue(16383 - midiPorts[0].masterVol());

      grid->addWidget(master, 6+CTRL*2, channels*2);
      connect(master, SIGNAL(valueChanged(int)), this, SLOT(masterChanged(int)));
      grid->setColStretch(channels*2+1, 20);
      updateValues(false);    // set init values
      setFixedWidth((channels+1) * (32+5));
      }

//---------------------------------------------------------
//   valChanged
//---------------------------------------------------------

void Mixer::valChanged(int p)
      {
      int type    = p >> 8;
      int index = p & 0xff;
      iMixerLine i;
      for (i = lines.begin(); i != lines.end(); ++i) {
            if ((*i)->index == index)
                  break;
            }
      assert(i != lines.end());
      MixerLine* l = *i;
      int channel = l->channel;
      int port    = l->port;
      int val;
      switch(type) {
            case 0:
                  val = l->reverb->value();
                  emit ctrlChanged(port, channel, 0x5b, val);
                  break;
            case 1:
                  val = l->chorus->value();
                  emit ctrlChanged(port, channel, 0x5d, val);
                  break;
            case 2:
                  val = l->variation->value();
                  emit ctrlChanged(port, channel, 0x5e, val);
                  break;
            case 3:
                  val = l->panpos->value();
                  emit ctrlChanged(port, channel, 0x0a, val);
                  break;
            case 4:
                  val = l->mute->isOn();
                  if (val)
                        l->mute->setPixmap(*editmuteSIcon);
                  else
                        l->mute->setPixmap(*greendotIcon);
                  song->setChannelMute(channel, val);
                  break;
            case 5:
                  val = 127 - l->volume->value();
                  emit ctrlChanged(port, channel, 0x7, val);
                  break;
            default:
                  printf("valChanged:interner Fehler type=%d\n", type);
                  break;
            }
      }

//---------------------------------------------------------
//   masterChanged
//---------------------------------------------------------

void Mixer::masterChanged(int val)
      {
      emit masterVolChanged(16383-val);
      }

//---------------------------------------------------------
//   closeEvent
//---------------------------------------------------------

void Mixer::closeEvent(QCloseEvent* e)
      {
      emit deleted((int)this);
      e->accept();
      }

//---------------------------------------------------------
//   updateMixer
//---------------------------------------------------------

void Mixer::updateValues(bool current)
      {
      for (iMixerLine i = lines.begin(); i != lines.end(); ++i) {
            MixerLine* l = *i;
            MidiPort* port = &midiPorts[l->port];
            ChannelState* state = current ? port->cState(l->channel) : port->iState(l->channel);
            int val = state->controller[CTRL_VOLUME];
            if (val != 127-l->volume->value())
                  l->volume->setValue(127 - val);
            val = state->controller[CTRL_CHORUS_SEND];
            if (val != l->chorus->value())
                  l->chorus->setValue(val);
            val = state->controller[CTRL_REVERB_SEND];
            if (val != l->reverb->value())
                  l->reverb->setValue(val);
            val = state->controller[CTRL_VARIATION_SEND];
            if (val != l->variation->value())
                  l->variation->setValue(val);
            val = state->controller[CTRL_PANPOT];
            if (val != l->panpos->value())
                  l->panpos->setValue(val);
            }
      }

//---------------------------------------------------------
//   readStatus
//---------------------------------------------------------

void Mixer::readStatus(Xml& xml)
      {

      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::TagStart:
                        if (tag == "topwin")
                              TopWin::readStatus(xml);
                        else
                              xml.unknown("MidiMixer");
                        break;
                  case Xml::TagEnd:
                        if (tag == "midimixer")
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   writeStatus
//---------------------------------------------------------

void Mixer::writeStatus(int level, Xml& xml) const
      {
      xml.tag(level++, "midimixer");
      TopWin::writeStatus(level, xml);
      xml.tag(level, "/midimixer");
      }
