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

#include "undo.h"
#include "song.h"
#include "globals.h"
#include "event.h"

// iundo points to last Undo() in Undo-list

//---------------------------------------------------------
//    startUndo
//---------------------------------------------------------

void Song::startUndo()
      {
      undoList->push_back(Undo());
      updateFlags = 0;
      }

//---------------------------------------------------------
//   endUndo
//---------------------------------------------------------

void Song::endUndo(int flags)
      {
      updateFlags = flags;
      endMsgCmd();
      }

//---------------------------------------------------------
//   undo
//---------------------------------------------------------

void Song::doUndo()
      {
      if (undoList->empty())
            return;
      Undo& u = undoList->back();
      for (riUndoOp i = u.rbegin(); i != u.rend(); ++i) {
            switch(i->type) {
                  case UndoOp::AddTrack:
                        {
                        iTrack it = _tracks.find(i->xxx.t.trackno);
                        _tracks.erase(it);
                        updateFlags |= SC_TRACK_REMOVED;
                        if ((*it)->type() == Track::WAVE)
                              updateFlags |= SC_AUDIO_MIXER;
                        }
                        break;
                  case UndoOp::DeleteTrack:
                        {
                        i->xxx.t.track->setSelected(0);
                        iTrack it = _tracks.find(i->xxx.t.trackno);
                        _tracks.insert(it, i->xxx.t.track);
                        updateFlags |= SC_TRACK_INSERTED;
                        if (i->xxx.t.track->type() == Track::WAVE)
                              updateFlags |= SC_AUDIO_MIXER;
                        }
                        break;
                  case UndoOp::ModifyTrack:
                        {
                        iTrack it = _tracks.find(i->xxx.t.trackno);
                        Track* track = *it;
                        *it = i->xxx.t.track;
                        i->xxx.t.track = track;
                        updateFlags |= SC_TRACK_MODIFIED;
                        if (i->xxx.t.track->type() == Track::WAVE)
                              updateFlags |= SC_AUDIO_MIXER;
                        }
                        break;
                  case UndoOp::SwapTrack:
                        {
                        iTrack ita = _tracks.find(i->xxx.i.a);
                        iTrack itb = _tracks.find(i->xxx.i.b);
                        Track* track = *ita;
                        *ita = *itb;
                        *itb = track;
                        updateFlags |= SC_TRACK_MODIFIED;
                        }
                        break;
                  case UndoOp::AddPart:
                        {
                        Part* part = i->xxx.p.oPart;
                        part->track()->parts()->remove(part);
                        updateFlags |= SC_PART_REMOVED;
                        i->xxx.p.oPart->events()->incARef(-1);
                        }
                        break;
                  case UndoOp::DeletePart:
                        addPart(i->xxx.p.oPart);
                        updateFlags |= SC_PART_INSERTED;
                        i->xxx.p.oPart->events()->incARef(1);
                        break;
                  case UndoOp::ModifyPart:
                        changePart(i->xxx.p.oPart, i->xxx.p.nPart);
                        i->xxx.p.oPart->events()->incARef(-1);
                        i->xxx.p.nPart->events()->incARef(1);
                        updateFlags |= SC_PART_MODIFIED;
                        break;
                  case UndoOp::AddEvent:
                        deleteEvent((MidiEvent*)i->xxx.e.nEvent, (MidiPart*)i->xxx.e.part);
                        updateFlags |= SC_EVENT_REMOVED;
                        break;
                  case UndoOp::DeleteEvent:
                        addEvent((MidiEvent*)i->xxx.e.nEvent, (MidiPart*)(i->xxx.e.part));
                        updateFlags |= SC_EVENT_INSERTED;
                        break;
                  case UndoOp::ModifyEvent:
                        updateFlags |= SC_EVENT_MODIFIED;
                        changeEvent((MidiEvent*)i->xxx.e.oEvent, (MidiEvent*)i->xxx.e.nEvent, (MidiPart*)i->xxx.e.part);
                        break;
                  case UndoOp::ModifyController:
                        updateFlags |= SC_TRACK_MODIFIED;
//TODO                        midiChannels[i->xxx.channel].iState.controller[i->xxx.ctrl] = i->oVal;
                        break;
                  case UndoOp::AddTempo:
                        tempomap.delTempo(i->xxx.i.a);
                        updateFlags |= SC_TEMPO;
                        break;
                  case UndoOp::DeleteTempo:
                        tempomap.addTempo(i->xxx.i.a, i->xxx.i.b);
                        updateFlags |= SC_TEMPO;
                        break;
                  case UndoOp::AddSig:
                        sigmap.del(i->xxx.i.a);
                        updateFlags |= SC_SIG;
                        break;
                  }
            }
      redoList->push_back(u); // put item on redo list
      undoList->pop_back();
      dirty = true;
      }

//---------------------------------------------------------
//   Song::redo
//---------------------------------------------------------

void Song::doRedo()
      {
      if (redoList->empty())
            return;
      Undo& u = redoList->back();
      for (iUndoOp i = u.begin(); i != u.end(); ++i) {
            switch(i->type) {
                  case UndoOp::AddTrack:
                        {
                        i->xxx.t.track->setSelected(0);
                        iTrack it = _tracks.find(i->xxx.t.trackno);
                        _tracks.insert(it, i->xxx.t.track);
                        updateFlags |= SC_TRACK_INSERTED;
                        if (i->xxx.t.track->type() == Track::WAVE)
                              updateFlags |= SC_AUDIO_MIXER;
                        }
                        break;
                  case UndoOp::DeleteTrack:
                        {
                        iTrack it = _tracks.find(i->xxx.t.trackno);
                        _tracks.erase(it);
                        updateFlags |= SC_TRACK_REMOVED;
                        if ((*it)->type() == Track::WAVE)
                              updateFlags |= SC_AUDIO_MIXER;
                        }
                        break;
                  case UndoOp::ModifyTrack:
                        {
                        iTrack it = _tracks.find(i->xxx.t.trackno);
                        Track* track = *it;
                        *it = i->xxx.t.track;
                        i->xxx.t.track = track;
                        updateFlags |= SC_TRACK_MODIFIED;
                        if ((*it)->type() == Track::WAVE)
                              updateFlags |= SC_AUDIO_MIXER;
                        }
                        break;
                  case UndoOp::SwapTrack:
                        {
                        iTrack ita = _tracks.find(i->xxx.i.a);
                        iTrack itb = _tracks.find(i->xxx.i.b);
                        Track* track = *ita;
                        *ita = *itb;
                        *itb = track;
                        updateFlags |= SC_TRACK_MODIFIED;
                        }
                        break;
                  case UndoOp::AddPart:
                        addPart(i->xxx.p.oPart);
                        updateFlags |= SC_PART_INSERTED;
                        i->xxx.p.oPart->events()->incARef(1);
                        break;
                  case UndoOp::DeletePart:
                        {
                        Part* part = i->xxx.p.oPart;
                        part->track()->parts()->remove(part);
                        updateFlags |= SC_PART_REMOVED;
                        i->xxx.p.oPart->events()->incARef(-1);
                        }
                        break;
                  case UndoOp::ModifyPart:
                        changePart(i->xxx.p.nPart, i->xxx.p.oPart);
                        updateFlags |= SC_PART_MODIFIED;
                        i->xxx.p.oPart->events()->incARef(1);
                        i->xxx.p.nPart->events()->incARef(-1);
                        break;
                  case UndoOp::AddEvent:
                        addEvent((MidiEvent*)i->xxx.e.nEvent, (MidiPart*)(i->xxx.e.part));
                        updateFlags |= SC_EVENT_INSERTED;
                        break;
                  case UndoOp::DeleteEvent:
                        deleteEvent((MidiEvent*)i->xxx.e.nEvent, (MidiPart*)i->xxx.e.part);
                        updateFlags |= SC_EVENT_REMOVED;
                        break;
                  case UndoOp::ModifyEvent:
                        changeEvent((MidiEvent*)i->xxx.e.nEvent, (MidiEvent*)i->xxx.e.oEvent, (MidiPart*)i->xxx.e.part);
                        updateFlags |= SC_EVENT_MODIFIED;
                        break;
                  case UndoOp::ModifyController:
                        updateFlags |= SC_TRACK_MODIFIED;
//TODO                        midiChannels[i->xxx.channel].iState.controller[i->xxx.ctrl] = i->xxx.nVal;
                        break;
                  case UndoOp::AddTempo:
                        tempomap.addTempo(i->xxx.i.a, i->xxx.i.b);
                        updateFlags |= SC_TEMPO;
                        break;
                  case UndoOp::DeleteTempo:
                        tempomap.delTempo(i->xxx.i.a);
                        updateFlags |= SC_TEMPO;
                        break;
                  case UndoOp::AddSig:
                        sigmap.add(i->xxx.i.a, i->xxx.i.b, i->xxx.i.c);
                        updateFlags |= SC_SIG;
                        break;
                  }
            }
      undoList->push_back(u); // put item on undo list
      redoList->pop_back();
      dirty = true;
      }

void Song::undoOp(UndoOp::UndoType type, int a, int b, int c)
      {
      UndoOp i;
      i.type = type;
      i.xxx.i.a  = a;
      i.xxx.i.b  = b;
      i.xxx.i.c  = c;
      undoList->back().push_back(i);
      dirty = true;
      }

void Song::undoOp(UndoOp::UndoType type, int trackno, Track* track)
      {
      UndoOp i;
      i.type = type;
      i.xxx.t.trackno = trackno;
      i.xxx.t.track   = track;
      undoList->back().push_back(i);
      dirty = true;
      }

void Song::undoOp(UndoOp::UndoType type, Part* part)
      {
      UndoOp i;
      i.type    = type;
      i.xxx.p.oPart = part;
      undoList->back().push_back(i);
      dirty = true;
      }

void Song::undoOp(UndoOp::UndoType type, Event* oev, Event* nev, Part* part)
      {
      UndoOp i;
      i.type     = type;
      i.xxx.e.nEvent = nev;
      i.xxx.e.oEvent = oev;
      i.xxx.e.part   = part;
      if (oev)
            oev->incRefs(1);
      if (nev)
            nev->incRefs(1);
      undoList->back().push_back(i);
      dirty = true;
      }

void Song::undoOp(UndoOp::UndoType type, Part* oPart, Part* nPart)
      {
      UndoOp i;
      i.type    = type;
      i.xxx.p.oPart = nPart;
      i.xxx.p.nPart = oPart;
      undoList->back().push_back(i);
      dirty = true;
      }

void Song::undoOp(UndoOp::UndoType type, int c, int ctrl, int ov, int nv)
      {
      UndoOp i;
      i.type      = type;
      i.xxx.c.channel = c;
      i.xxx.c.ctrl    = ctrl;
      i.xxx.c.oVal    = ov;
      i.xxx.c.nVal    = nv;
      undoList->back().push_back(i);
      dirty = true;
      }

void Song::undoOp(UndoOp::UndoType type, SigEvent* oevent, SigEvent* nevent)
      {
      UndoOp i;
      i.type         = type;
      i.xxx.s.oSignature = oevent;
      i.xxx.s.nSignature = nevent;
      undoList->back().push_back(i);
      dirty = true;
      }

