//=============================================================================
//  MuseScore
//  Music Composition & Notation
//  $Id: musescore.cpp 4961 2011-11-11 16:24:17Z lasconic $
//
//  Copyright (C) 2002-2011 Werner Schweer and others
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================

#include <fenv.h>

#include "musescore.h"
#include "libmscore/score.h"
#include "libmscore/instrument.h"
#include "libmscore/measure.h"
#include "libmscore/segment.h"
#include "libmscore/chordrest.h"
#include "libmscore/chord.h"
#include "libmscore/note.h"
#include "libmscore/undo.h"
#include "mixer.h"
#include "scoreview.h"
#include "playpanel.h"
#include "preferences.h"
#include "seq.h"
#include "synthesizer/msynthesizer.h"

#ifdef OSC
#include "ofqf/qoscserver.h"
#endif

namespace Ms {

extern MasterSynthesizer* synti;

//---------------------------------------------------------
//   initOsc
//---------------------------------------------------------

#ifndef OSC
void MuseScore::initOsc()
      {
      }

#else // #ifndef OSC

//---------------------------------------------------------
//   initOsc
//---------------------------------------------------------

void MuseScore::initOsc()
      {
      if (!preferences.useOsc)
            return;
      int port = preferences.oscPort;
      QOscServer* osc = new QOscServer(port, qApp);

      PathObject* oo = new PathObject( "/addpitch", QVariant::Int, osc);
      QObject::connect(oo, SIGNAL(data(int)), SLOT(oscIntMessage(int)));

      oo = new PathObject( "/tempo", QVariant::Int, osc);
      QObject::connect(oo, SIGNAL(data(int)), SLOT(oscTempo(int)));
      oo = new PathObject( "/volume", QVariant::Int, osc);
      QObject::connect(oo, SIGNAL(data(int)), SLOT(oscVolume(int)));
      oo = new PathObject( "/goto", QVariant::Int, osc);
      QObject::connect(oo, SIGNAL(data(int)), SLOT(oscGoto(int)));
      oo = new PathObject( "/select-measure", QVariant::Int, osc);
      QObject::connect(oo, SIGNAL(data(int)), SLOT(oscSelectMeasure(int)));
      for (int i = 1; i <= 12; i++ ) {
            oo = new PathObject( QString("/vol%1").arg(i), QVariant::Double, osc);
            QObject::connect(oo, SIGNAL(data(double)), SLOT(oscVolChannel(double)));
            }
      for(int i = 1; i <= 12; i++ ) {
            oo = new PathObject( QString("/pan%1").arg(i), QVariant::Double, osc);
            QObject::connect(oo, SIGNAL(data(double)), SLOT(oscPanChannel(double)));
            }
      for(int i = 1; i <= 12; i++ ) {
            oo = new PathObject( QString("/mute%1").arg(i), QVariant::Double, osc);
            QObject::connect(oo, SIGNAL(data(double)), SLOT(oscMuteChannel(double)));
            }

      oo = new PathObject( "/open", QVariant::String, osc);
      QObject::connect(oo, SIGNAL(data(QString)), SLOT(oscOpen(QString)));
      oo = new PathObject( "/close-all", QVariant::Invalid, osc);
      QObject::connect(oo, SIGNAL(data()), SLOT(oscCloseAll()));

      oo = new PathObject( "/plugin", QVariant::String, osc);
      QObject::connect(oo, SIGNAL(data(QString)), SLOT(oscTriggerPlugin(QString)));

      oo = new PathObject( "/color-note", QVariant::List, osc);
      QObject::connect(oo, SIGNAL(data(QVariantList)), SLOT(oscColorNote(QVariantList)));

      for (const Shortcut* s : Shortcut::shortcuts()) {
            oo = new PathObject( QString("/actions/%1").arg(s->key().data()), QVariant::Invalid, osc);
            QObject::connect(oo, SIGNAL(data()), SLOT(oscAction()));
            }
      }

//---------------------------------------------------------
//   oscIntMessage
//---------------------------------------------------------

void MuseScore::oscIntMessage(int val)
      {
      if (val < 128) {
            midiNoteReceived(0, val, 60);
            midiNoteReceived(0, val, 0);
            }
      else
            midiCtrlReceived(val-128, 22);
      }

void MuseScore::oscAction()
      {
      PathObject* pathObject = qobject_cast<PathObject*>(sender());
      QString path = pathObject->path().mid(9);
      QAction* a = getAction(path.toLocal8Bit().data());
      a->trigger();
      }

void MuseScore::oscGoto(int m)
      {
      qDebug("GOTO %d", m);
      if (cv == 0)
            return;
      cv->searchPage(m);
      }

void MuseScore::oscSelectMeasure(int m)
      {
      qDebug("SelectMeasure %d", m);
      if (cv == 0)
            return;
      cv->selectMeasure(m);
      }


void MuseScore::oscOpen(QString path)
      {
      qDebug("Open %s", qPrintable(path));
      openScore(path);
      }


void MuseScore::oscCloseAll()
      {
      qDebug("CloseAll");
      while(cs != 0)
          closeScore(cs);
      }

//---------------------------------------------------------
//   oscTempo
//---------------------------------------------------------

void MuseScore::oscTempo(int val)
      {
      if (val < 0)
            val = 0;
      if (val > 127)
            val = 127;
      val = (val * 240) / 128;
      if (playPanel)
            playPanel->setRelTempo(val);
      if (seq)
            seq->setRelTempo(double(val));
      }

//---------------------------------------------------------
//   oscTriggerPlugin
//---------------------------------------------------------

void MuseScore::oscTriggerPlugin(QString /*s*/)
      {
#if 0 // TODO
      QStringList args = s.split(",");
      if(args.length() > 0) {
            int idx = pluginIdxFromPath(args.at(0));
            if(idx != -1) {
                  for(int i = 1; i < args.length()-1; i++) {
                        addGlobalObjectToPluginEngine(qPrintable(args.at(i)), args.at(i+1));
                        i++;
                        }
                  pluginTriggered(idx);
                  }
            }
#endif
      }

//---------------------------------------------------------
//   oscColorNote
//---------------------------------------------------------
void MuseScore::oscColorNote(QVariantList list)
      {
      qDebug() << list;
      if(!cs)
            return;
      if (list.length() != 2 && list.length() != 3)
            return;
      int tick;
      int pitch;
      QColor noteColor("red"); //default to red
      bool ok;
      tick = list[0].toInt(&ok);
      if (!ok)
            return;
      pitch = list[1].toInt(&ok);
      if (!ok)
            return;
      if(list.length() == 3 && list[2].canConvert(QVariant::String)) {
            QColor color(list[2].toString());
            if(color.isValid())
                  noteColor = color;
            }

      Measure* measure = cs->tick2measure(tick);
      if(!measure)
            return;
      Segment* s = measure->findSegment(Segment::Type::ChordRest, tick);
      if (!s)
            return;
      //get all chords in segment...
      int n = cs->nstaves() * VOICES;
      for (int i = 0; i < n; i++) {
            Element* e = s->element(i);
            if (e && e->isChordRest()) {
                  ChordRest* cr = static_cast<ChordRest*>(e);
                  if(cr->type() == Element::Type::CHORD) {
                        Chord* chord = static_cast<Chord*>(cr);
                        for (int idx = 0; idx < chord->notes().length(); idx++) {
                              Note* note = chord->notes()[idx];
                              if (note->pitch() == pitch) {
                                    cs->startCmd();
                                    cs->undo(new ChangeProperty(note, P_ID::COLOR, noteColor));
                                    cs->endCmd();
                                    return;
                                    }
                              }
                        }
                  }
            }
      }
//---------------------------------------------------------
//   oscVolume
//---------------------------------------------------------

void MuseScore::oscVolume(int val)
      {
      double v = val / 128.0;
      synti->setGain(v);
      }

//---------------------------------------------------------
//   oscVolChannel
//---------------------------------------------------------

void MuseScore::oscVolChannel(double val)
      {
      if(!cs)
            return;
      PathObject* po = (PathObject*) sender();

      int i = po->path().mid(4).toInt() - 1;
      QList<MidiMapping>* mms = cs->midiMapping();
      if( i >= 0 && i < mms->size()) {
            MidiMapping mm = mms->at(i);
            Channel* channel = mm.articulation;
            int iv = lrint(val*127);
            seq->setController(channel->channel, CTRL_VOLUME, iv);
            channel->volume = iv;
            if (mixer)
                  mixer->partEdit(i)->volume->setValue(iv);
            }
      }

//---------------------------------------------------------
//   oscPanChannel
//---------------------------------------------------------

void MuseScore::oscPanChannel(double val)
      {
      if(!cs)
            return;
      PathObject* po = (PathObject*) sender();

      int i = po->path().mid(4).toInt() - 1;
      QList<MidiMapping>* mms = cs->midiMapping();
      if( i >= 0 && i < mms->size()) {
            MidiMapping mm = mms->at(i);
            Channel* channel = mm.articulation;
            int iv = lrint((val + 1) * 64);
            seq->setController(channel->channel, CTRL_PANPOT, iv);
            channel->volume = iv;
            if (mixer)
                  mixer->partEdit(i)->pan->setValue(iv);
            }
      }

//---------------------------------------------------------
//   oscMuteChannel
//---------------------------------------------------------

void MuseScore::oscMuteChannel(double val)
      {
      if(!cs)
            return;
      PathObject* po = (PathObject*) sender();

      int i = po->path().mid(5).toInt() - 1;
      QList<MidiMapping>* mms = cs->midiMapping();
      if( i >= 0 && i < mms->size()) {
            MidiMapping mm = mms->at(i);
            Channel* channel = mm.articulation;
            channel->mute = (val==0.0f ? false : true);
            if (mixer)
                  mixer->partEdit(i)->mute->setCheckState(val==0.0f ? Qt::Unchecked:Qt::Checked);
            }
      }
#endif // #ifndef OSC
}

