//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2012-2016 Werner Schweer
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2
//  as published by the Free Software Foundation and appearing in
//  the file LICENCE.GPL
//=============================================================================

#include "range.h"
#include "measure.h"
#include "segment.h"
#include "rest.h"
#include "chord.h"
#include "score.h"
#include "slur.h"
#include "tie.h"
#include "note.h"
#include "tuplet.h"
#include "barline.h"
#include "utils.h"
#include "staff.h"
#include "excerpt.h"
#include "repeat.h"

namespace Ms {

//---------------------------------------------------------
//   cleanupTuplet
//---------------------------------------------------------

static void cleanupTuplet(Tuplet* t)
      {
      const auto elements(t->elements());
      t->clear();
      for (DurationElement* e : elements) {
            e->setTuplet(nullptr); // prevent deleting the top tuplet by its children
            if (e->isTuplet())
                  cleanupTuplet(toTuplet(e));
            delete e;
            }
      }

//---------------------------------------------------------
//   TrackList
//---------------------------------------------------------

TrackList::~TrackList()
      {
      int n = size();
      for (int i = 0; i < n; ++i) {
            Element* e = at(i);
            if (e->isTuplet()) {
                  Tuplet* t = toTuplet(e);
                  cleanupTuplet(t);
                  }
            else
                  delete e;
            }
      }

//---------------------------------------------------------
//   appendTuplet
//---------------------------------------------------------

void TrackList::appendTuplet(Tuplet* srcTuplet, Tuplet* dstTuplet)
      {
      for (DurationElement* de : srcTuplet->elements()) {
            DurationElement* e = toDurationElement(de->clone());
            dstTuplet->add(e);
            if (de->isTuplet()) {
                  Tuplet* st = toTuplet(de);
                  Tuplet* dt = toTuplet(e);
                  appendTuplet(st, dt);
                  }
            }
      }

//---------------------------------------------------------
//   combineTuplet
//---------------------------------------------------------

void TrackList::combineTuplet(Tuplet* dst, Tuplet* src)
      {
      dst->setTicks(dst->ticks() * 2);
      dst->setBaseLen(dst->baseLen().shift(-1));

      // try to combine tie'd notes
      unsigned idx = 0;
      if (dst->elements().back()->isChord() && src->elements().front()->isChord()) {
            Chord* chord = toChord(src->elements().front());
            bool akkumulateChord = true;
            for (Note* n : chord->notes()) {
                  if (!n->tieBack() || !n->tieBack()->generated()) {
                        akkumulateChord = false;
                        break;
                        }
                  }
            if (akkumulateChord) {
                  Chord* bc  = toChord(dst->elements().back());
                  bc->setTicks(bc->ticks() + chord->ticks());

                  // forward ties
                  int i = 0;
                  for (Note* n : bc->notes()) {
                        n->setTieFor(chord->notes()[i]->tieFor());
                        ++i;
                        }
                  idx = 1;    // skip first src element
                  }
            }

      for (; idx < src->elements().size(); ++idx) {
            DurationElement* de = src->elements()[idx];
            DurationElement* e = toDurationElement(de->clone());
            dst->add(e);
            if (de->isTuplet()) {
                  Tuplet* st = toTuplet(de);
                  Tuplet* dt = toTuplet(e);
                  appendTuplet(st, dt);
                  }
            }
      }

//---------------------------------------------------------
//   append
//---------------------------------------------------------

void TrackList::append(Element* e)
      {
      if (e->isDurationElement()) {
            _duration += toDurationElement(e)->ticks();

            bool accumulateRest = e->isRest() && !empty() && back()->isRest();
            Segment* s          = accumulateRest ? toRest(e)->segment() : 0;

            if (s && !s->score()->isSpannerStartEnd(s->tick(), e->track()) && !s->annotations().size()) {
                  // akkumulate rests
                  Rest* rest  = toRest(back());
                  Fraction du = rest->ticks();
                  du += toRest(e)->ticks();
                  rest->setTicks(du);
                  }
            else {
                  Element* element = 0;
                  if (e->isTuplet()) {
                        Tuplet* src = toTuplet(e);
                        if (src->generated() && !empty() && back()->isTuplet()) {
                              Tuplet* b = toTuplet(back());
                              combineTuplet(b, src);
                              }
                        else {
                              element = e->clone();
                              Tuplet* dst = toTuplet(element);
                              appendTuplet(src, dst);
                              }
                        }
                  else {
                        element = e->clone();
                        ChordRest* src = toChordRest(e);
                        Segment* s1 = src->segment();
                        for (Element* ee : s1->annotations()) {
                              if (ee->track() == e->track())
                                    _range->annotations.push_back({ s1->tick(), ee->clone() });
                              }
                        if (e->isChord()) {
                              Chord* chord = toChord(e);
                              bool akkumulateChord = true;
                              for (Note* n : chord->notes()) {
                                    if (!n->tieBack() || !n->tieBack()->generated()) {
                                          akkumulateChord = false;
                                          break;
                                          }
                                   }
                              if (akkumulateChord && !empty() && back()->isChord()) {
                                    Chord* bc   = toChord(back());
                                    const Fraction du = bc->ticks() + chord->ticks();
                                    bc->setTicks(du);

                                    // forward ties
                                    int idx = 0;
                                    for (Note* n : bc->notes()) {
                                          n->setTieFor(chord->notes()[idx]->tieFor());
                                          ++idx;
                                          }
                                    delete element;
                                    element = 0;
                                    }
                              }
                        }
                  if (element) {
                        element->setSelected(false);
                        QList<Element*>::append(element);
                        }
                  }
            }
      else {
            Element* c = e->clone();
            c->setParent(0);
            QList<Element*>::append(c);
            }
      }

//---------------------------------------------------------
//   appendGap
//---------------------------------------------------------

void TrackList::appendGap(const Fraction& du)
      {
      if (du.isZero())
            return;
      Element* e = empty() ? 0 : back();
      if (e && e->isRest()) {
            Rest* rest  = toRest(back());
            Fraction dd = rest->ticks();
            dd          += du;
            _duration   += du;
            rest->setTicks(dd);
            }
      else {
            Rest* rest = new Rest(0);
            rest->setTicks(du);
            QList<Element*>::append(rest);
            _duration   += du;
            }
      }

//---------------------------------------------------------
//   truncate
//    reduce len of last gap by f
//---------------------------------------------------------

bool TrackList::truncate(const Fraction& f)
      {
      if (empty())
            return true;
      Element* e = back();
      if (!e->isRest())
            return false;
      Rest* r = toRest(e);
      if (r->ticks() < f)
            return false;
      if (r->ticks() == f) {
            removeLast();
            delete r;
            }
      else
            r->setTicks(r->ticks() - f);
      _duration -= f;
      return true;
      }

//---------------------------------------------------------
//   read
//---------------------------------------------------------

void TrackList::read(const Segment* fs, const Segment* es)
      {
      Fraction tick = fs->tick();

      const Segment* s;
      for (s = fs; s && (s != es); s = s->next1()) {
            if (!s->enabled())
                  continue;
            Element* e = s->element(_track);
            if (!e || e->generated()) {
                  for (Element* ee : s->annotations()) {
                        if (ee->track() == _track)
                              _range->annotations.push_back({ s->tick(), ee->clone() });
                        }
                  continue;
                  }
            if (e->isRepeatMeasure()) {
                  // TODO: copy previous measure contents?
                  RepeatMeasure* rm = toRepeatMeasure(e);
                  Rest r(*rm);
                  append(&r);
                  tick += r.ticks();
                  }
            else if (e->isChordRest()) {
                  DurationElement* de = toDurationElement(e);
                  Fraction gap = s->tick() - tick;
                  if (de->tuplet()) {
                        Tuplet* t = de->topTuplet();
                        s  = skipTuplet(t);    // continue with first chord/rest after tuplet
                        de = t;
                        }
                  Q_ASSERT(gap >= Fraction(0,1));
                  if (gap.isNotZero()) {
                        appendGap(gap);
                        tick += gap;
                        }
                  append(de);
                  tick += de->ticks();
                  }
            else if (e->isBarLine()) {
                  BarLine* bl = toBarLine(e);
                  if (bl->barLineType() != BarLineType::NORMAL)
                        append(e);
                  }
            else
                  append(e);
            }
      Fraction gap = es->tick() - tick;
      if (gap.isNotZero())
            appendGap(gap);

      //
      // connect ties
      //
      int n = size();
      for (int i = 0; i < n; ++i) {
            Element* e = at(i);
            if (!e->isChord())
                  continue;
            Chord* chord = toChord(e);
            for (Note* n1 : chord->notes()) {
                  Tie* tie = n1->tieFor();
                  if (!tie)
                        continue;
                  for (int k = i+1; k < n; ++k) {
                        Element* ee = at(k);
                        if (!ee->isChord())
                              continue;
                        Chord* c2 = toChord(ee);
                        bool found = false;
                        for (Note* n2 : c2->notes()) {
                              if (n1->pitch() == n2->pitch()) {
                                    tie->setEndNote(n2);
                                    n2->setTieBack(tie);
                                    found = true;
                                    break;
                                    }
                              }
                        if (!found)
                              qDebug("Tied note not found");
                        break;
                        }
                  }
            }
      }

//---------------------------------------------------------
//   checkRest
//---------------------------------------------------------

static void checkRest(Fraction& rest, Measure*& m, const Fraction& d)
      {
      if (rest.isZero()) {
            if (m->nextMeasure()) {
                  m  = m->nextMeasure();
                  rest = m->ticks();
                  }
            else {
                  qFatal("premature end of measure list, rest %d/%d", d.numerator(), d.denominator());
                  }
            }
      }

//---------------------------------------------------------
//   writeTuplet
//    measure - current measure
//    rest    - available time in measure
//---------------------------------------------------------

Tuplet* TrackList::writeTuplet(Tuplet* parent, Tuplet* tuplet, Measure*& measure, Fraction& rest) const
      {
      Score* score = measure->score();
      Tuplet* dt   = tuplet->clone();
      dt->setParent(measure);
      Fraction du  = tuplet->ticks();
      if (du > rest) {
            // we must split the tuplet
            dt->setTicks(du * Fraction(1, 2));
            dt->setBaseLen(tuplet->baseLen().shift(1));
            }
      if (parent)
            parent->add(dt);

      for (DurationElement* e : tuplet->elements()) {
            Fraction duration = e->globalTicks();
            Tuplet* tt        = dt;
            Fraction ratio    = Fraction(1, 1);
            while (tt) {
                  ratio *= tt->ratio();
                  tt = tt->tuplet();
                  }

            bool firstpart = true;
            while (duration > Fraction(0,1)) {
                  if (rest.isZero()) {
                        if (measure->nextMeasure()) {
                              measure = measure->nextMeasure();
                              rest    = measure->ticks();
                              if (e != tuplet->elements().back()) {
                                    // create second part of split tuplet
                                    dt = dt->clone();
                                    dt->setGenerated(true);
                                    dt->setParent(measure);
                                    Tuplet* pt = dt;
                                    while (parent) {
                                          Tuplet* tt1 = parent->clone();
                                          tt1->setGenerated(true);
                                          tt1->setParent(measure);
                                          tt1->add(pt);
                                          pt = tt1;
                                          parent = parent->tuplet();
                                          }
                                    }
                              }
                        else {
                              qFatal("premature end of measure list in track %d, rest %d/%d",
                                 _track, duration.numerator(), duration.denominator());
                              }
                        }
                  if (e->isChordRest()) {
                        Fraction dd = qMin(rest, duration) * ratio;
                        std::vector<TDuration> dl = toDurationList(dd, false);
                        for (const TDuration& k : dl) {
                              Segment* segment = measure->undoGetSegmentR(SegmentType::ChordRest, measure->ticks() - rest);
                              Fraction gd      = k.fraction() / ratio;
                              ChordRest* cr    = toChordRest(e->clone());
                              if (!firstpart)
                                    cr->removeMarkings(true);
                              cr->setScore(score);
                              cr->setTrack(_track);
                              segment->add(cr);
                              cr->setTicks(k.fraction());
                              cr->setDurationType(k);
                              rest     -= gd;
                              duration -= gd;

                              if (cr->isChord()) {
                                    for (Note* note : toChord(cr)->notes()) {
                                          if (!duration.isZero() && !note->tieFor()) {
                                                Tie* tie = new Tie(score);
                                                tie->setGenerated(true);
                                                note->add(tie);
                                                }
                                          }
                                    }
                              dt->add(cr);
                              firstpart = false;
                              }
                        }
                  else if (e->isTuplet()) {
                        Tuplet* tt1 = toTuplet(e);
                        Tuplet* ttt = writeTuplet(dt, tt1, measure, rest);
                        dt          = ttt->tuplet();
                        parent      = dt->tuplet();
                        duration    = Fraction();
                        }
                  firstpart = false;
                  }
            }
      return dt;
      }

//---------------------------------------------------------
//   write
//    rewrite notes into measure list measure
//---------------------------------------------------------

bool TrackList::write(Score* score, const Fraction& tick) const
      {
      if ((_track % VOICES) && size() == 1 && at(0)->isRest())     // don’t write rests in voice > 0
            return true;
      Measure* measure = score->tick2measure(tick);
      Measure* m       = measure;
      Fraction remains = m->endTick() - tick;
      Segment* segment = 0;

      for (Element* e : *this) {
            if (e->isDurationElement()) {
                  Fraction duration = toDurationElement(e)->ticks();
                  checkRest(remains, m, duration);     // go to next measure, if necessary
                  if (duration > remains && e->isTuplet()) {
                        // experimental: allow tuplet split in the middle
                        if (duration != remains * 2) {
                              MScore::setError(CANNOT_SPLIT_TUPLET);
                              return false;
                              }
                        }
                  bool firstpart = true;
                  while (duration > Fraction(0,1)) {
                        if ((e->isRest() || e->isRepeatMeasure()) && (duration >= remains || e == back()) && (remains == m->ticks())) {
                              //
                              // handle full measure rest
                              //
                              Segment* seg = m->getSegmentR(SegmentType::ChordRest, m->ticks() - remains);
                              if ((_track % VOICES) == 0) {
                                    // write only for voice 1
                                    Rest* r = new Rest(score, TDuration::DurationType::V_MEASURE);
                                    // ideally we should be using stretchedLen
                                    // but this is not valid during rewrite when adding time signatures
                                    // since the time signature has not been added yet
                                    //Fraction stretchedLen = m->stretchedLen(staff);
                                    //r->setTicks(stretchedLen);
                                    r->setTicks(m->ticks());
                                    r->setTrack(_track);
                                    seg->add(r);
                                    }
                              duration -= m->ticks();
                              remains.set(0, 1);
                              }
                        else if (e->isChordRest()) {
                              Fraction du               = qMin(remains, duration);
                              std::vector<TDuration> dl = toDurationList(du, e->isChord());
                              Q_ASSERT(!dl.empty());
                              for (const TDuration& k : dl) {
                                    segment       = m->undoGetSegmentR(SegmentType::ChordRest, m->ticks() - remains);
                                    ChordRest* cr = toChordRest(e->clone());
                                    if (!firstpart)
                                          cr->removeMarkings(true);
                                    cr->setTrack(_track);
                                    cr->setScore(score);
                                    Fraction gd = k.fraction();
                                    cr->setTicks(gd);
                                    cr->setDurationType(k);

                                    segment->add(cr);
                                    duration -= gd;
                                    remains  -= gd;

                                    if (cr->isChord()) {
                                          for (Note* note : toChord(cr)->notes()) {
                                                if (!duration.isZero() && !note->tieFor()) {
                                                      Tie* tie = new Tie(score);
                                                      tie->setGenerated(true);
                                                      note->add(tie);
                                                      }
                                                }
                                          }
                                    }
                              }
                        else if (e->isTuplet()) {
                              writeTuplet(0, toTuplet(e), m, remains);
                              duration = Fraction();
                              }
                        firstpart = false;
                        if (duration > Fraction(0,1))
                              checkRest(remains, m, duration);     // go to next measure, if necessary
                        }
                  }
            else if (e->isBarLine()) {
//                  if (pos.numerator() == 0 && m) {
//                        BarLineType t = toBarLine(e)->barLineType();
//                        Measure* pm = m->prevMeasure();
//TODO                        if (pm)
//                              pm->setEndBarLineType(t,0);
//                        }
                  }
            else if (e->isClef()) {
                  Segment* seg;
                  if (remains == m->ticks() && m->tick() > Fraction(0,1)) {
                        Measure* pm = m->prevMeasure();
                        seg = pm->getSegmentR(SegmentType::Clef, pm->ticks());
                        }
                  else if (remains != m->ticks())
                        seg = m->getSegmentR(SegmentType::Clef, m->ticks() - remains);
                  else
                        seg = m->getSegmentR(SegmentType::HeaderClef, Fraction(0,1));
                  Element* ne = e->clone();
                  ne->setScore(score);
                  ne->setTrack(_track);
                  seg->add(ne);
                  }
            else {
                  if (!m)
                        break;
                  // add the element in its own segment;
                  // but KeySig has to be at start of (current) measure

                  Segment* seg = m->getSegmentR(Segment::segmentType(e->type()), e->isKeySig() ? Fraction() : m->ticks() - remains);
                  Element* ne = e->clone();
                  ne->setScore(score);
                  ne->setTrack(_track);
                  seg->add(ne);
                  }
            }
      //
      // connect ties from measure->first() to segment
      //

      for (Segment* s = measure->first(); s; s = s->next1()) {
            Element* e = s->element(_track);
            if (!e || !e->isChord())
                  continue;
            Chord* chord = toChord(e);
            for (Note* n : chord->notes()) {
                  Tie* tie = n->tieFor();
                  if (!tie)
                        continue;
                  Note* nn = searchTieNote(n);
                  if (nn) {
                        tie->setEndNote(nn);
                        nn->setTieBack(tie);
                        }
                  }
            if (s == segment)
                  break;
            }
      return true;
      }

//---------------------------------------------------------
//   ScoreRange
//---------------------------------------------------------

ScoreRange::~ScoreRange()
      {
      qDeleteAll(tracks);
      }

//---------------------------------------------------------
//   read
//---------------------------------------------------------

void ScoreRange::read(Segment* first, Segment* last, bool readSpanner)
      {
      _first        = first;
      _last         = last;
      Score* score  = first->score();
      QList<int> sl = score->uniqueStaves();

      int startTrack = 0;
      int endTrack   = score->nstaves() * VOICES;

      spanner.clear();

      if (readSpanner) {
            Fraction stick = first->tick();
            Fraction etick = last->tick();
            for (auto i : first->score()->spanner()) {
                  Spanner* s = i.second;
                  if (s->tick() >= stick && s->tick() < etick && s->track() >= startTrack && s->track() < endTrack) {
                        Spanner* ns = toSpanner(s->clone());
                        ns->setParent(0);
                        ns->setStartElement(0);
                        ns->setEndElement(0);
                        ns->setTick(ns->tick() - stick);
                        spanner.push_back(ns);
                        }
                  }
            }
      for (int staffIdx : sl) {
            int sTrack = staffIdx * VOICES;
            int eTrack = sTrack + VOICES;
            for (int track = sTrack; track < eTrack; ++track) {
                  TrackList* dl = new TrackList(this);
                  dl->setTrack(track);
                  dl->read(first, last);
                  tracks.append(dl);
                  }
            }
      }

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

bool ScoreRange::write(Score* score, const Fraction& tick) const
      {
      for (TrackList* dl : tracks) {
            int track = dl->track();
            if (!dl->write(score, tick))
                  return false;
            if ((track % VOICES) == VOICES - 1)  {
                  // clone staff if appropriate after all voices have been copied
                  int staffIdx = track / VOICES;
                  Staff* ostaff = score->staff(staffIdx);
                  const LinkedElements* linkedStaves = ostaff->links();
                  if (linkedStaves) {
                        for (auto le : *linkedStaves) {
                              Staff* nstaff = toStaff(le);
                              if (nstaff == ostaff)
                                    continue;
                              Excerpt::cloneStaff2(ostaff, nstaff, tick, (tick + dl->ticks()));
                              }
                        }
                  }
            ++track;
            }
      for (Spanner* s : spanner) {
            s->setTick(s->tick() + tick);
            if (s->isSlur()) {
                  Slur* slur = toSlur(s);
                  if (slur->startCR()->isGrace()) {
                        Chord* sc = slur->startChord();
                        int idx   = sc->graceIndex();
                        Chord* dc = toChord(score->findCR(s->tick(), s->track()));
                        s->setStartElement(dc->graceNotes()[idx]);
                        }
                  else
                        s->setStartElement(0);
                  if (slur->endCR()->isGrace()) {
                        Chord* sc = slur->endChord();
                        int idx   = sc->graceIndex();
                        Chord* dc = toChord(score->findCR(s->tick2(), s->track2()));
                        s->setEndElement(dc->graceNotes()[idx]);
                        }
                  else
                        s->setEndElement(0);
                  }
            score->undoAddElement(s);
            }
      for (const Annotation& a : annotations) {
            Measure* tm = score->tick2measure(a.tick);
            Segment *op = toSegment(a.e->parent());
            Segment* s = tm->undoGetSegment(op->segmentType(), a.tick);
            if (s) {
                  a.e->setParent(s);
                  score->undoAddElement(a.e);
                  }
            }
      return true;
      }

//---------------------------------------------------------
//   fill
//---------------------------------------------------------

void ScoreRange::fill(const Fraction& f)
      {
      const Fraction oldDuration = ticks();
      Fraction oldEndTick = _first->tick() + oldDuration;
      for (auto t : tracks)
            t->appendGap(f);

      Fraction diff = ticks() - oldDuration;
      for (Spanner* sp : spanner) {
            if (sp->tick2() >= oldEndTick && sp->tick() < oldEndTick)
                  sp->setTicks(sp->ticks() + diff);
            }
      }

//---------------------------------------------------------
//   truncate
//    reduce len of last gap by f
//---------------------------------------------------------

bool ScoreRange::truncate(const Fraction& f)
      {
      for (TrackList* dl : tracks) {
            if (dl->empty())
                  continue;
            Element* e = dl->back();
            if (!e->isRest())
                  return false;
            Rest* r = toRest(e);
            if (r->ticks() < f)
                  return false;
            }
      for (TrackList* dl : tracks)
            dl->truncate(f);
      return true;
      }

//---------------------------------------------------------
//   ticks
//---------------------------------------------------------

Fraction ScoreRange::ticks() const
      {
      return tracks.empty() ? Fraction() : tracks[0]->ticks();
      }

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

void TrackList::dump() const
      {
      qDebug("elements %d, duration %d/%d", size(), _duration.numerator(), _duration.denominator());
      for (Element* e : *this) {
            if (e->isDurationElement()) {
                  Fraction du = toDurationElement(e)->ticks();
                  qDebug("   %s  %d/%d", e->name(), du.numerator(), du.denominator());
                  }
            else
                  qDebug("   %s", e->name());
            }
      }

}

