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

#include "song.h"
#include "track.h"
#include "undo.h"
#include "seq.h"
#include "key.h"
#include "midiport.h"
#include "globals.h"
#include "event.h"
#include "mpevent.h"
#include "drummap.h"
#include "marker/marker.h"
#include "minstrument.h"
#include "wave.h"

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <qapplication.h>

#define A_LYRICS 0
#define OFF 24

//---------------------------------------------------------
//   Event
//---------------------------------------------------------

Event::Event()
      {
      refs      = 0;
      _selected = false;
      }

Event::Event(int t, int l)
      {
      refs     = 0;
      setPosTick(t);
      setLenTick(l);
      _selected = false;
      }
Event::Event(const Event& ev)
  : PosLen(ev)
      {
      refs      = 0;
      _selected = ev._selected;
      }

void Event::move(int offset)
      {
      setPosTick(posTick()+offset);
      }

//---------------------------------------------------------
//   MidiEvent
//---------------------------------------------------------

MidiEvent::MidiEvent()
      {
      _type    = NoEvent;
      edata    = 0;
      _enh     = 0;
      a        = 0;
      b        = 0;
      c        = 0;
      _voice   = 0;
      _stem    = 0;
      _trk     = 0;
      }

MidiEvent::MidiEvent(int tick, EventType t,
   int _a, int _b, int _c, int len) : Event(tick, len)
      {
      _type    = t;
      edata    = 0;
      a        = _a;
      b        = _b;
      c        = _c;         // velo off
      _enh     = 0;
      _voice   = 0;
      _stem    = 0;
      _trk     = 0;
      }

MidiEvent::MidiEvent(const MidiPlayEvent* ev)
   : Event()
      {
      edata    = 0;
      c        = 0;         // velo off
      _enh     = 0;
      _voice   = 0;
      _stem    = 0;
      _trk     = 0;
      switch(ev->type()) {
            case 0x80:  _type = NoteOff; break;
            case 0x90:  _type = Note;    break;
            case 0xa0:  _type = PAfter;  break;
            case 0xb0:  _type = Ctrl7;   break;
            case 0xc0:  _type = Program; break;
            case 0xd0:  _type = CAfter;  break;
            case 0xe0:
                  _type = Pitch;
                  a = ((ev->dataB() << 7) + ev->dataA()) - 8192;
                  b = 0;
                  return;
            case 0xf0:
                  edata = ev->eventData();
                  ++(edata->refCount);
                  _type = Sysex;
                  break;
            }
      a = ev->dataA();
      b = ev->dataB();
      }

MidiEvent::MidiEvent(int tick, EventType t,
   int len, const unsigned char* ptr) : Event(tick, 0)
      {
      _type    = t;
      edata    = new EvData(ptr, len);
      edata->refCount = 1;
      _enh     = 0;
      a        = 0;
      b        = 0;
      c        = 0;
      _voice   = 0;
      _stem    = 0;
      _trk     = 0;
      }

MidiEvent::MidiEvent(const MidiEvent& ev)
   : Event(ev)
      {
      _type      = ev._type;
      a          = ev.a;
      b          = ev.b;
      c          = ev.c;
      _enh       = ev._enh;
      _voice     = ev._voice;
      _stem      = ev._stem;
      _trk       = ev._trk;
      attributes = ev.attributes;
      edata      = ev.edata;
      if (edata)
            ++(edata->refCount);
      }

MidiEvent::~MidiEvent()
      {
      if (edata && (--(edata->refCount) == 0))
            delete edata;
      }

//---------------------------------------------------------
//   MidiEvent::mid
//---------------------------------------------------------

Event* MidiEvent::mid(int b, int e)
      {
      if (posTick() < b || posTick() >= e)
            return 0;
      return new MidiEvent(*this);
      }

//---------------------------------------------------------
//   WaveEvent
//---------------------------------------------------------

WaveEvent::WaveEvent()
      {
      setType(SAMPLES);
      _clip = 0;
      }

WaveEvent::WaveEvent(int tick, Clip* cl)
   : Event(tick, 0)
      {
      setType(SAMPLES);
      setLenSample(cl->samples());
      _clip = cl;
      }

//---------------------------------------------------------
//   WaveEvent::mid
//---------------------------------------------------------

Event* WaveEvent::mid(int b, int e)
      {
      WaveEvent* ev = new WaveEvent(*this);

      int offset = b - posSample();
      int end    = endSample();
      if (e < end)
            end = e;

      int len = end - b;
      Clip* cl = new Clip(_clip->file1(), _clip->spos()+offset, len);
      cl->incRef();
      ev->setClip(cl);
      ev->setPosSample(b);
      ev->setLenSample(len);
      return ev;
      }

//---------------------------------------------------------
//   addLyrics
//---------------------------------------------------------

void AttributeList::addLyrics(const QString& s)
      {
      Attribute a(A_LYRICS, s);
      add(Attribute(A_LYRICS, s));
      }

//---------------------------------------------------------
//   isNoteOff
//    checkt, ob 'e' der Off-Event für die Note 'this' ist
//---------------------------------------------------------

bool MidiEvent::isNoteOff(MidiEvent* e)
      {
      return (e->isNoteOff() && (e->pitch() == a));
      }

//---------------------------------------------------------
//   Event::dump
//---------------------------------------------------------

void Event::dump() const
      {
      printf("Event %p refs:%d ", this, refs);
      PosLen::dump();
      }

void WaveEvent::dump() const
      {
      Event::dump();
      }

void MidiEvent::dump() const
      {
      Event::dump();
      char* p;
      switch(_type) {
            case NoEvent:   p = "No Event"; break;
            case Note:      p = "Note    "; break;
            case NoteOff:   p = "NoteOff "; break;
            case Program:   p = "Program "; break;
            case Ctrl7:     p = "Ctrl7   "; break;
            case Ctrl14:    p = "Ctrl14  "; break;
            case Sysex:     p = "Sysex   "; break;
            case PAfter:    p = "PAfter  "; break;
            case CAfter:    p = "CAfter  "; break;
            case Pitch:     p = "Pitch   "; break;
            case Meta:      p = "Meta    "; break;
            case NRPN:      p = "NRPN    "; break;
            case RPN:       p = "RPN     "; break;
            default:        p = "??      "; break;
            }
      printf("  <%s> a:0x%x(%d) b:0x%x(%d) c:0x%x(%d), trk:%p\n",
         p, a, a, b, b, c, c, _trk);
      if (edata) {
            for (int i = 0; i < edata->dataLen; ++i) {
                  printf("%02x ", edata->data[i]);
                  }
            printf("\n");
            }

//      char _enh;                    // enharmonische Verwechslung 0 - 4
//      unsigned char* _data;
      }

//---------------------------------------------------------
//   readEventList
//---------------------------------------------------------

void EventList::read(Xml& xml, const char* name)
      {
      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 == "event") {
                              Event* e = new MidiEvent();
                              e->read(xml);
                              add(e);
                              }
                        else
                              xml.unknown("readEventList");
                        break;
                  case Xml::TagEnd:
                        if (tag == name)
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   EventList
//---------------------------------------------------------

EventList::~EventList()
      {
      clear();
      }

//---------------------------------------------------------
//   add
//---------------------------------------------------------

iEvent EventList::add(Event* event, int tick)
      {
      event->incRefs(1);
      return std::multimap<int, Event*, std::less<int> >::insert(std::pair<const int, Event*> (tick, event));
      }

//---------------------------------------------------------
//   move
//---------------------------------------------------------

void EventList::move(Event* event, int tick)
      {
      iEvent i = find(event);
      erase(i);
      std::multimap<int, Event*, std::less<int> >::insert(std::pair<const int, Event*> (tick, event));
      }

//---------------------------------------------------------
//   erase
//---------------------------------------------------------

void EventList::erase(iEvent i)
      {
      if (i->second->incRefs(-1) <= 0) {
            delete i->second;
            }
      std::multimap<int, Event*, std::less<int> >::erase(i);
      }

void EventList::erase(iEvent istart, iEvent iend)
      {
      for (iEvent i = istart; i != iend; ++i) {
            if (i->second->incRefs(-1) <= 0) {
                  delete i->second;
                  }
            }
      std::multimap<int, Event*, std::less<int> >::erase(istart, iend);
      }

void EventList::insert(iEvent istart, iEvent iend)
      {
      for (iEvent i = istart; i != iend; ++i) {
            i->second->incRefs(1);
            }
      std::multimap<int, Event*, std::less<int> >::insert(istart, iend);
      }

//---------------------------------------------------------
//   clear
//---------------------------------------------------------

void EventList::clear()
      {
      erase(begin(), end());
      }

//---------------------------------------------------------
//   find
//---------------------------------------------------------

iEvent EventList::find(Event* event)
      {
      EventRange range = equal_range(event->posTick());
      for (iEvent i = range.first; i != range.second; ++i) {
            if (i->second == event)
                  return i;
            }
      return end();
      }

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

void EventList::dump() const
      {
      for (ciEvent i = begin(); i != end(); ++i)
            i->second->dump();
      }

//---------------------------------------------------------
//   MidiEvent::write
//---------------------------------------------------------

void MidiEvent::write(int level, Xml& xml, int offset) const
      {
      xml.nput(level++, "<event tick=\"%d\"", posTick() + offset);
      switch(_type) {
            case Note:
                  xml.nput(" len=\"%d\"", lenTick());
                  break;
            default:
                  xml.nput(" type=\"%d\"", _type);
                  break;
            }
      if (edata) {
            xml.nput(" datalen=\"%d\">\n", edata->dataLen);
            xml.nput(level, "");
            for (int i = 0; i < edata->dataLen; ++i)
                  xml.nput("%02x ", edata->data[i] & 0xff);
            xml.nput("\n");
            xml.tag(level, "/event");
            }
      else {
            if (_enh)
                  xml.nput(" enh=\"%d\"", _enh);
            if (a)
                  xml.nput(" a=\"%d\"", a);
            if (b)
                  xml.nput(" b=\"%d\"", b);
            if (c)
                  xml.nput(" c=\"%d\"", c);
            if (_voice)
                  xml.nput(" voice=\"%d\"", _voice);
            if (_stem)
                  xml.nput(" stem=\"%d\"", _stem);
            if (!attributes.empty()) {
                  xml.nput(">\n");
                  ++level;
                  for (ciAttribute i = attributes.begin(); i!= attributes.end(); ++i) {
                        const Attribute* a = &*i;
                        xml.nput(level, "<attribute type=\"%d\"",
                           a->type());
                        if (a->pos().x())
                              xml.nput(" x=\"%d\"", a->pos().x());
                        if (a->pos().y())
                              xml.nput(" y=\"%d\"", a->pos().y());
                        xml.nput(">");
                        switch(a->type()) {
                              case A_LYRICS:
                                    if (!a->text().isEmpty()) {
                                          xml.nput("%s", a->text().latin1());
                                          }
                              default:
                                    xml.nput("</attribute>\n");
                                    break;
                              }
                        }
                  xml.tag(level, "/event");
                  --level;
                  }
            else {
                  xml.nput(" />\n");
                  }
            }
      }

//---------------------------------------------------------
//   Event::readAttr
//---------------------------------------------------------

void MidiEvent::readAttr(Xml& xml)
      {
      int type = 0;
      int x = 0;
      int y = 0;
      QString s;
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::TagStart:
                        xml.unknown("Event");
                        break;
                  case Xml::Text:
                        s = tag;
                        break;
                  case Xml::Attribut:
                        if (tag == "x")
                              x = xml.s2().toInt();
                        else if (xml.s1() == "y")
                              y = xml.s2().toInt();
                        else if (xml.s1() == "type")
                              type = xml.s2().toInt();
                        break;
                  case Xml::TagEnd:
                        if (tag == "attribute") {
                              addAttr(Attribute(type, x, y, s));
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   Event::read
//---------------------------------------------------------

void MidiEvent::read(Xml& xml)
      {
      _type  = Note;
      edata  = 0;
      a      = 0;
      b      = 0;
      c      = 0;
      _voice = 0;
      _enh   = 0;
      _stem  = 0;

      int dataLen = 0;
      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 == "attribute") {
                              readAttr(xml);
                              }
                        else
                              xml.unknown("Event");
                        break;
                  case Xml::Text:
                        {
                        const char*s = tag.latin1();
                        edata = new EvData;
                        edata->data = new unsigned char[dataLen];
                        edata->dataLen = dataLen;
                        edata->refCount = 1;
                        unsigned char* d = edata->data;
// printf("read SYSEX <%s>\n", s);
                        for (int i = 0; i < dataLen; ++i) {
                              char* endp;
                              *d++ = strtol(s, &endp, 16);
                              s = endp;
                              }
                        }
                        break;
                  case Xml::Attribut:
                        if (tag == "tick")
                              setPosTick(xml.s2().toInt());
                        else if (tag == "type")
                              _type = EventType(xml.s2().toInt());
                        else if (tag == "len")
                              setLenTick(xml.s2().toInt());
                        else if (tag == "a")
                              a = xml.s2().toInt();
                        else if (tag == "b")
                              b = xml.s2().toInt();
                        else if (tag == "enh")
                              _enh = xml.s2().toInt();
                        else if (tag == "c")
                              c = xml.s2().toInt();
                        else if (tag == "voice")
                              _voice = xml.s2().toInt();
                        else if (tag == "stem")
                              _stem = xml.s2().toInt();
                        else if (tag == "datalen")
                              dataLen = xml.s2().toInt();
                        break;
                  case Xml::TagEnd:
                        if (tag == "event")
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   WaveEvent::read
//---------------------------------------------------------

void WaveEvent::read(Xml& xml)
      {
      _clip = 0;
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                  case Xml::Attribut:
                        return;
                  case Xml::TagStart:
                        if (tag == "poslen")
                              PosLen::read(xml, "poslen");
                        else if (tag == "clip") {
                              const QString& name = xml.parse1();
                              _clip = waveClips->search(name);
                              if (_clip)
                                    _clip->incRef();
                              }
                        else
                              xml.unknown("Event");
                        break;
                  case Xml::TagEnd:
                        if (tag == "event") {
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   write
//---------------------------------------------------------

void WaveEvent::write(int level, Xml& xml, int offset) const
      {
      xml.tag(level++, "event");
      xml.strTag(level, "clip", _clip->name());
      PosLen wpos(*this);
      if (offset)
            wpos.setPosTick(wpos.posTick() + offset);
      wpos.write(level, xml, "poslen");
      xml.etag(level, "event");
      }

//---------------------------------------------------------
//   clone
//---------------------------------------------------------

Event* WaveEvent::clone()
      {
      _clip->incRef();
      return new WaveEvent(*this);
      }

MidiEvent& MidiEvent::operator=(const MidiEvent& ev)
      {
      printf("OPERATOR = MidiEvent\n");
      abort();
#if 0
      if (edata == ev.edata)
            return *this;
      if (edata && (--(edata->refCount) == 0))
            delete edata;
      memcpy(this, &ev, sizeof(MidiPlayEvent));
      if (edata)
            (edata->refCount)++;
#endif
      return *this;
      }

