//=============================================================================
//  MuseScore
//  Linux Music Score Editor
//  $Id: sig.cpp 3482 2010-09-21 07:10:03Z lasconic $
//
//  Copyright (C) 2002-2007 Werner Schweer and others
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================

#include "sig.h"
#include "xml.h"
#include "al.h"

namespace AL {

//---------------------------------------------------------
//   ticks_beat
//---------------------------------------------------------

static int ticks_beat(int n)
      {
      int m = (AL::division * 4) / n;
      if ((AL::division * 4) % n) {
            fprintf(stderr, "Mscore: ticks_beat(): bad divisor %d\n", n);
            abort();
            }
      return m;
      }

//---------------------------------------------------------
//   ticks_measure
//---------------------------------------------------------

int ticks_measure(const Fraction& f)
      {
      return (AL::division * 4 * f.numerator()) / f.denominator();
      }

//---------------------------------------------------------
//   SigEvent
//---------------------------------------------------------

SigEvent::SigEvent(const Fraction& f)
      {
      actual  = f;
      nominal = f;
      bar     = 0;
      ticks   = ticks_measure(f);
      }

SigEvent::SigEvent(const Fraction& a, const Fraction& n)
      {
      actual  = a;
      nominal = n;
      bar     = 0;
      ticks   = ticks_measure(a);
      }

//---------------------------------------------------------
//   operator==
//---------------------------------------------------------

bool SigEvent::operator==(const SigEvent& e) const
      {
      return (actual.identical(e.actual)) && (nominal.identical(e.nominal));
      }

//---------------------------------------------------------
//   TimeSigMap
//---------------------------------------------------------

TimeSigMap::TimeSigMap()
      {
      _serial = 0;
      }

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

void TimeSigMap::add(int tick, const Fraction& f)
      {
      if (!f.isValid()) {
            printf("illegal signature %d/%d\n", f.numerator(), f.denominator());
            }
      (*this)[tick] = SigEvent(f);
      normalize();
      }

void TimeSigMap::add(int tick, const Fraction& a, const Fraction& n)
      {
      (*this)[tick] = SigEvent(a, n);
      normalize();
      }

void TimeSigMap::add(int tick, const SigEvent& ev)
      {
      (*this)[tick] = ev;
      normalize();
      }

void TimeSigMap::add(int tick, int ticks, const Fraction& nominal)
      {
      Fraction actual(1, 4);
      if ((ticks % AL::division) == 0) {
            actual = Fraction(ticks / AL::division, 4);
            }
      else if ((ticks % (AL::division/2)) == 0) {
            actual = Fraction(ticks / (AL::division/2), 8);
            }
      else if ((ticks % (AL::division/4)) == 0) {
            actual = Fraction(ticks / (AL::division/4), 16);
            }
      else {
            printf("TimeSigMap::add(tick:%d, ticks:%d, z2:%d, n2:%d): irregular measure not supported\n",
               tick, ticks, nominal.numerator(), nominal.denominator());
            if (debugMsg)
                  abort();
            }

      (*this)[tick] = SigEvent(actual, nominal);
      normalize();
      }

//---------------------------------------------------------
//   del
//---------------------------------------------------------

void TimeSigMap::del(int tick)
      {
      erase(tick);
      normalize();
      }

//---------------------------------------------------------
//   TimeSigMap::normalize
//---------------------------------------------------------

void TimeSigMap::normalize()
      {
      int z    = 4;
      int n    = 4;
      int tick = 0;
      int bar  = 0;
      int tm   = ticks_measure(Fraction(z, n));

      for (iSigEvent i = begin(); i != end(); ++i) {
            SigEvent& e  = i->second;
            e.bar        = bar + (i->first - tick) / tm;
            bar          = e.bar;
            tick         = i->first;
            if (e.nominalEqualActual()) {
                  tm      = ticks_measure(e.fraction());
                  e.ticks = tm;
                  }
            }
      ++_serial;
      }

//---------------------------------------------------------
//   ticksMeasure
//---------------------------------------------------------

int TimeSigMap::ticksMeasure(int tick) const
      {
      return timesig(tick).ticks;
      }

//---------------------------------------------------------
//   timesig
//---------------------------------------------------------

const SigEvent& TimeSigMap::timesig(int tick) const
      {
      static const SigEvent ev(Fraction(4, 4));
      if (empty())
            return ev;
      ciSigEvent i = upper_bound(tick);
      if (i != begin())
            --i;
      return i->second;
      }

//---------------------------------------------------------
//   tickValues
//---------------------------------------------------------

void TimeSigMap::tickValues(int t, int* bar, int* beat, int* tick) const
      {
      ciSigEvent e = upper_bound(t);
      if (empty() || e == begin()) {
            fprintf(stderr, "tickValue(0x%x) not found\n", t);
            abort();
            }
      --e;
      int delta  = t - e->first;
      int ticksB = ticks_beat(e->second.fraction().denominator());
      int ticksM = ticksB * e->second.fraction().numerator();
      if (ticksM == 0) {
            printf("TimeSigMap::tickValues: at %d %s\n", t, qPrintable(e->second.fraction().print()));
            *bar = 0;
            *beat = 0;
            *tick = 0;
            return;
            }
      *bar       = e->second.bar + delta / ticksM;
      int rest   = delta % ticksM;
      *beat      = rest / ticksB;
      *tick      = rest % ticksB;
      }

//---------------------------------------------------------
//   bar2tick
//---------------------------------------------------------

int TimeSigMap::bar2tick(int bar, int beat, int tick) const
      {
      ciSigEvent e;

      for (e = begin(); e != end(); ++e) {
            if (bar < e->second.bar)
                  break;
            }
      if (empty() || e == begin()) {
            fprintf(stderr, "TimeSigMap::bar2tick(): not found(%d,%d,%d) not found\n",
               bar, beat, tick);
            if (empty())
                  fprintf(stderr, "   list is empty\n");
            // abort();
            return 0;
            }
      --e;
      int ticksB = ticks_beat(e->second.fraction().denominator());
      int ticksM = ticksB * e->second.fraction().numerator();
      return e->first + (bar - e->second.bar) * ticksM + ticksB * beat + tick;
      }

//---------------------------------------------------------
//   TimeSigMap::write
//---------------------------------------------------------

void TimeSigMap::write(Xml& xml) const
      {
      xml.stag("siglist");
      for (ciSigEvent i = begin(); i != end(); ++i)
            i->second.write(xml, i->first);
      xml.etag();
      }

//---------------------------------------------------------
//   TimeSigMap::read
//---------------------------------------------------------

void TimeSigMap::read(QDomElement e, int fileDivision)
      {
      for (e = e.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
            QString tag(e.tagName());
            if (tag == "sig") {
                  SigEvent t;
                  int tick = t.read(e, fileDivision);
                  (*this)[tick] = t;
                  }
            else
                  domError(e);
            }
      normalize();
      }

//---------------------------------------------------------
//   SigEvent::write
//---------------------------------------------------------

void SigEvent::write(Xml& xml, int tick) const
      {
      xml.stag(QString("sig tick=\"%1\"").arg(tick));
      if (!nominalEqualActual()) {
            xml.tag("nom2",   nominal.numerator());
            xml.tag("denom2", nominal.denominator());
            }
      xml.tag("nom",   actual.numerator());
      xml.tag("denom", actual.denominator());
      xml.etag();
      }

//---------------------------------------------------------
//   SigEvent::read
//---------------------------------------------------------

int SigEvent::read(QDomElement e, int fileDivision)
      {
      int tick  = e.attribute("tick", "0").toInt();
      tick      = tick * AL::division / fileDivision;

      int nominator;
      int denominator;
      int denominator2 = -1;
      int nominator2   = -1;
      for (e = e.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
            QString tag(e.tagName());
            int i = e.text().toInt();

            if (tag == "nom")
                  nominator = i;
            else if (tag == "denom")
                  denominator = i;
            else if (tag == "nom2")
                  nominator2 = i;
            else if (tag == "denom2")
                  denominator2 = i;
            else
                  domError(e);
            }
      if ((nominator2 == -1) || (denominator2 == -1)) {
            nominator2   = nominator;
            denominator2 = denominator;
            }
      actual = Fraction(nominator, denominator);
      nominal = Fraction(nominator2, denominator2);
      ticks = ticks_measure(actual);
      return tick;
      }

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

void TimeSigMap::removeTime(int tick, int len)
      {
// printf("TimeSigMap::removeTime %d len %d\n", tick, len);
      TimeSigMap tmp;
      for (ciSigEvent i = begin(); i != end(); ++i) {
            // entry at tick 0 is sticky
            if ((i->first >= tick) && (i->first != 0)) {
                  if (i->first >= tick + len)
                        tmp.add(i->first - len, i->second);
                  else
                        printf("TimeSigMap::remove sig event\n");
                  }
            else
                  tmp.add(i->first, i->second);
            }
      *this = tmp;
//      clear();
//      insert(tmp.begin(), tmp.end());
      normalize();
      }

//---------------------------------------------------------
//   insert
//---------------------------------------------------------

void TimeSigMap::insertTime(int tick, int len)
      {
// printf("TimeSigMap::insertTime %d len %d\n", tick, len);
      TimeSigMap tmp;
      for (ciSigEvent i = begin(); i != end(); ++i) {
            // entry at tick 0 is sticky
            if (i->first >= tick && (i->first != 0))
                  tmp.add(i->first + len, i->second);
            else
                  tmp.add(i->first, i->second);
            }
      *this = tmp;
//      clear();
//      insert(tmp.begin(), tmp.end());
      normalize();
      }

//---------------------------------------------------------
//   raster
//---------------------------------------------------------

unsigned TimeSigMap::raster(unsigned t, int raster) const
      {
      if (raster == 1)
            return t;
      ciSigEvent e = upper_bound(t);
      if (e == end()) {
            printf("TimeSigMap::raster(%x,)\n", t);
            // abort();
            return t;
            }
      int delta  = t - e->first;
      int ticksM = ticks_beat(e->second.fraction().denominator()) * e->second.fraction().numerator();
      if (raster == 0)
            raster = ticksM;
      int rest   = delta % ticksM;
      int bb     = (delta/ticksM)*ticksM;
      return  e->first + bb + ((rest + raster/2)/raster)*raster;
      }

//---------------------------------------------------------
//   raster1
//    round down
//---------------------------------------------------------

unsigned TimeSigMap::raster1(unsigned t, int raster) const
      {
      if (raster == 1)
            return t;
      ciSigEvent e = upper_bound(t);

      int delta  = t - e->first;
      int ticksM = ticks_beat(e->second.fraction().denominator()) * e->second.fraction().numerator();
      if (raster == 0)
            raster = ticksM;
      int rest   = delta % ticksM;
      int bb     = (delta/ticksM)*ticksM;
      return  e->first + bb + (rest/raster)*raster;
      }

//---------------------------------------------------------
//   raster2
//    round up
//---------------------------------------------------------

unsigned TimeSigMap::raster2(unsigned t, int raster) const
      {
      if (raster == 1)
            return t;
      ciSigEvent e = upper_bound(t);

      int delta  = t - e->first;
      int ticksM = ticks_beat(e->second.fraction().denominator()) * e->second.fraction().numerator();
      if (raster == 0)
            raster = ticksM;
      int rest   = delta % ticksM;
      int bb     = (delta/ticksM)*ticksM;
      return  e->first + bb + ((rest+raster-1)/raster)*raster;
      }

//---------------------------------------------------------
//   rasterStep
//---------------------------------------------------------

int TimeSigMap::rasterStep(unsigned t, int raster) const
      {
      if (raster == 0) {
            ciSigEvent e = upper_bound(t);
            return ticks_beat(e->second.fraction().denominator()) * e->second.fraction().numerator();
            }
      return raster;
      }

//---------------------------------------------------------
//   measureRest
//    return (measure end - tick) as a fraction
//    (silly implementation)
//---------------------------------------------------------

Fraction TimeSigMap::measureRest(unsigned tick) const
      {
      const SigEvent& e = timesig(tick);
      int bar, beat, restTicks;
      tickValues(tick, &bar, &beat, &restTicks);
      int stick = bar2tick(bar, 0, 0);
      return e.fraction() - Fraction::fromTicks(tick - stick);
      }

//---------------------------------------------------------
//   TimeSigMap::dump
//---------------------------------------------------------

void TimeSigMap::dump() const
      {
      printf("TimeSigMap:\n");
      for (ciSigEvent i = begin(); i != end(); ++i)
            printf("%6d timesig: %s measure: %d\n",
               i->first, qPrintable(i->second.fraction().print()), i->second.bar);
      }
      
}     // namespace AL
