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

#include <stdio.h>
#include <assert.h>
#include "sig.h"
#include "globals.h"
#include "xml.h"

SigList sigmap;

//---------------------------------------------------------
//   SigList
//---------------------------------------------------------

SigList::SigList()
      {
      insert(std::pair<const int, SigEvent*> (MAX_TICK, new SigEvent(4, 4, 0)));
      }

//---------------------------------------------------------
//   add
//    signatures are only allowed at the beginning of
//    a bar
//---------------------------------------------------------

void SigList::add(int tick, int z, int n)
      {
      if (z == 0 || n == 0) {
            printf("illegal signature %d/%d\n", z, n);
            }
      tick = raster1(tick, 0);
      iSigEvent e = upper_bound(tick);
      assert(e != end());

      if (tick == e->second->tick) {
            e->second->z = z;
            e->second->n = n;
            }
      else {
            SigEvent* ne = e->second;
            SigEvent* ev = new SigEvent(ne->z, ne->n, ne->tick);
            ne->z = z;
            ne->n = n;
            ne->tick = tick;
            insert(std::pair<const int, SigEvent*> (tick, ev));
            }
      normalize();
      }

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

void SigList::del(int tick)
      {
// printf("SigList::del(%d)\n", tick);
      iSigEvent e = find(tick);
      if (e == end()) {
            printf("SigList::del(%d): not found\n", tick);
            return;
            }
      iSigEvent ne = e;
      ++ne;
      if (ne == end()) {
            printf("SigList::del() HALLO\n");
            return;
            }
      ne->second->z = e->second->z;
      ne->second->n = e->second->n;
      ne->second->tick  = e->second->tick;
      erase(e);
      normalize();
      }

//---------------------------------------------------------
//   SigList::normalize
//---------------------------------------------------------

void SigList::normalize()
      {
      int z = 0;
      int n = 0;
      int tick = 0;
      iSigEvent ee;

      for (iSigEvent e = begin(); e != end();) {
            if (z == e->second->z && n == e->second->n) {
                  e->second->tick = tick;
                  erase(ee);
                  }
            z    = e->second->z;
            n    = e->second->n;
            ee   = e;
            tick = e->second->tick;
            ++e;
            }

      int bar = 0;
      for (iSigEvent e = begin(); e != end();) {
            e->second->bar = bar;
            int delta  = e->first - e->second->tick;
            int ticksB = ticks_beat(e->second->n);
            int ticksM = ticksB * e->second->z;
            bar += delta / ticksM;
            if (delta % ticksM)     // Teil eines Taktes
                  ++bar;
            ++e;
            }
      }

//---------------------------------------------------------
//   SigList::dump
//---------------------------------------------------------

void SigList::dump() const
      {
      printf("\nSigList:\n");
      for (ciSigEvent i = begin(); i != end(); ++i) {
            printf("%6d %06d Bar %3d %02d/%d\n",
               i->first, i->second->tick,
               i->second->bar, i->second->z, i->second->n);
            }
      }

void SigList::clear()
      {
      for (iSigEvent i = begin(); i != end(); ++i)
            delete i->second;
      std::map<int, SigEvent*, std::less<int> >::clear();
      insert(std::pair<const int, SigEvent*> (MAX_TICK, new SigEvent(4, 4, 0)));
      }

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

int SigList::ticksMeasure(int Z, int N) const
      {
      return ticks_beat(N) * Z;
      }

int SigList::ticksMeasure(int tick) const
      {
      ciSigEvent i = upper_bound(tick);
      if (i == end()) {
            printf("ticksMeasure: not found %d\n", tick);
            dump();
            abort();
            }
      return ticksMeasure(i->second->z, i->second->n);
      }

//---------------------------------------------------------
//   ticksBeat
//---------------------------------------------------------

int SigList::ticksBeat(int tick) const
      {
      ciSigEvent i = upper_bound(tick);
      assert(i != end());
      return ticks_beat(i->second->n);
      }

int SigList::ticks_beat(int n) const
      {
      int m = division;
      switch (n) {
            case  1:  m <<= 2; break;           // 1536
            case  2:  m <<= 1; break;           // 768
            case  3:  m += m >> 1; break;       // 384+192
            case  4:  break;                    // 384
            case  8:  m >>= 1; break;           // 192
            case 16:  m >>= 2; break;           // 96
            case 32:  m >>= 3; break;           // 48
            case 64:  m >>= 4; break;           // 24
            case 128: m >>= 5; break;           // 12
            default: assert(false); break;
            }
      return m;
      }

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

void SigList::timesig(int tick, int& z, int& n) const
      {
      ciSigEvent i = upper_bound(tick);
      if (i == end()) {
            printf("timesig(%d): not found\n", tick);
            abort();
            }
      z = i->second->z;
      n = i->second->n;
      }

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

void SigList::tickValues(int t, int* bar, int* beat, int* tick) const
      {
      if (t < 0)
            t = 0;
      ciSigEvent e = upper_bound(t);
      if (e == end()) {
            fprintf(stderr, "tickValues(0x%x) not found(%d)\n", t, size());
            abort();
            }

      int delta  = t - e->second->tick;
      int ticksB = ticks_beat(e->second->n);
      int ticksM = ticksB * e->second->z;
      *bar       = e->second->bar + delta / ticksM;
      int rest   = delta % ticksM;
      *beat      = rest / ticksB;
      *tick      = rest % ticksB;
// printf("tickValues %d: %d:%d:%d   delta %d\n", t, *bar, *beat, *tick, delta);
      }

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

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

      if (bar < 0)
            bar = 0;
      for (e = begin(); e != end();) {
            ciSigEvent ee = e;
            ++ee;
            if (ee == end())
                  break;
            if (bar < ee->second->bar)
                  break;
            e = ee;
            }
      int ticksB = ticks_beat(e->second->n);
      int ticksM = ticksB * e->second->z;
      return e->second->tick + (bar-e->second->bar)*ticksM + ticksB*beat + tick;
      }

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

int SigList::raster(int t, int raster) const
      {
      if (raster == 1)
            return t;
      ciSigEvent e = upper_bound(t);
      assert(e != end());

      int delta  = t - e->second->tick;
      int ticksM = ticks_beat(e->second->n) * e->second->z;
      if (raster == 0)
            raster = ticksM;
      int rest   = delta % ticksM;
      int bb     = (delta/ticksM)*ticksM;
      return  e->second->tick + bb + ((rest + raster/2)/raster)*raster;
      }

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

int SigList::raster1(int t, int raster) const
      {
      if (raster == 1)
            return t;
      ciSigEvent e = upper_bound(t);
      assert(e != end());

      int delta  = t - e->second->tick;
      int ticksM = ticks_beat(e->second->n) * e->second->z;
      if (raster == 0)
            raster = ticksM;
      int rest   = delta % ticksM;
      int bb     = (delta/ticksM)*ticksM;
      return  e->second->tick + bb + (rest/raster)*raster;
      }

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

int SigList::raster2(int t, int raster) const
      {
      if (raster == 1)
            return t;
      ciSigEvent e = upper_bound(t);
      assert(e != end());

      int delta  = t - e->second->tick;
      int ticksM = ticks_beat(e->second->n) * e->second->z;
      if (raster == 0)
            raster = ticksM;
      int rest   = delta % ticksM;
      int bb     = (delta/ticksM)*ticksM;
      return  e->second->tick + bb + ((rest+raster-1)/raster)*raster;
      }

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

int SigList::rasterStep(int t, int raster) const
      {
      if (raster == 0) {
            ciSigEvent e = upper_bound(t);
            assert(e != end());
            return ticks_beat(e->second->n) * e->second->z;
            }
      return raster;
      }

//---------------------------------------------------------
//   SigList::write
//---------------------------------------------------------

void SigList::write(int level, Xml& xml) const
      {
      xml.tag(level++, "siglist");
      for (ciSigEvent i = begin(); i != end(); ++i)
            i->second->write(level, xml, i->first);
      xml.tag(level, "/siglist");
      }

//---------------------------------------------------------
//   SigList::read
//---------------------------------------------------------

void SigList::read(Xml& xml)
      {
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::TagStart:
                        if (tag == "sig") {
                              SigEvent* t = new SigEvent();
                              int tick = t->read(xml);
                              iSigEvent pos = find(tick);
                              if (pos != end())
                                    erase(pos);
                              insert(std::pair<const int, SigEvent*> (tick, t));
                              }
                        else
                              xml.unknown("SigList");
                        break;
                  case Xml::Attribut:
                        break;
                  case Xml::TagEnd:
                        if (tag == "siglist") {
                              normalize();
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

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

void SigEvent::write(int level, Xml& xml, int at) const
      {
      xml.tag(level++, "sig at=\"%d\"", at);
      xml.intTag(level, "tick", tick);
      xml.intTag(level, "nom", z);
      xml.intTag(level, "denom", n);
      xml.tag(level, "/sig");
      }

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

int SigEvent::read(Xml& xml)
      {
      int at = 0;
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return 0;
                  case Xml::TagStart:
                        if (tag == "tick")
                              tick = xml.parseInt();
                        else if (tag == "nom")
                              z = xml.parseInt();
                        else if (tag == "denom")
                              n = xml.parseInt();
                        else
                              xml.unknown("SigEvent");
                        break;
                  case Xml::Attribut:
                        if (tag == "at")
                              at = xml.s2().toInt();
                        break;
                  case Xml::TagEnd:
                        if (tag == "sig")
                              return at;
                  default:
                        break;
                  }
            }
      return 0;
      }


