//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: part.cpp,v 1.2 2003/10/29 22:12:47 spamatica Exp $
//
//  (C) Copyright 1999/2000 Werner Schweer (ws@seh.de)
//=========================================================

#include <stdio.h>
#include <assert.h>
#include <cmath>

#include "song.h"
#include "part.h"
#include "track.h"
#include "globals.h"
#include "event.h"
#include "midithread.h"

int Part::snGen;

PartColor partColors[NUM_PARTCOLORS] = {
      { "White",        0xff, 0xff, 0xff },
      { "Refrain",      0xff, 0x00, 0x00 },
      { "Bridge",       0x00, 0xff, 0x00 },
      { "Intro",        0x00, 0x00, 0xff },
      { "Coda",         0xff, 0xff, 0x00 },
      { "Chorus",       0x00, 0xff, 0xff },
      { "Solo",         0xff, 0x00, 0xff },
      { "Brass",        0x9f, 0xc7, 0xef },
      { "Percussion",   0x00, 0xff, 0x7f },
      { "Drums",        0x7f, 0x00, 0x00 },
      { "Guitar",       0x00, 0x7f, 0x00 },
      { "Bass",         0x00, 0x00, 0x7f },
      { "Flute",        0x7f, 0x7f, 0x3f },
      { "Strings",      0x00, 0x7f, 0x7f },
      { "Keyboard",     0x7f, 0x00, 0x7f },
      { "Piano",        0x00, 0x7f, 0xff },
      { "Saxophon",     0x00, 0x3f, 0x3f },
      };

//---------------------------------------------------------
//   addEvent
//---------------------------------------------------------

iEvent Part::addEvent(Event* p)
      {
      return _events->add(p);
      }

//---------------------------------------------------------
//   index
//---------------------------------------------------------

int PartList::index(Part* part)
      {
      int index = 0;
      for (iPart i = begin(); i != end(); ++i, ++index)
            if (i->second == part) {
                  return index;
                  }
      printf("PartList::index(): not found!\n");
      return 0;
      }

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

Part* PartList::find(int idx)
      {
      int index = 0;
      for (iPart i = begin(); i != end(); ++i, ++index)
            if (index == idx)
                  return i->second;
      return 0;
      }

//---------------------------------------------------------
//   Part
//---------------------------------------------------------

Part::Part(Track* t)
      {
      setSn(newSn());
      _track      = t;
      _selected   = false;
      _mute       = false;
      _colorIndex = 0;
      _events     = new EventList;
      _events->incRef(1);
      _events->incARef(1);
      }

//---------------------------------------------------------
//   Part
//---------------------------------------------------------

Part::Part(Track* t, EventList* ev)
      {
      setSn(newSn());
      _track      = t;
      _selected   = false;
      _mute       = false;
      _colorIndex = 0;
      _events     = ev;
      _events->incRef(1);
      _events->incARef(1);
      }

//---------------------------------------------------------
//   WavePart
//---------------------------------------------------------

WavePart::WavePart(WaveTrack* t)
   : Part(t)
      {
      setType(SAMPLES);
      }

WavePart::WavePart(WaveTrack* t, EventList* ev)
   : Part(t, ev)
      {
      setType(SAMPLES);
      }

//---------------------------------------------------------
//   Part
//---------------------------------------------------------

Part::~Part()
      {
      _events->incRef(-1);
      if (_events->refCount() <= 0)
            delete _events;
      }

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

iPart PartList::findPart(int tick)
      {
      iPart i;
      for (i = begin(); i != end(); ++i)
            if (i->second->posTick() == tick)
                  break;
      return i;
      }

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

iPart PartList::add(Part* part)
      {
      return insert(std::pair<const int, Part*> (part->posTick(), part));
      }

//---------------------------------------------------------
//   remove
//---------------------------------------------------------

void PartList::remove(Part* part)
      {
      iPart i;
      for (i = begin(); i != end(); ++i) {
            if (i->second == part) {
                  erase(i);
                  break;
                  }
            }
      assert(i != end());
      }

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

void Song::addPart(Part* part)
      {

      if( part->track()->type() != Track::WAVE ) {
      // adjust song len:
      int epos = part->posTick() + part->lenTick();
      if (epos > len())
      setLen(epos);
      }

      Track* track = part->track();
      track->addPart(part);
      }

//---------------------------------------------------------
//   cmdResizePart
//---------------------------------------------------------

void Song::cmdResizePart(Track* track, Part* oPart, int len)
      {
      switch(track->type()) {
            case Track::WAVE:
                  printf("wave part resize not implemented\n");
                  break;
            case Track::MIDI:
            case Track::DRUM:
                  {
                  startUndo();
                  MidiPart* nPart = new MidiPart(*(MidiPart*)oPart);
                  nPart->setLenTick(len);

                  //
                  // cut Events in nPart
                  if (oPart->lenTick() > len) {
                        EventList* el = nPart->events();
                        iEvent ie = el->lower_bound(len);
                        for (; ie != el->end();) {
                              iEvent i = ie;
                              ++ie;
                              midiThread->msgDeleteEvent((MidiEvent*)i->second, nPart, false);
                              }
                        }
                  midiThread->msgChangePart(oPart, nPart, false);
                  endUndo(SC_PART_MODIFIED);
                  break;
                  }
            }
      }

//---------------------------------------------------------
//   splitPart
//    split part "part" at "tick" position
//    create two new parts p1 and p2
//---------------------------------------------------------

void Track::splitPart(Part* part, int tickpos, Part*& p1, Part*& p2)
      {
      int l1 = 0;       // len of first new part (ticks or samples)
      int l2 = 0;       // len of second new part

      int samplepos = lrint(tempomap.tick2time(tickpos) * sampleRate);

      switch (type()) {
            case WAVE:
                  l1 = samplepos - part->posSample();
                  l2 = part->lenSample() - l1;
                  break;
            case MIDI:
            case DRUM:
                  l1 = tickpos - part->posTick();
                  l2 = part->lenTick() - l1;
                  break;
            }

      if (l1 <= 0 || l2 <= 0)
            return;

      p1 = newPart(part);     // new left part
      p2 = newPart(part);     // new right part

      switch (type()) {
            case WAVE:
                  p1->setLenSample(l1);
                  p2->setPosSample(samplepos);
                  p2->setLenSample(l2);
                  break;
            case MIDI:
            case DRUM:
                  p1->setLenTick(l1);
                  p2->setPosTick(tickpos);
                  p2->setLenTick(l2);
                  break;
            }

      p2->setSn(p2->newSn());

      EventList* se  = part->events();
      EventList* de1 = p1->events();
      EventList* de2 = p2->events();

      if (type() == WAVE) {
            int ps   = part->posSample();
            int d1p1 = p1->posSample();
            int d2p1 = p1->endSample();
            int d1p2 = p2->posSample();
            int d2p2 = p2->endSample();
            for (iEvent ie = se->begin(); ie != se->end(); ++ie) {
                  Event* event = ie->second;
                  int s1 = event->posSample() + ps;
                  int s2 = event->endSample() + ps;

                  if ((s2 > d1p1) && (s1 < d2p1)) {
                        Event* si = event->mid(d1p1 - ps, d2p1 - ps);
                        de1->add(si);
                        }
                  if ((s2 > d1p2) && (s1 < d2p2)) {
                        Event* si = event->mid(d1p2 - ps, d2p2 - ps);
                        si->setPosSample(si->posSample() - l1);
                        si->setPosSample(0);
                        de2->add(si);
                        }
                  }
            }
      else {
            for (iEvent ie = se->begin(); ie != se->end(); ++ie) {
                  Event* event = ie->second->clone();
                  int t = event->posTick();
                  if (t >= l1) {
                        event->move(-l1);
                        de2->add(event);
                        }
                  else
                        de1->add(event);
                  }
            }
      }

//---------------------------------------------------------
//   cmdSplitPart
//---------------------------------------------------------

void Song::cmdSplitPart(Track* track, Part* part, int tick)
      {
      int l1 = tick - part->posTick();
      int l2 = part->lenTick() - l1;
      if (l1 <= 0 || l2 <= 0)
            return;
      Part* p1;
      Part* p2;
      track->splitPart(part, tick, p1, p2);

      startUndo();
      midiThread->msgChangePart(part, p1, false);
      midiThread->msgAddPart(p2, false);
      endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED);
      }

//---------------------------------------------------------
//   changePart
//---------------------------------------------------------

void Song::changePart(Part* oPart, Part* nPart)
      {
      nPart->setSn(oPart->sn());

      Track* oTrack = oPart->track();
      Track* nTrack = nPart->track();

      switch(oTrack->type()) {
            case Track::WAVE:
                  oTrack->parts()->remove(oPart);
                  nTrack->parts()->add(nPart);
                  break;
            case Track::MIDI:
            case Track::DRUM:
                  {
                  oTrack->parts()->remove(oPart);
                  nTrack->parts()->add(nPart);
                  }
                  break;
            }
      }

//---------------------------------------------------------
//   cmdGluePart
//---------------------------------------------------------

void Song::cmdGluePart(Track* track, Part* oPart)
      {
      PartList* pl   = track->parts();
      Part* nextPart = 0;

      for (iPart ip = pl->begin(); ip != pl->end(); ++ip) {
            if (ip->second == oPart) {
                  ++ip;
                  if (ip == pl->end())
                        return;
                  nextPart = ip->second;
                  break;
                  }
            }

      Part* nPart = track->newPart(oPart);
      nPart->setLenTick(nextPart->posTick()+nextPart->lenTick()-oPart->posTick());

      // populate nPart with Events from oPart and nextPart

      EventList* sl1 = oPart->events();
      EventList* sl2 = nextPart->events();
      EventList* dl  = nPart->events();
      int tickOffset = nextPart->posTick() - oPart->posTick();
      for (iEvent ie = sl1->begin(); ie != sl1->end(); ++ie)
            dl->add(ie->second);
      for (iEvent ie = sl2->begin(); ie != sl2->end(); ++ie) {
            Event* event = ie->second->clone();
            event->move(tickOffset);
            dl->add(event);
            }
      startUndo();
      midiThread->msgRemovePart(nextPart, false);
      midiThread->msgChangePart(oPart, nPart, false);
      endUndo(SC_PART_MODIFIED | SC_PART_REMOVED);
      }

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

void Part::dump() const
      {
      printf("Part: <%s> ", _name.latin1());
      PosLen::dump();
      }

void WavePart::dump() const
      {
      Part::dump();
      printf("  WavePart\n");
      }

void MidiPart::dump() const
      {
      Part::dump();
      printf("  MidiPart\n");
      }

Event* WavePart::newEvent() const
      {
      return new WaveEvent();
      }

Event* MidiPart::newEvent() const
      {
      return new MidiEvent();
      }

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

MidiPart* MidiPart::clone() const
      {
      return new MidiPart(*this);
      }

WavePart* WavePart::clone() const
      {
      return new WavePart(*this);
      }

