//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2018 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 "score.h"
#include "page.h"
#include "system.h"
#include "measure.h"
#include "layout.h"
#include "bracket.h"
#include "spanner.h"
#include "barline.h"
#include "tie.h"
#include "chord.h"
#include "staff.h"
#include "box.h"
#include "spacer.h"
#include "sym.h"
#include "systemdivider.h"
#include "tuplet.h"
#include "dynamic.h"
#include "stafflines.h"
#include "tempotext.h"
#include "hairpin.h"
#include "part.h"
#include "keysig.h"
#include "sig.h"
#include "breath.h"
#include "tempo.h"
#include "fermata.h"
#include "lyrics.h"

namespace Ms {

extern bool isTopBeam(ChordRest* cr);
extern bool notTopBeam(ChordRest* cr);
extern void layoutTies(Chord* ch, System* system, int stick);
extern void layoutDrumsetChord(Chord* c, const Drumset* drumset, const StaffType* st, qreal spatium);

//---------------------------------------------------------
//   processLines
//---------------------------------------------------------

static void processLines(System* system, std::vector<Spanner*> lines, bool align)
      {
      std::vector<SpannerSegment*> segments;
      for (Spanner* sp : lines) {
            SpannerSegment* ss = sp->layoutSystem(system);     // create/layout spanner segment for this system
            if (ss->autoplace())
                  segments.push_back(ss);
            }

      if (align && segments.size() > 1) {
            qreal y = segments[0]->offset().y();
            for (unsigned i = 1; i < segments.size(); ++i)
                  y = qMax(y, segments[i]->offset().y());
            for (auto ss : segments)
                  ss->ryoffset() = y;
            }
      }

//---------------------------------------------------------
//   resetSystems
//    in linear mode there is only one page
//    which contains one system
//---------------------------------------------------------

void Score::resetSystems(bool /*layoutAll*/, LayoutContext& lc)
      {
// if (layoutAll) {
      Page* page = 0;
      if (true) {
            for (System* s : _systems) {
                  for (SpannerSegment* ss : s->spannerSegments())
                        ss->setParent(0);
                  }
            qDeleteAll(_systems);
            _systems.clear();
            qDeleteAll(pages());
            pages().clear();
            if (!firstMeasure()) {
                  qDebug("no measures");
                  return;
                  }

            for (MeasureBase* mb = first(); mb; mb = mb->next())
                  mb->setSystem(0);

            page = new Page(this);
            pages().push_back(page);
            page->bbox().setRect(0.0, 0.0, loWidth(), loHeight());
            page->setNo(0);

            System* system = new System(this);
            _systems.push_back(system);
            page->appendSystem(system);
            for (int i = 0; i < nstaves(); ++i)
                  system->insertStaff(i);
            }
      else {
            if (pages().isEmpty())
                  return;
            page = pages().front();
            // system = systems().front();
            }
      lc.page = page;
      }

//---------------------------------------------------------
//   collectLinearSystem
//   Append all measures to System. VBox is not included to System
//---------------------------------------------------------

 void Score::collectLinearSystem(LayoutContext& lc)
      {
      System* system = systems().front();
      system->setInstrumentNames(/* longNames */ true);
      // we need to reset tempo because fermata is setted
      //inside getNextMeasure and it lead to twice timeStretch
      resetTempo();

      QPointF pos;
      bool firstMeasure = true;

      //set first measure to lc.nextMeasures for following
      //utilizing in getNextMeasure()
      lc.nextMeasure = _measures.first();
      getNextMeasure(lc);

      while (lc.curMeasure) {
            qreal ww = 0.0;
            if (lc.curMeasure->isVBox() || lc.curMeasure->isTBox()) {
                  lc.curMeasure->setParent(nullptr);
                  getNextMeasure(lc);
                  continue;
                  }
            system->appendMeasure(lc.curMeasure);
            if (lc.curMeasure->isMeasure()) {
                  Measure* m = toMeasure(lc.curMeasure);
                        if (m->mmRest()) {
                              m->mmRest()->setSystem(nullptr);
                              }
                  if (firstMeasure) {
                        system->layoutSystem(0.0);
                        if (m->repeatStart()) {
                              Segment* s = m->findSegmentR(SegmentType::StartRepeatBarLine, 0);
                              if (!s->enabled())
                                    s->setEnabled(true);
                              }
                        m->addSystemHeader(true);
                        pos.rx() += system->leftMargin();
                        firstMeasure = false;
                        }
                  else if (m->header())
                        m->removeSystemHeader();
                  m->createEndBarLines(true);
                  m->computeMinWidth();
                  ww = m->width();
                  m->stretchMeasure(ww);
                  m->setPos(pos);
                  m->layoutStaffLines();
                  }
            else if (lc.curMeasure->isHBox()) {
                  lc.curMeasure->setPos(pos + QPointF(toHBox(lc.curMeasure)->topGap(), 0.0));
                  lc.curMeasure->layout();
                  ww = lc.curMeasure->width();
                  }
            pos.rx() += ww;

            getNextMeasure(lc);
            }

      system->setWidth(pos.x());
      }

//---------------------------------------------------------
//   layoutLinear
//---------------------------------------------------------

void Score::layoutLinear(bool layoutAll, LayoutContext& lc)
      {
      lc.score = this;
      resetSystems(layoutAll, lc);

      collectLinearSystem(lc);
//      hideEmptyStaves(systems().front(), true);     this does not make sense

      lc.layoutLinear();
      }

//---------------------------------------------------------
//   layoutLinear
//---------------------------------------------------------

void LayoutContext::layoutLinear()
      {
      System* system = score->systems().front();

      //
      // layout
      //    - beams
      //    - RehearsalMark, StaffText
      //    - Dynamic
      //    - update the segment shape + measure shape
      //
      //
      int stick = system->measures().front()->tick();
      int etick = system->measures().back()->endTick();

      //
      // layout slurs
      //
      if (etick > stick) {    // ignore vbox
            auto spanners = score->spannerMap().findOverlapping(stick, etick);

            std::vector<Spanner*> spanner;
            for (auto interval : spanners) {
                  Spanner* sp = interval.value;
                  if (sp->tick() < etick && sp->tick2() >= stick) {
                        if (sp->isSlur())
                              spanner.push_back(sp);
                        }
                  }
            processLines(system, spanner, false);
            }

      std::vector<Dynamic*> dynamics;
      for (MeasureBase* mb : system->measures()) {
            if (!mb->isMeasure())
                  continue;
            SegmentType st = SegmentType::ChordRest;
            Measure* m = toMeasure(mb);
            for (Segment* s = m->first(st); s; s = s->next(st)) {
                  for (Element* e : s->elist()) {
                        if (!e)
                              continue;
                        if (e->isChordRest()) {
                              ChordRest* cr = toChordRest(e);
                              if (isTopBeam(cr))
                                    cr->beam()->layout();
                              if (e->isChord()) {
                                    Chord* c = toChord(e);
                                    for (Chord* ch : c->graceNotes())
                                          layoutTies(ch, system, stick);
                                    layoutTies(c, system, stick);
                                    c->layoutArticulations2();
                                    }
                              }
                        }
                  for (Element* e : s->annotations()) {
                        if (e->visible() && e->isDynamic()) {
                              Dynamic* d = toDynamic(e);
                              d->layout();
                              }
                        else if (e->isFiguredBass())
                              e->layout();
                        }
                  }
            }

      //
      // layout tuplet
      //

      for (MeasureBase* mb : system->measures()) {
            if (!mb->isMeasure())
                  continue;
            Measure* m = toMeasure(mb);
            static const SegmentType st { SegmentType::ChordRest };
            for (int track = 0; track < score->ntracks(); ++track) {
                  if (!score->staff(track / VOICES)->show()) {
                        track += VOICES-1;
                        continue;
                        }
                  for (Segment* s = m->first(st); s; s = s->next(st)) {
                        ChordRest* cr = s->cr(track);
                        if (!cr)
                              continue;
                        DurationElement* de = cr;
                        while (de->tuplet() && de->tuplet()->elements().front() == de) {
                              Tuplet* t = de->tuplet();
                              t->layout();
                              de = de->tuplet();
                              }
                        }
                  }
            }

      //
      //    layout SpannerSegments for current system
      //

      if (etick > stick) {    // ignore vbox
            auto spanners = score->spannerMap().findOverlapping(stick, etick);

            std::vector<Spanner*> ottavas;
            std::vector<Spanner*> spanner;
            std::vector<Spanner*> pedal;

            for (auto interval : spanners) {
                  Spanner* sp = interval.value;
                  if (sp->tick() < etick && sp->tick2() > stick) {
                        if (sp->isOttava())
                              ottavas.push_back(sp);
                        else if (sp->isPedal())
                              pedal.push_back(sp);
                        else if (!sp->isSlur())             // slurs are already handled
                              spanner.push_back(sp);
                        }
                  }
            processLines(system, ottavas, false);
            processLines(system, pedal, true);
            processLines(system, spanner, false);

            //
            // vertical align volta segments
            //
            std::vector<SpannerSegment*> voltaSegments;
            for (SpannerSegment* ss : system->spannerSegments()) {
                  if (ss->isVoltaSegment())
                       voltaSegments.push_back(ss);
                 }
            if (voltaSegments.size() > 1) {
                  qreal y = 0;
                  for (SpannerSegment* ss : voltaSegments)
                        y = qMin(y, ss->offset().y());
                  for (SpannerSegment* ss : voltaSegments)
                        ss->ryoffset() = y;
                  }
            }

      //
      // TempoText, Fermata
      //

      for (MeasureBase* mb : system->measures()) {
            if (!mb->isMeasure())
                  continue;
            SegmentType st = SegmentType::ChordRest;
            Measure* m = toMeasure(mb);
            for (Segment* s = m->first(st); s; s = s->next(st)) {
                  for (Element* e : s->annotations()) {
                        if (e->isTempoText()) {
                              TempoText* tt = toTempoText(e);
                              if (score->isMaster())
                                    score->setTempo(tt->segment(), tt->tempo());
                              tt->layout();
                              }
                        else if (e->isFermata())
                              e->layout();
                        }
                  }
            }

      //
      // Jump, Marker
      //

      for (MeasureBase* mb : system->measures()) {
            if (!mb->isMeasure())
                  continue;
            Measure* m = toMeasure(mb);
            for (Element* e : m->el()) {
                  if (e->visible() && (e->isJump() || e->isMarker()))
                        e->layout();
                  }
            }

      //
      // RehearsalMark, StaffText, FretDiagram
      //

      for (MeasureBase* mb : system->measures()) {
            if (!mb->isMeasure())
                  continue;
            SegmentType st = SegmentType::ChordRest;
            Measure* m = toMeasure(mb);
            for (Segment* s = m->first(st); s; s = s->next(st)) {
                  // layout in specific order
                  for (Element* e : s->annotations()) {
                        if (e->visible() && e->isFretDiagram())
                              e->layout();
                        }
                  for (Element* e : s->annotations()) {
                        if (e->visible() && (e->isStaffText() || e->isHarmony()))
                              e->layout();
                        }
                  for (Element* e : s->annotations()) {
                        if (e->visible() && e->isRehearsalMark())
                              e->layout();
                        }
                  }
            }

      score->layoutLyrics(system);

      for (Spanner* sp : score->unmanagedSpanners()) {
            if (sp->tick() >= etick || sp->tick2() <= stick)
                  continue;
            sp->layoutSystem(system);
            }

      system->layout2();   // compute staff distances

      for (MeasureBase* mb : system->measures()) {
            if (!mb->isMeasure())
                  continue;
            Measure* m = toMeasure(mb);

            for (int track = 0; track < score->ntracks(); ++track) {
                  for (Segment* segment = m->first(); segment; segment = segment->next()) {
                        Element* e = segment->element(track);
                        if (!e)
                              continue;
                        if (e->isChordRest()) {
                              if (!score->staff(track2staff(track))->show())
                                    continue;
                              ChordRest* cr = toChordRest(e);
                              if (notTopBeam(cr))                   // layout cross staff beams
                                    cr->beam()->layout();

                              if (cr->isChord()) {
                                    Chord* c = toChord(cr);
                                    for (Chord* cc : c->graceNotes()) {
                                          if (cc->beam() && cc->beam()->elements().front() == cc)
                                                cc->beam()->layout();
                                          for (Note* n : cc->notes()) {
                                                Tie* tie = n->tieFor();
                                                if (tie)
                                                      tie->layout();
                                                for (Spanner* sp : n->spannerFor())
                                                      sp->layout();
                                                }
                                          for (Element* element : cc->el()) {
                                                if (element->isSlur())
                                                      element->layout();
                                                }
                                          }
                                    c->layoutArpeggio2();
                                    for (Note* n : c->notes()) {
                                          Tie* tie = n->tieFor();
                                          if (tie)
                                                tie->layout();
                                          for (Spanner* sp : n->spannerFor())
                                                sp->layout();
                                          }
                                    }
                              }
                        else if (e->isBarLine())
                              toBarLine(e)->layout2();
                        }
                  }
            m->layout2();
            }
      page->setPos(0, 0);
      system->setPos(page->lm(), page->tm() + score->styleP(Sid::staffUpperBorder));
      page->setWidth(system->width() + system->pos().x());
      // Set buffer space after the last system to avoid problems with mouse input.
      // Mouse input divides space between systems equally (see Score::searchSystem),
      // hence the choice of the value.
      const qreal buffer = 0.5 * score->styleS(Sid::maxSystemDistance).val() * score->spatium();
      page->setHeight(system->height() + system->pos().y() + buffer);
      page->rebuildBspTree();
      }

//---------------------------------------------------------
//   layoutMeasureLinear
//---------------------------------------------------------

void LayoutContext::layoutMeasureLinear(MeasureBase* mb)
      {
      adjustMeasureNo(mb);

      if (!mb->isMeasure()) {
            mb->setTick(tick);
            return;
            }

      //-----------------------------------------
      //    process one measure
      //-----------------------------------------

      Measure* measure = toMeasure(mb);
      measure->moveTicks(tick - measure->tick());

      //
      //  implement section break rest
      //
      if (measure->sectionBreak() && measure->pause() != 0.0)
            score->setPause(measure->endTick(), measure->pause());

      //
      // calculate accidentals and note lines,
      // create stem and set stem direction
      //
      for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
            Staff* staff           = score->staff(staffIdx);
            const Drumset* drumset = staff->part()->instrument()->useDrumset() ? staff->part()->instrument()->drumset() : 0;
            AccidentalState as;      // list of already set accidentals for this measure
            as.init(staff->keySigEvent(measure->tick()), staff->clef(measure->tick()));

            for (Segment& segment : measure->segments()) {
                  if (segment.isKeySigType()) {
                        KeySig* ks = toKeySig(segment.element(staffIdx * VOICES));
                        if (!ks)
                              continue;
                        int t = segment.tick();
                        as.init(staff->keySigEvent(t), staff->clef(t));
                        ks->layout();
                        }
                  else if (segment.isChordRestType()) {
                        const StaffType* st = staff->staffType(segment.tick());
                        int track     = staffIdx * VOICES;
                        int endTrack  = track + VOICES;

                        for (int t = track; t < endTrack; ++t) {
                              ChordRest* cr = segment.cr(t);
                              if (!cr)
                                    continue;
                              qreal m = staff->mag(segment.tick());
                              if (cr->small())
                                    m *= score->styleD(Sid::smallNoteMag);

                              if (cr->isChord()) {
                                    Chord* chord = toChord(cr);
                                    chord->cmdUpdateNotes(&as);
                                    for (Chord* c : chord->graceNotes()) {
                                          c->setMag(m * score->styleD(Sid::graceNoteMag));
                                          c->computeUp();
                                          if (c->stemDirection() != Direction::AUTO)
                                                c->setUp(c->stemDirection() == Direction::UP);
                                          else
                                                c->setUp(!(t % 2));
                                          if (drumset)
                                                layoutDrumsetChord(c, drumset, st, score->spatium());
                                          c->layoutStem1();
                                          }
                                    if (drumset)
                                          layoutDrumsetChord(chord, drumset, st, score->spatium());
                                    chord->computeUp();
                                    chord->layoutStem1();   // create stems needed to calculate spacing
                                                            // stem direction can change later during beam processing
                                    }
                              cr->setMag(m);
                              }
                        }
                  else if (segment.isClefType()) {
                        Element* e = segment.element(staffIdx * VOICES);
                        if (e) {
                              toClef(e)->setSmall(true);
                              e->layout();
                              }
                        }
                  else if (segment.isType(SegmentType::TimeSig | SegmentType::Ambitus | SegmentType::HeaderClef)) {
                        Element* e = segment.element(staffIdx * VOICES);
                        if (e)
                              e->layout();
                        }
                  }
            }

      score->createBeams(measure);

      for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
            for (Segment& segment : measure->segments()) {
                  if (segment.isChordRestType()) {
                        score->layoutChords1(&segment, staffIdx);
                        for (int voice = 0; voice < VOICES; ++voice) {
                              ChordRest* cr = segment.cr(staffIdx * VOICES + voice);
                              if (cr) {
                                    for (Lyrics* l : cr->lyrics()) {
                                          if (l)
                                                l->layout();
                                          }
                                    if (cr->isChord())
                                          toChord(cr)->layoutArticulations();
                                    }
                              }
                        }
                  }
            }

      for (Segment& segment : measure->segments()) {
            if (segment.isBreathType()) {
                  qreal length = 0.0;
                  int t = segment.tick();
                  // find longest pause
                  for (int i = 0, n = score->ntracks(); i < n; ++i) {
                        Element* e = segment.element(i);
                        if (e && e->isBreath()) {
                              Breath* b = toBreath(e);
                              b->layout();
                              length = qMax(length, b->pause());
                              }
                        }
                  if (length != 0.0)
                        score->setPause(t, length);
                  }
            else if (segment.isTimeSigType()) {
                  for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
                        TimeSig* ts = toTimeSig(segment.element(staffIdx * VOICES));
                        if (ts)
                              score->staff(staffIdx)->addTimeSig(ts);
                        }
                  }
            else if (score->isMaster() && segment.isChordRestType()) {
                  for (Element* e : segment.annotations()) {
                        if (!(e->isTempoText()
                           || e->isDynamic()
                           || e->isFermata()
                           || e->isRehearsalMark()
                           || e->isFretDiagram()
                           || e->isHarmony()
                           || e->isStaffText()              // ws: whats left?
                           || e->isFiguredBass())) {
                              e->layout();
                              }
                        }
                  // TODO, this is not going to work, we just cleaned the tempomap
                  // it breaks the test midi/testBaroqueOrnaments.mscx where first note has stretch 2
                  // Also see fixTicks
                  qreal stretch = 0.0;
                  for (Element* e : segment.annotations()) {
                        if (e->isFermata())
                              stretch = qMax(stretch, toFermata(e)->timeStretch());
                        }
                  if (stretch != 0.0 && stretch != 1.0) {
                        qreal otempo = score->tempomap()->tempo(segment.tick());
                        qreal ntempo = otempo / stretch;
                        score->setTempo(segment.tick(), ntempo);
                        int etick = segment.tick() + segment.ticks() - 1;
                        auto e = score->tempomap()->find(etick);
                        if (e == score->tempomap()->end())
                              score->setTempo(etick, otempo);
                        }
                  }
            else if (segment.isChordRestType()) {
                  // chord symbols need to be layouted in parts too
                  for (Element* e : segment.annotations()) {
                        if (e->isHarmony())
                              e->layout();
                        }
                  }
            }

      // update time signature map
      // create event if measure len and time signature are different
      // even if they are equivalent 4/4 vs 2/2
      // also check if nominal time signature has changed

      if (score->isMaster() && ((!measure->len().identical(sig) && measure->len() != sig * measure->mmRestCount())
         || (prevMeasure && prevMeasure->isMeasure()
         && !measure->timesig().identical(toMeasure(prevMeasure)->timesig()))))
            {
            if (measure->isMMRest())
                  sig = measure->mmRestFirst()->len();
            else
                  sig = measure->len();
            score->sigmap()->add(tick, SigEvent(sig, measure->timesig(), measure->no()));
            }

      Segment* seg = measure->findSegmentR(SegmentType::StartRepeatBarLine, 0);
      if (measure->repeatStart()) {
            if (!seg)
                  seg = measure->getSegmentR(SegmentType::StartRepeatBarLine, 0);
            measure->barLinesSetSpan(seg);      // this also creates necessary barlines
            for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
                  BarLine* b = toBarLine(seg->element(staffIdx * VOICES));
                  if (b) {
                        b->setBarLineType(BarLineType::START_REPEAT);
                        b->layout();
                        }
                  }
            }
      else if (seg)
            score->undoRemoveElement(seg);

      for (Segment& s : measure->segments()) {
            // DEBUG: relayout grace notes as beaming/flags may have changed
            if (s.isChordRestType()) {
                  for (Element* e : s.elist()) {
                        if (e && e->isChord()) {
                              Chord* chord = toChord(e);
                              chord->layout();
                              if (chord->tremolo())            // debug
                                    chord->tremolo()->layout();
                              }
                        }
                  }
            else if (s.isEndBarLineType())
                  continue;
            s.createShapes();
            }

      tick += measure->ticks();
      }

} // namespace Ms

