//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: track.cpp,v 1.34 2004/06/18 08:36:43 wschweer Exp $
//
//  (C) Copyright 2000-2004 Werner Schweer (ws@seh.de)
//=========================================================

#include "track.h"
#include "event.h"
#include "mididev.h"
#include "midiport.h"
#include "song.h"
#include "xml.h"

bool MidiTrack::_soloMode;
bool AudioTrack::_soloMode;

//---------------------------------------------------------
//   y
//---------------------------------------------------------

int Track::y() const
      {
      TrackList* tl = song->tracks();
      int yy = 0;
      for (ciTrack it = tl->begin(); it != tl->end(); ++it) {
            if (this == *it)
                  return yy;
            yy += (*it)->height();
            }
      printf("Track::y(%s): track not in tracklist\n", name().latin1());
      return -1;
      }

//---------------------------------------------------------
//   Track::init
//---------------------------------------------------------

void Track::init()
      {
      _activity      = 0;
      _lastActivity  = 0;
      _recordFlag    = false;
      _mute          = false;
      _solo          = false;
      _off           = false;
      _channels      = 0;           // 1 - mono, 2 - stereo
      _selected      = false;
      _height        = 20;
      _locked        = false;
      for (int i = 0; i < MAX_CHANNELS; ++i) {
            _meter[i] = 0;
            _peak[i]  = 0;
            }
      }

Track::Track(Track::TrackType t)
      {
      init();
      _type = t;
      }

Track::Track(const Track& t)
      {
      _activity     = t._activity;
      _lastActivity = t._lastActivity;
      _recordFlag   = t._recordFlag;
      _mute         = t._mute;
      _solo         = t._solo;
      _off          = t._off;
      _channels     = t._channels;
      _selected     = t.selected();
      _y            = t._y;
      _height       = t._height;
      _comment      = t.comment();
      _name         = t.name();
      _type         = t.type();
      _locked       = t.locked();

      const PartList* pl = t.cparts();
      for (ciPart ip = pl->begin(); ip != pl->end(); ++ip) {
            Part* newPart = ip->second->clone();
            newPart->setTrack(this);
            _parts.add(newPart);
            }
      for (int i = 0; i < MAX_CHANNELS; ++i) {
            _meter[i] = 0;
            _peak[i]  = 0;
            }
      }

//---------------------------------------------------------
//   setDefaultName
//    generate unique name for track
//---------------------------------------------------------

void Track::setDefaultName()
      {
      QString base;
      switch(_type) {
            case MIDI:
            case DRUM:
            case WAVE:
                  base = QString("Track");
                  break;
            case AUDIO_OUTPUT:
                  base = QString("Out");
                  break;
            case AUDIO_GROUP:
                  base = QString("Group");
                  break;
            case AUDIO_AUX:
                  base = QString("Aux");
                  break;
            case AUDIO_INPUT:
                  base = QString("Input");
                  break;
            case AUDIO_SOFTSYNTH:
                  base = QString("Synth");
                  break;
            };
      base += " ";
      for (int i = 1; true; ++i) {
            QString n;
            n.setNum(i);
            QString s = base + n;
            Track* track = song->findTrack(s);
            if (track == 0) {
                  setName(s);
                  break;
                  }
            }
      }

//---------------------------------------------------------
//   dump
//---------------------------------------------------------

void Track::dump() const
      {
      printf("Track <%s>: typ %d, parts %d sel %d\n",
         _name.latin1(), _type, _parts.size(), _selected);
      }

//---------------------------------------------------------
//   MidiTrack
//---------------------------------------------------------

MidiTrack::MidiTrack()
   : Track(MIDI)
      {
      init();
      _events = new EventList;
      _mpevents = new MPEventList;
      }

MidiTrack::MidiTrack(const MidiTrack& mt)
   : Track(mt)
      {
      _outPort       = mt.outPort();
      _outChannel    = mt.outChannel();
      _inPortMask    = mt.inPortMask();
      _inChannelMask = mt.inChannelMask();
      _events        = new EventList;
      _mpevents      = new MPEventList;
      transposition  = mt.transposition;
      velocity       = mt.velocity;
      delay          = mt.delay;
      len            = mt.len;
      compression    = mt.compression;
      }

MidiTrack::~MidiTrack()
      {
      delete _events;
      delete _mpevents;
      }

//---------------------------------------------------------
//   init
//---------------------------------------------------------

void MidiTrack::init()
      {
      _outPort       = 0;
      _outChannel    = 0;
      _inPortMask    = 0xffff;
      _inChannelMask = 0xffff;      // "ALL"
      transposition  = 0;
      velocity       = 0;
      delay          = 0;
      len            = 100;          // percent
      compression    = 100;          // percent
      }

//---------------------------------------------------------
//   addPart
//---------------------------------------------------------

iPart Track::addPart(Part* p)
      {
      p->setTrack(this);
      return _parts.add(p);
      }

//---------------------------------------------------------
//   findPart
//---------------------------------------------------------

Part* Track::findPart(unsigned tick)
      {
      for (iPart i = _parts.begin(); i != _parts.end(); ++i) {
            Part* part = i->second;
            if (tick >= part->tick() && tick < (part->tick()+part->lenTick()))
                  return part;
            }
      return 0;
      }

//---------------------------------------------------------
//   newPart
//---------------------------------------------------------

Part* MidiTrack::newPart(Part*p, bool clone)
      {
      MidiPart* part = clone ? new MidiPart(this, p->events()) : new MidiPart(this);
      if (p) {
            part->setName(p->name());
            part->setColorIndex(p->colorIndex());

            *(PosLen*)part = *(PosLen*)p;
            part->setMute(p->mute());
            }
      return part;
      }

//---------------------------------------------------------
//   automationType
//---------------------------------------------------------

AutomationType MidiTrack::automationType() const
      {
      MidiPort* port = &midiPorts[outPort()];
      return port->automationType(outChannel());
      }

//---------------------------------------------------------
//   setAutomationType
//---------------------------------------------------------

void MidiTrack::setAutomationType(AutomationType t)
      {
      MidiPort* port = &midiPorts[outPort()];
      port->setAutomationType(outChannel(), t);
      }

//---------------------------------------------------------
//   Track::writeProperties
//---------------------------------------------------------

void Track::writeProperties(int level, Xml& xml) const
      {
      xml.strTag(level, "name", _name);
      if (!_comment.isEmpty())
            xml.strTag(level, "comment", _comment);
      xml.intTag(level, "record", _recordFlag);
      xml.intTag(level, "mute", mute());
      xml.intTag(level, "solo", solo());
      xml.intTag(level, "off", off());
      xml.intTag(level, "channels", _channels);
      xml.intTag(level, "height", _height);
      xml.intTag(level, "locked", _locked);
      if (_selected)
            xml.intTag(level, "selected", _selected);
      }

//---------------------------------------------------------
//   Track::readProperties
//---------------------------------------------------------

bool Track::readProperties(Xml& xml, const QString& tag)
      {
      if (tag == "name")
            _name = xml.parse1();
      else if (tag == "comment")
            _comment = xml.parse1();
      else if (tag == "record") {
            bool recordFlag = xml.parseInt();
            setRecordFlag1(recordFlag);
            setRecordFlag2(recordFlag);
            }
      else if (tag == "mute")
            _mute = xml.parseInt();
      else if (tag == "solo")
            _solo = xml.parseInt();
      else if (tag == "off")
            _off = xml.parseInt();
      else if (tag == "height")
            _height = xml.parseInt();
      else if (tag == "channels")
            _channels = xml.parseInt();
      else if (tag == "locked")
            _locked = xml.parseInt();
      else if (tag == "selected")
            _selected = xml.parseInt();
      else
            return true;
      return false;
      }

//---------------------------------------------------------
//   MidiTrack::write
//---------------------------------------------------------

void MidiTrack::write(int level, Xml& xml) const
      {
      char* tag;

      if (type() == DRUM)
            tag = "drumtrack";
      else
            tag = "miditrack";
      xml.tag(level++, tag);
      Track::writeProperties(level, xml);

      xml.intTag(level, "device", outPort());
      xml.intTag(level, "channel", outChannel());
      xml.intTag(level, "inportMap", inPortMask());
      xml.intTag(level, "inchannelMap", inChannelMask());
      xml.intTag(level, "locked", _locked);

      xml.intTag(level, "transposition", transposition);
      xml.intTag(level, "velocity", velocity);
      xml.intTag(level, "delay", delay);
      xml.intTag(level, "len", len);
      xml.intTag(level, "compression", compression);
      xml.intTag(level, "automation", int(automationType()));

      const PartList* pl = cparts();
      for (ciPart p = pl->begin(); p != pl->end(); ++p)
            p->second->write(level, xml);
      xml.etag(level, tag);
      }

//---------------------------------------------------------
//   MidiTrack::read
//---------------------------------------------------------

void MidiTrack::read(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 == "transposition")
                              transposition = xml.parseInt();
                        else if (tag == "velocity")
                              velocity = xml.parseInt();
                        else if (tag == "delay")
                              delay = xml.parseInt();
                        else if (tag == "len")
                              len = xml.parseInt();
                        else if (tag == "compression")
                              compression = xml.parseInt();
                        else if (tag == "part") {
                              Part* p = newPart();
                              p->read(xml);
                              parts()->add(p);
                              }
                        else if (tag == "device")
                              setOutPort(xml.parseInt());
                        else if (tag == "channel")
                              setOutChannel(xml.parseInt());
                        else if (tag == "inportMap")
                              setInPortMask(xml.parseInt());
                        else if (tag == "inchannelMap")
                              setInChannelMask(xml.parseInt());
                        else if (tag == "locked")
                              _locked = xml.parseInt();
                        else if (tag == "automation")
                              setAutomationType(AutomationType(xml.parseInt()));
                        else if (Track::readProperties(xml, tag)) {
                              // version 1.0 compatibility:
                              if (tag == "track" && xml.majorVersion() == 1 && xml.minorVersion() == 0)
                                    break;
                              xml.unknown("MidiTrack");
                              }
                        break;
                  case Xml::Attribut:
                        break;
                  case Xml::TagEnd:
                        if (tag == "miditrack" || tag == "drumtrack") {
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

