//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: trackinfo.cpp,v 1.10 2004/05/12 21:41:07 wschweer Exp $
//  (C) Copyright 1999-2004 Werner Schweer (ws@seh.de)
//=========================================================

#include <qlayout.h>
#include <qcombobox.h>
#include <qtoolbutton.h>
#include <qlabel.h>
#include <qpopupmenu.h>
#include <qhbox.h>
#include <qcheckbox.h>
#include <qspinbox.h>
#include <qpushbutton.h>
#include <qwidgetstack.h>

#include "config.h"
#include "arranger.h"
#include "intlabel.h"
#include "midiport.h"
#include "mididev.h"
#include "utils.h"
#include "tlist.h"
#include "mtrackinfobase.h"
#include "alayout.h"
#include "audio.h"
#include "mixer/amixer.h"
#include "midi.h"
#include "midictrl.h"
#include "xpm/muse_leftside_logo.xpm"
#include "mixer/astrip.h"

//---------------------------------------------------------
//   MidiTrackInfo
//---------------------------------------------------------

class MidiTrackInfo : public MidiTrackInfoBase {
   public:
      MidiTrackInfo(QWidget* parent) : MidiTrackInfoBase(parent) {}
      };

//---------------------------------------------------------
//   showTrackInfo
//---------------------------------------------------------

void Arranger::showTrackInfo(bool flag)
      {
      showTrackinfoFlag = flag;
      trackInfo->setShown(flag);
      infoScroll->setShown(flag);
      updateTrackInfo();
      }

//---------------------------------------------------------
//   genTrackInfo
//---------------------------------------------------------

void Arranger::genTrackInfo(QWidget* parent)
      {
      trackInfo = new WidgetStack(parent, "trackInfoStack");

      noTrackInfo          = new QWidget(trackInfo);
      QPixmap *noInfoPix   = new QPixmap(160, 1000); //muse_leftside_logo_xpm);
      const QPixmap *logo  = new QPixmap(muse_leftside_logo_xpm);
      noInfoPix->fill(noTrackInfo->paletteBackgroundColor() );
      copyBlt(noInfoPix, 10, 0, logo, 0,0, logo->width(), logo->height());
      noTrackInfo->setPaletteBackgroundPixmap(*noInfoPix);
      noTrackInfo->setGeometry(0, 0, 65, 200);
      noTrackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding));

      midiTrackInfo = new MidiTrackInfo(trackInfo);
      trackInfo->addWidget(noTrackInfo,   0);
      trackInfo->addWidget(midiTrackInfo, 1);
      trackInfo->addWidget(0, 2);

      genMidiTrackInfo();
      }

//---------------------------------------------------------
//   updateTrackInfo
//---------------------------------------------------------

void Arranger::updateTrackInfo()
      {
      if (!showTrackinfoFlag) {
            switchInfo(-1);
            return;
            }
      if (selected == 0) {
            switchInfo(0);
            return;
            }
      if (selected->isMidiTrack()) {
            switchInfo(1);
            updateMidiTrackInfo();
            }
      else {
            switchInfo(2);
            }
      }

//---------------------------------------------------------
//   switchInfo
//---------------------------------------------------------

void Arranger::switchInfo(int n)
      {
      if (n == 2) {
            AudioStrip* w = (AudioStrip*)(trackInfo->getWidget(2));
            if (w == 0 || selected != w->getTrack()) {
                  if (w)
                        delete w;
                  w = new AudioStrip(trackInfo, (AudioTrack*)selected);
                  connect(song, SIGNAL(songChanged(int)), w, SLOT(songChanged(int)));
                  w->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
                  trackInfo->addWidget(w, 2);
                  w->show();
                  tgrid->activate();
                  }
            }
      if (trackInfo->curIdx() == n)
            return;
      trackInfo->raiseWidget(n);
      tgrid->activate();
      }

//---------------------------------------------------------
//   iNameChanged
//---------------------------------------------------------

void Arranger::iNameChanged()
      {
      QString txt = midiTrackInfo->iName->text();
      if (txt == selected->name())
            return;
      Track* track = selected->clone();
      selected->setName(txt);
      audio->msgChangeTrack(track, selected);
      }

//---------------------------------------------------------
//   iOutputChannelChanged
//---------------------------------------------------------

void Arranger::iOutputChannelChanged(int channel)
      {
      --channel;
      MidiTrack* track = (MidiTrack*)selected;
      if (channel != track->outChannel()) {
            track->setOutChannel(channel);
            song->update(-1);
            }
      }

//---------------------------------------------------------
//   iKanalChanged
//---------------------------------------------------------

void Arranger::iInputChannelChanged(const QString& s)
      {
      MidiTrack* track = (MidiTrack*)selected;
      int val = string2bitmap(s);
      if (val != track->inChannelMask()) {
            track->setInChannelMask(val);
            list->redraw();
            }
      }

//---------------------------------------------------------
//   iOutputPortChanged
//---------------------------------------------------------

void Arranger::iOutputPortChanged(int index)
      {
      MidiTrack* track = (MidiTrack*)selected;
      if (index == track->outPort())
            return;
      track->setOutPort(index);
      list->redraw();
      }

//---------------------------------------------------------
//   iInputPortChanged
//---------------------------------------------------------

void Arranger::iInputPortChanged(const QString& s)
      {
      int val = string2bitmap(s);
      MidiTrack* track = (MidiTrack*)selected;
      if (val == track->inPortMask())
            return;
      track->setInPortMask(val);
      list->redraw();
      }

//---------------------------------------------------------
//   iProgramChanged
//---------------------------------------------------------

void Arranger::iProgramChanged()
      {
      MidiTrack* track = (MidiTrack*)selected;
      int channel = track->outChannel();
      int port    = track->outPort();
      int hbank   = midiTrackInfo->iHBank->value();
      int lbank   = midiTrackInfo->iLBank->value();
      int prog    = midiTrackInfo->iProgram->value();

      if (hbank > 0 && hbank < 129)
            hbank -= 1;
      else
            hbank = 0xff;
      if (lbank > 0 && lbank < 129)
            lbank -= 1;
      else
            lbank = 0xff;
      if (prog > 0 && prog < 129)
            prog -= 1;
      else
            prog = 0xff;

      program = (hbank << 16) + (lbank << 8) + prog;
      MidiPlayEvent ev(0, port, channel, ME_CONTROLLER, CTRL_PROGRAM, program);
      audio->msgPlayMidiEvent(&ev);
      MidiInstrument* instr = midiPorts[port].instrument();
      const char* name = instr->getPatchName(channel, program, song->mtype());
      midiTrackInfo->iPatch->setText(QString(name));
//      updateTrackInfo();
      }

//---------------------------------------------------------
//   iLautstChanged
//---------------------------------------------------------

void Arranger::iLautstChanged(int val)
      {
      if (val < 0 || val > 127)
            return;
      MidiTrack* track = (MidiTrack*)selected;
      MidiPlayEvent ev(0, track->outPort(), track->outChannel(),
         ME_CONTROLLER, CTRL_VOLUME, val);
      audio->msgPlayMidiEvent(&ev);
      song->update(SC_MIDI_CONTROLLER);
      }

//---------------------------------------------------------
//   iTranspChanged
//---------------------------------------------------------

void Arranger::iTranspChanged(int val)
      {
      MidiTrack* track = (MidiTrack*)selected;
      track->transposition = val;
      }

//---------------------------------------------------------
//   iAnschlChanged
//---------------------------------------------------------

void Arranger::iAnschlChanged(int val)
      {
      MidiTrack* track = (MidiTrack*)selected;
      track->velocity = val;
      }

//---------------------------------------------------------
//   iVerzChanged
//---------------------------------------------------------

void Arranger::iVerzChanged(int val)
      {
      MidiTrack* track = (MidiTrack*)selected;
      track->delay = val;
      }

//---------------------------------------------------------
//   iLenChanged
//---------------------------------------------------------

void Arranger::iLenChanged(int val)
      {
      MidiTrack* track = (MidiTrack*)selected;
      track->len = val;
      }

//---------------------------------------------------------
//   iKomprChanged
//---------------------------------------------------------

void Arranger::iKomprChanged(int val)
      {
      MidiTrack* track = (MidiTrack*)selected;
      track->compression = val;
      }

//---------------------------------------------------------
//   iPanChanged
//---------------------------------------------------------

void Arranger::iPanChanged(int val)
      {
      if (val < -65 || val > 64)
            return;
      MidiTrack* track = (MidiTrack*)selected;
      int port    = track->outPort();
      int channel = track->outChannel();

      // Realtime Change:
      MidiPlayEvent ev(0, port, channel,
         ME_CONTROLLER, CTRL_PANPOT, val + 65);
      audio->msgPlayMidiEvent(&ev);
      song->update(SC_MIDI_CONTROLLER);
      }

//---------------------------------------------------------
//   instrPopup
//---------------------------------------------------------

void Arranger::instrPopup()
      {
      MidiTrack* track = (MidiTrack*)selected;
      int channel = track->outChannel();
      int port    = track->outPort();
      MidiInstrument* instr = midiPorts[port].instrument();
      instr->populatePatchPopup(pop, channel, song->mtype());

      int rv = pop->exec(midiTrackInfo->iPatch->mapToGlobal(QPoint(10,5)));
      if (rv != -1) {
            MidiPlayEvent ev(0, port, channel, ME_CONTROLLER, CTRL_PROGRAM, rv);
            audio->msgPlayMidiEvent(&ev);
            updateTrackInfo();
            }
      }

//---------------------------------------------------------
//   genMidiTrackInfo
//---------------------------------------------------------

void Arranger::genMidiTrackInfo()
      {
      connect(midiTrackInfo->iPatch, SIGNAL(released()), SLOT(instrPopup()));

      pop = new QPopupMenu(midiTrackInfo->iPatch);
      pop->setCheckable(false);

      connect(midiTrackInfo->iName, SIGNAL(returnPressed()), SLOT(iNameChanged()));
      connect(midiTrackInfo->iOutputChannel, SIGNAL(valueChanged(int)), SLOT(iOutputChannelChanged(int)));
      connect(midiTrackInfo->iInputChannel, SIGNAL(textChanged(const QString&)), SLOT(iInputChannelChanged(const QString&)));
      connect(midiTrackInfo->iHBank, SIGNAL(valueChanged(int)), SLOT(iProgramChanged()));
      connect(midiTrackInfo->iLBank, SIGNAL(valueChanged(int)), SLOT(iProgramChanged()));
      connect(midiTrackInfo->iProgram, SIGNAL(valueChanged(int)), SLOT(iProgramChanged()));
      connect(midiTrackInfo->iLautst, SIGNAL(valueChanged(int)), SLOT(iLautstChanged(int)));
      connect(midiTrackInfo->iTransp, SIGNAL(valueChanged(int)), SLOT(iTranspChanged(int)));
      connect(midiTrackInfo->iAnschl, SIGNAL(valueChanged(int)), SLOT(iAnschlChanged(int)));
      connect(midiTrackInfo->iVerz, SIGNAL(valueChanged(int)), SLOT(iVerzChanged(int)));
      connect(midiTrackInfo->iLen, SIGNAL(valueChanged(int)), SLOT(iLenChanged(int)));
      connect(midiTrackInfo->iKompr, SIGNAL(valueChanged(int)), SLOT(iKomprChanged(int)));
      connect(midiTrackInfo->iPan, SIGNAL(valueChanged(int)), SLOT(iPanChanged(int)));
      connect(midiTrackInfo->iOutput, SIGNAL(activated(int)), SLOT(iOutputPortChanged(int)));
      connect(midiTrackInfo->iInput, SIGNAL(textChanged(const QString&)), SLOT(iInputPortChanged(const QString&)));
      connect(midiTrackInfo->recordButton, SIGNAL(clicked()), SLOT(recordClicked()));
      }

//---------------------------------------------------------
//   updateMidiTrackInfo
//---------------------------------------------------------

void Arranger::updateMidiTrackInfo()
      {
      MidiTrack* track = (MidiTrack*)selected;
      int outChannel = track->outChannel();
      int inChannel  = track->inChannelMask();
      int outPort    = track->outPort();
      int inPort     = track->inPortMask();

      midiTrackInfo->iInput->clear();
      midiTrackInfo->iOutput->clear();

      for (int i = 0; i < MIDI_PORTS; ++i) {
            MidiPort* port  = &midiPorts[i];
            MidiDevice* dev = port->device();
            QString name;
            name.sprintf("%d(%s)", i+1, dev ? dev->name().latin1() : "none");
            midiTrackInfo->iOutput->insertItem(name, i);
            if (i == outPort)
                  midiTrackInfo->iOutput->setCurrentItem(i);
            }
      midiTrackInfo->iInput->setText(bitmap2String(inPort));
      midiTrackInfo->iInputChannel->setText(bitmap2String(inChannel));

      if (midiTrackInfo->iName->text() != selected->name()) {
            midiTrackInfo->iName->setText(selected->name());
            midiTrackInfo->iName->home(false);
            }

      midiTrackInfo->iOutputChannel->setValue(outChannel+1);
      midiTrackInfo->iInputChannel->setText(bitmap2String(inChannel));

      seek();

      midiTrackInfo->iTransp->setValue(track->transposition);
      midiTrackInfo->iAnschl->setValue(track->velocity);
      midiTrackInfo->iVerz->setValue(track->delay);
      midiTrackInfo->iLen->setValue(track->len);
      midiTrackInfo->iKompr->setValue(track->compression);
      }

//---------------------------------------------------------
//   seek
//    change values akkording to seek position
//---------------------------------------------------------

void Arranger::seek()
      {
      if (!showTrackinfoFlag || !selected)
            return;
      switch(selected->type()) {
            case Track::MIDI:
            case Track::DRUM:
                  {
                  MidiTrack* track = (MidiTrack*)selected;
                  int outPort      = track->outPort();
                  int outChannel   = track->outChannel();
                  MidiPort* mp = &midiPorts[outPort];

                  // int nprogram = mp->getCtrl(outChannel, tick, CTRL_PROGRAM);
                  int nprogram = mp->hwCtrlState(outChannel, CTRL_PROGRAM);
                  if (program != nprogram) {
                        program = nprogram;

                        int hb, lb, pr;
                        if (program == CTRL_VAL_UNKNOWN) {
                              hb = lb = pr = 0;
                              midiTrackInfo->iPatch->setText("---");
                              }
                        else {
                              MidiInstrument* instr = midiPorts[outPort].instrument();
                              const char* name = instr->getPatchName(outChannel, program, song->mtype());
                              midiTrackInfo->iPatch->setText(QString(name));

                              hb = ((program >> 16) & 0xff) + 1;
                              if (hb == 0x100)
                                    hb = 0;
                              lb = ((program >> 8) & 0xff) + 1;
                              if (lb == 0x100)
                                    lb = 0;
                              pr = (program & 0xff) + 1;
                              if (pr == 0x100)
                                    pr = 0;
                              }
                        midiTrackInfo->iHBank->blockSignals(true);
                        midiTrackInfo->iLBank->blockSignals(true);
                        midiTrackInfo->iProgram->blockSignals(true);

                        midiTrackInfo->iHBank->setValue(hb);
                        midiTrackInfo->iLBank->setValue(lb);
                        midiTrackInfo->iProgram->setValue(pr);

                        midiTrackInfo->iHBank->blockSignals(false);
                        midiTrackInfo->iLBank->blockSignals(false);
                        midiTrackInfo->iProgram->blockSignals(false);
                        }

                  //int nvolume = midiPorts[outPort].getCtrl(outChannel, tick, CTRL_VOLUME);
                  int nvolume = midiPorts[outPort].hwCtrlState(outChannel, CTRL_VOLUME);
                  if (nvolume == CTRL_VAL_UNKNOWN)
                        nvolume = -1;
                  // int npan = midiPorts[outPort].getCtrl(outChannel, tick, CTRL_PANPOT);
                  int npan = midiPorts[outPort].hwCtrlState(outChannel, CTRL_PANPOT);
                  if (npan == CTRL_VAL_UNKNOWN)
                        npan = -66;
                  if (nvolume != volume) {
                        volume = nvolume;
                        midiTrackInfo->iLautst->blockSignals(true);
                        midiTrackInfo->iLautst->setValue(volume);
                        midiTrackInfo->iLautst->blockSignals(false);
                        }
                  if (npan != pan) {
                        pan = npan;
                        midiTrackInfo->iPan->blockSignals(true);
                        midiTrackInfo->iPan->setValue(pan - 65);
                        midiTrackInfo->iPan->blockSignals(false);
                        }
                  }
                  break;
            case Track::WAVE:
            case Track::AUDIO_OUTPUT:
            case Track::AUDIO_INPUT:
            case Track::AUDIO_GROUP:
            case Track::AUDIO_AUX:
            case Track::AUDIO_SOFTSYNTH:
                  break;
            }
      }

