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

#include <assert.h>
#include "pos.h"
#include "xml.h"
#include "tempo.h"
#include "globals.h"
//#include "song.h"
#include "sig.h"

extern int mtcType;

//---------------------------------------------------------
//   Pos
//---------------------------------------------------------

Pos::Pos()
      {
      _type      = TICKS;
      _posTick   = 0;
      _posTime   = 0.0;
      _posSample = 0;
      sn         = -1;
      }

Pos::Pos(const Pos& p)
      {
      _type      = p._type;
      _posTick   = p._posTick;
      _posTime   = p._posTime;
      _posSample = p._posSample;
      sn         = -1;
      }

Pos::Pos(int t, bool ticks)
      {
      if (ticks) {
            _type      = TICKS;
            _posTick   = t;
            _posSample = 0;
            }
      else {
            _type      = SAMPLES;
            _posSample = t;
            _posTick   = 0;
            }
      _posTime   = 0.0;
      sn         = -1;
      }

Pos::Pos(const QString& s)
      {
      int m, b, t;
      sscanf(s.latin1(), "%04d.%02d.%03d", &m, &b, &t);
      int tick = sigmap.bar2tick(m, b, t);
      _type      = TICKS;
      _posTick   = tick;
      _posTime   = 0.0;
      _posSample = 0;
      sn         = -1;
      }

//---------------------------------------------------------
//   setType
//---------------------------------------------------------

void Pos::setType(TType t)
      {
      if (t == _type)
            return;

      switch(_type) {
            case TIME:
                  if (t == TICKS)
                        _posTick = tempomap.time2tick(_posTime, _posTick, &sn);
                  else
                        _posSample = int(_posTime * sampleRate);
                  break;

            case TICKS:
                  _posTime = tempomap.tick2time(_posTick, _posTime, &sn);
                  _posSample = int(_posTime * sampleRate);
                  break;

            case SAMPLES:
                  if (t == TICKS)
                        _posTick = tempomap.time2tick(double(_posSample)/double(sampleRate),
                           _posTick, &sn);
                  else
                        _posTime = double(_posSample)/double(sampleRate);
                  break;
            }
      _type = t;
      sn    = -1;
      }

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

Pos& Pos::operator+=(Pos a)
      {
      switch(_type) {
            case SAMPLES:
                  _posSample += a.posSample();
                  break;
            case TICKS:
                  _posTick += a.posTick();
                  break;
            case TIME:
                  _posTime += a.posTime();
                  break;
            }
      sn = -1;          // invalidate cached values
      return *this;
      }

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

Pos& Pos::operator+=(int a)
      {
      switch(_type) {
            case SAMPLES:
                  _posSample += a;
                  break;
            case TICKS:
                  _posTick += a;
                  break;
            case TIME:
                  printf("not impl.\n");
                  abort();
            }
      sn = -1;          // invalidate cached values
      return *this;
      }

Pos operator+(Pos a, int b)
      {
      Pos c;
      c.setType(a.type());
      return c += b;
      }

Pos operator+(Pos a, Pos b)
      {
      Pos c = a;
      return c += b;
      }

//---------------------------------------------------------
//   posTick
//---------------------------------------------------------

int Pos::posTick() const
      {
      switch(_type) {
            case TIME:
                  _posTick = tempomap.time2tick(_posTime, _posTick, &sn);
                  break;
            case TICKS:
                  break;
            case SAMPLES:
                  _posTick = tempomap.time2tick(double(_posSample)/double(sampleRate),
                     _posTick, &sn);
                  break;
            }
      return _posTick;
      }

//---------------------------------------------------------
//   posSample
//---------------------------------------------------------

int Pos::posSample() const
      {
      switch(_type) {
            case SAMPLES:
                  break;
            case TICKS:
                  _posTime = tempomap.tick2time(_posTick, _posTime, &sn);
                  // fall trough
            case TIME:
                  _posSample = int(_posTime * sampleRate);
                  break;
            }
// printf("posSample type %d, %d\n", _type, _posSample);
      return _posSample;
      }

//---------------------------------------------------------
//   posTime
//---------------------------------------------------------

double Pos::posTime() const
      {
      switch(_type) {
            case SAMPLES:
                  _posTime = double(_posSample)/double(sampleRate);
                  break;
            case TICKS:
                  _posTime = tempomap.tick2time(_posTick, _posTime, &sn);
                  break;
            case TIME:
                  break;
            }
      return _posTime;
      }

//---------------------------------------------------------
//   setPosTick
//---------------------------------------------------------

void Pos::setPosTick(int pos)
      {
      _posTick = pos;
      sn    = -1;
      switch(_type) {
            case SAMPLES:
                  _posSample = int(tempomap.tick2time(pos, &sn) * sampleRate);
                  break;
            case TIME:
                  _posTime = tempomap.tick2time(pos, &sn);
                  break;
            case TICKS:
                  break;
            }
      }

//---------------------------------------------------------
//   setPosSample
//---------------------------------------------------------

void Pos::setPosSample(int pos)
      {
      _posSample = pos;
      sn      = -1;
      switch(_type) {
            case TICKS:
                  _posTick = tempomap.time2tick(double(pos)/double(sampleRate), &sn);
                  break;
            case TIME:
                  _posTime = double(pos)/double(sampleRate);
                  break;
            case SAMPLES:
                  break;
            }
      }

//---------------------------------------------------------
//   setPosTime
//---------------------------------------------------------

void Pos::setPosTime(double pos)
      {
      _posTime = pos;
      sn = -1;
      switch(_type) {
            case TICKS:
                  _posTick = tempomap.time2tick(pos, &sn);
                  break;
            case SAMPLES:
                  _posSample = int(pos * sampleRate);
                  break;
            case TIME:
                  break;
            }
      }

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

void Pos::write(int level, Xml& xml, const char* name) const
      {
      xml.nput(level++, "<%s ", name);

      switch(_type) {
            case TICKS:
                  xml.nput("tick=\"%d\"", _posTick);
                  break;
            case SAMPLES:
                  xml.nput("sample=\"%d\"", _posSample);
                  break;
            case TIME:
                  xml.nput("time=\"%f\"", _posTime);
                  break;
            }
      xml.put(" />", name);
      }

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

void Pos::read(Xml& xml, const char* name)
      {
      sn = -1;
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;

                  case Xml::TagStart:
                        xml.unknown(name);
                        break;

                  case Xml::Attribut:
                        if (tag == "tick") {
                              _posTick = xml.s2().toInt();
                              _type = TICKS;
                              }
                        else if (tag == "sample") {
                              _posSample = xml.s2().toInt();
                              _type = SAMPLES;
                              }
                        else if (tag == "time") {
                              _posTime = xml.s2().toDouble();
                              _type = TIME;
                              }
                        else
                              xml.unknown(name);
                        break;

                  case Xml::TagEnd:
                        if (tag == name)
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   PosLen
//---------------------------------------------------------

PosLen::PosLen()
      {
      _lenTick     = 0;
      _lenTime     = 0.0;
      _lenSample   = 0;
      sn        = -1;
      }

PosLen::PosLen(const PosLen& p)
  : Pos(p)
      {
      _lenTick   = p._lenTick;
      _lenTime   = p._lenTime;
      _lenSample = p._lenSample;
      sn = -1;
      }

void PosLen::dump() const
      {
      Pos::dump();
      printf("  len ");
      switch(_type) {
            case SAMPLES:
                  printf(" samples=%d\n", _lenSample);
                  break;
            case TIME:
                  printf(" time=%f\n", _lenTime);
                  break;
            case TICKS:
                  printf(" ticks=%d\n", _lenTick);
                  break;
            }
      }

void Pos::dump() const
      {
      printf("  pos ");
      switch(_type) {
            case SAMPLES:
                  printf(" samples=%d\n", _posSample);
                  break;
            case TIME:
                  printf(" time=%f\n", _posTime);
                  break;
            case TICKS:
                  printf(" ticks=%d\n", _posTick);
                  break;
            }
      }

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

void PosLen::write(int level, Xml& xml, const char* name) const
      {
      xml.nput(level++, "<%s ", name);

      switch(_type) {
            case TICKS:
                  xml.nput("tick=\"%d\" len=\"%d\"", _posTick, _lenTick);
                  break;
            case SAMPLES:
                  xml.nput("sample=\"%d\" len=\"%d\"", _posSample, _lenSample);
                  break;
            case TIME:
                  xml.nput("time=\"%f\" len=\"%d\"", _posTime, _lenTime);
                  break;
            }
      xml.put(" />", name);
      }

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

void PosLen::read(Xml& xml, const char* name)
      {
      sn = -1;
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;

                  case Xml::TagStart:
                        xml.unknown(name);
                        break;

                  case Xml::Attribut:
                        if (tag == "tick") {
                              _posTick = xml.s2().toInt();
                              _type = TICKS;
                              }
                        else if (tag == "sample") {
                              _posSample = xml.s2().toInt();
                              _type = SAMPLES;
                              }
                        else if (tag == "time") {
                              _posTime = xml.s2().toDouble();
                              _type = TIME;
                              }
                        else if (tag == "len") {
                              switch(type()) {
                                    case TICKS:
                                          _lenTick = xml.s2().toInt();
                                          break;
                                    case SAMPLES:
                                          setLenSample(xml.s2().toInt());
                                          break;
                                    case TIME:
                                          setLenTime(xml.s2().toDouble());
                                          break;
                                    }
                              }
                        else
                              xml.unknown(name);
                        break;

                  case Xml::TagEnd:
                        if (tag == name)
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   setLenTick
//---------------------------------------------------------

void PosLen::setLenTick(int len)
      {
      _lenTick = len;
      sn    = -1;
      switch(_type) {
            case SAMPLES:
                  _lenSample = int(tempomap.tick2time(len, &sn) * sampleRate);
                  break;
            case TIME:
                  _lenTime = tempomap.tick2time(len, &sn);
                  break;
            case TICKS:
                  break;
            }
      }

//---------------------------------------------------------
//   setLenSample
//---------------------------------------------------------

void PosLen::setLenSample(int len)
      {
      _lenSample = len;
      sn      = -1;
      switch(_type) {
            case TICKS:
                  _lenTick = tempomap.time2tick(double(len)/double(sampleRate), &sn);
                  break;
            case TIME:
                  _lenTime = double(len)/double(sampleRate);
                  break;
            case SAMPLES:
                  break;
            }
      }

//---------------------------------------------------------
//   setLenTime
//---------------------------------------------------------

void PosLen::setLenTime(double len)
      {
      _lenTime = len;
      sn = -1;
      switch(_type) {
            case TICKS:
                  _lenTick = tempomap.time2tick(len, &sn);
                  break;
            case SAMPLES:
                  _lenSample = int(len * sampleRate);
                  break;
            case TIME:
                  break;
            }
      }

//---------------------------------------------------------
//   lenTick
//---------------------------------------------------------

int PosLen::lenTick() const
      {
      switch(_type) {
            case TIME:
                  _lenTick = tempomap.time2tick(_lenTime, _lenTick, &sn);
                  break;
            case TICKS:
                  break;
            case SAMPLES:
                  _lenTick = tempomap.time2tick(double(_lenSample)/double(sampleRate),
                     _lenTick, &sn);
//printf("lenTick %p: type %d  sn %d samples %d, ticks %d\n",
//         this, _type, sn, _lenSample, _lenTick);
                  break;
            }
      return _lenTick;
      }

//---------------------------------------------------------
//   lenSample
//---------------------------------------------------------

int PosLen::lenSample() const
      {
      switch(_type) {
            case SAMPLES:
                  break;
            case TICKS:
                  _lenTime = tempomap.tick2time(_lenTick, _lenTime, &sn);
                  // fall trough
            case TIME:
                  _lenSample = int(_lenTime * sampleRate);
                  break;
            }
// printf("lenSample %p: type %d sn %d samples %d\n",
//    this, _type, sn, _lenSample);
      return _lenSample;
      }

//---------------------------------------------------------
//   lenTime
//---------------------------------------------------------

double PosLen::lenTime() const
      {
      switch(_type) {
            case SAMPLES:
                  _lenTime = double(_lenSample)/double(sampleRate);
                  break;
            case TICKS:
                  _lenTime = tempomap.tick2time(_lenTick, _lenTime, &sn);
                  break;
            case TIME:
                  break;
            }
      return _lenTime;
      }

//---------------------------------------------------------
//   end
//---------------------------------------------------------

Pos PosLen::end() const
      {
      Pos pos(*this);
      pos.invalidSn();
      switch(_type) {
            case SAMPLES:
                  pos.setPosSample(pos.posSample() + _lenSample);
                  break;
            case TICKS:
                  pos.setPosTick(pos.posTick() + _lenTick);
                  break;
            case TIME:
                  pos.setPosTime(pos.posTime() + _lenTime);
                  break;
            }
      return pos;
      }

//---------------------------------------------------------
//   setPos
//---------------------------------------------------------

void PosLen::setPos(const Pos& pos)
      {
      switch(pos.type()) {
            case SAMPLES:
                  setPosSample(pos.posSample());
                  break;
            case TICKS:
                  setPosTick(pos.posTick());
                  break;
            case TIME:
                  setPosTime(pos.posTime());
                  break;
            }
      }

Pos::Pos(int measure, int beat, int tick)
      {
      int t = sigmap.bar2tick(measure, beat, tick);
      _type      = TICKS;
      _posTick   = t;
      _posTime   = 0.0;
      _posSample = 0;
      sn         = -1;
      }

Pos::Pos(int min, int sec, int frame, int subframe)
      {
      double time = min * 60.0 + sec;

      double f = frame + subframe/100.0;
      switch(mtcType) {
            case 0:     // 24 frames sec
                  time += f * 1.0/24.0;
                  break;
            case 1:     // 25
                  time += f * 1.0/25.0;
                  break;
            case 2:     // 30 drop frame
                  time += f * 1.0/30.0;
                  break;
            case 3:     // 30 non drop frame
                  time += f * 1.0/30.0;
                  break;
            }
      _type      = TIME;
      _posTime   = time;
      sn         = -1;
      }


void Pos::mbt(int* bar, int* beat, int* tick) const
      {
      sigmap.tickValues(posTick(), bar, beat, tick);
      }

void Pos::msf(int* min, int* sec, int* frame, int* subFrame) const
      {
      double time = posTime();
      *min  = int(time) / 60;
      *sec  = int(time) % 60;
      double rest = time - (*min * 60 + *sec);
      switch(mtcType) {
            case 0:     // 24 frames sec
                  rest *= 24;
                  break;
            case 1:     // 25
                  rest *= 25;
                  break;
            case 2:     // 30 drop frame
                  rest *= 30;
                  break;
            case 3:     // 30 non drop frame
                  rest *= 30;
                  break;
            }
      *frame = int(rest);
      *subFrame = int((rest- *frame)*100);
      }

bool Pos::isValid(int,int,int)
      {
      return true;
      }

bool Pos::isValid(int,int,int,int)
      {
      return true;
      }

