//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: waveview.cpp,v 1.10 2004/04/24 14:58:53 wschweer Exp $
//  (C) Copyright 2000 Werner Schweer (ws@seh.de)
//=========================================================

#include <stdio.h>
#include <values.h>

#include <qpainter.h>
#include "globals.h"
#include "wave.h"
#include "waveview.h"
#include "song.h"
#include "event.h"
#include "midieditor.h"

//---------------------------------------------------------
//   WaveView
//---------------------------------------------------------

WaveView::WaveView(MidiEditor* pr, QWidget* parent, int xscale, int yscale)
   : View(parent, xscale, 1)
      {
      editor = pr;
      setVirt(true);
      pos[0] = tempomap.tick2frame(song->cpos());
      pos[1] = tempomap.tick2frame(song->lpos());
      pos[2] = tempomap.tick2frame(song->rpos());
      yScale = yscale;

      setMouseTracking(true);
      setBg(QColor(192, 208, 255));

      if (editor->parts()->empty()) {
            curPart = 0;
            curPartId = -1;
            }
      else {
            curPart   = (WavePart*)(editor->parts()->begin()->second);
            curPartId = curPart->sn();
            }
      connect(song, SIGNAL(posChanged(int,unsigned,bool)), SLOT(setPos(int,unsigned,bool)));
      songChanged(SC_SELECTION);
      }

//---------------------------------------------------------
//   setYScale
//---------------------------------------------------------

void WaveView::setYScale(int val)
      {
      yScale = val;
      redraw();
      }

//---------------------------------------------------------
//   draw
//---------------------------------------------------------

void WaveView::pdraw(QPainter& p, const QRect& rr)
      {
      int x1 = rr.x();
      int x2 = rr.right() + 1;
      if (x1 < 0)
            x1 = 0;
      if (x2 > width())
            x2 = width();
      int hh = height();
      int h  = hh/2;
      int y  = rr.y() + h;

      for (iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) {
            WavePart* wp = (WavePart*)(ip->second);
            int channels = wp->track()->channels();
            int px = wp->frame();

            EventList* el = wp->events();
            for (iEvent e = el->begin(); e != el->end(); ++e) {
                  Event event  = e->second;
                  if (event.empty())
                        continue;
                  SndFileR f = event.sndFile();
                  if (f.isNull())
                        continue;
                  int xScale = xmag;
                  if (xScale < 0)
                        xScale = -xScale;

                  int sx, ex;
                  sx = event.frame() + px + xScale/2 - startSample;
                  ex = sx + event.lenFrame();
                  sx = sx / xScale - xpos;
                  ex = ex / xScale - xpos;

                  if (sx < x1)
                        sx = x1;
                  if (ex > x2)
                        ex = x2;

                  int pos = (xpos + sx) * xScale + event.spos() - event.frame() - px;
                  h       = hh / (channels * 2);
                  int cc  = hh % (channels * 2) ? 0 : 1;

                  for (int i = sx; i < ex; i++) {
                        y  = rr.y() + h;
                        SampleV sa[f.channels()];
                        f.read(sa, xScale, pos);
                        pos += xScale;
                        for (int k = 0; k < channels; ++k) {
                              int kk = k % f.channels();
                              int peak = (sa[kk].peak * (h - 1)) / yScale;
                              int rms  = (sa[kk].rms  * (h - 1)) / yScale;
                              if (peak > h)
                                    peak = h;
                              if (rms > h)
                                    rms = h;
                              p.setPen(QColor(darkGray));
                              p.drawLine(i, y - peak - cc, i, y + peak);
                              p.setPen(QColor(black));
                              p.drawLine(i, y - rms - cc, i, y + rms);
                              y  += 2 * h;
                              }
                        }
                  }
            }
      View::pdraw(p, rr);
      }

//---------------------------------------------------------
//   draw
//---------------------------------------------------------

void WaveView::draw(QPainter& p, const QRect& r)
      {
      int x = r.x();
      int y = r.y();
      int w = r.width();
      int h = r.height();
      if (x < 0)
            x = 0;
      if (y < 0)
            y = 0;

      int x2 = x + w;
      int y2 = y + h;

      //
      //    draw marker & centerline
      //
      p.setPen(red);
      if (pos[0] >= x && pos[0] < x2) {
            p.drawLine(pos[0], y, pos[0], y2);
            }
      p.setPen(blue);
      if (pos[1] >= x && pos[1] < x2) {
            p.drawLine(pos[1], y, pos[1], y2);
            }
      if (pos[2] >= x && pos[2] < x2)
            p.drawLine(pos[2], y, pos[2], y2);

      int n  = curPart->track()->channels();
      int hn = h / n;
      int hh = hn / 2;
      for (int i = 0; i < n; ++i) {
            int h2     = hn * i;
            int center = hh + h2;
            p.setPen(QColor(i & i ? red : blue));
            p.drawLine(x, center, x2, center);
            p.setPen(QColor(black));
            p.drawLine(x, h2, x2, h2);
            }
      }

//---------------------------------------------------------
//   getCaption
//---------------------------------------------------------

QString WaveView::getCaption() const
      {
      return QString("Part ") + curPart->name();
      }

//---------------------------------------------------------
//   songChanged
//---------------------------------------------------------

void WaveView::songChanged(int flags)
      {
      if (flags & SC_SELECTION) {
            startSample  = MAXINT;
            endSample    = 0;
            curPart      = 0;
            for (iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) {
                  WavePart* part = (WavePart*)(p->second);
                  if (part->sn() == curPartId)
                        curPart = part;
                  int ssample = part->frame();
                  int esample = ssample + part->lenFrame();
                  if (ssample < startSample)
                        startSample = ssample;
                  if (esample > endSample)
                        endSample = esample;
                  }
            }
      if (flags & SC_TEMPO) {
            setPos(0, song->cpos(), false);
            setPos(1, song->lpos(), false);
            setPos(2, song->rpos(), false);
            }
      redraw();
      }

//---------------------------------------------------------
//   setPos
//    set one of three markers
//    idx   - 0-cpos  1-lpos  2-rpos
//    flag  - emit followEvent()
//---------------------------------------------------------

void WaveView::setPos(int idx, unsigned val, bool adjustScrollbar)
      {
      val = tempomap.tick2frame(val);
      if (pos[idx] == val)
            return;
      int opos = mapx(pos[idx]);
      int npos = mapx(val);

      if (adjustScrollbar && idx == 0) {
            switch (song->follow()) {
                  case  Song::NO:
                        break;
                  case Song::JUMP:
                        if (npos >= width()) {
                              int ppos =  val - rmapxDev(width()/4);
                              if (ppos < 0)
                                    ppos = 0;
                              emit followEvent(ppos);
                              opos = mapx(pos[idx]);
                              npos = mapx(val);
                              }
                        else if (npos < 0) {
                              int ppos =  val - rmapxDev(width()*3/4);
                              if (ppos < 0)
                                    ppos = 0;
                              emit followEvent(ppos);
                              opos = mapx(pos[idx]);
                              npos = mapx(val);
                              }
                        break;
                  case Song::CONTINUOUS:
                        if (npos > (width()*5)/8) {
                              int ppos =  pos[idx] - rmapxDev(width()*5/8);
                              if (ppos < 0)
                                    ppos = 0;
                              emit followEvent(ppos);
                              opos = mapx(pos[idx]);
                              npos = mapx(val);
                              }
                        else if (npos < (width()*3)/8) {
                              int ppos =  pos[idx] - rmapxDev(width()*3/8);
                              if (ppos < 0)
                                    ppos = 0;
                              emit followEvent(ppos);
                              opos = mapx(pos[idx]);
                              npos = mapx(val);
                              }
                        break;
                  }
            }

      int x;
      int w = 1;
      if (opos > npos) {
            w += opos - npos;
            x = npos;
            }
      else {
            w += npos - opos;
            x = opos;
            }
      pos[idx] = val;
      redraw(QRect(x, 0, w, height()));
      }

//---------------------------------------------------------
//   viewMousePressEvent
//---------------------------------------------------------

void WaveView::viewMousePressEvent(QMouseEvent* event)
      {
      button = event->button();
      viewMouseMoveEvent(event);
      }

void WaveView::viewMouseReleaseEvent(QMouseEvent*)
      {
      button = QMouseEvent::NoButton;
      }

//---------------------------------------------------------
//   viewMouseMoveEvent
//---------------------------------------------------------

void WaveView::viewMouseMoveEvent(QMouseEvent* event)
      {
      unsigned x = event->x();
      emit timeChanged(x);

      int i;
      switch (button) {
            case QMouseEvent::LeftButton:
                  i = 0;
                  break;
            case QMouseEvent::MidButton:
                  i = 1;
                  break;
            case QMouseEvent::RightButton:
                  i = 2;
                  break;
            default:
                  return;
            }
      Pos p(tempomap.frame2tick(x), true);
      song->setPos(i, p);
      }

//---------------------------------------------------------
//   range
//    returns range in samples
//---------------------------------------------------------

void WaveView::range(int* s, int *e)
      {
      *s = curPart->frame();
      int etick = curPart->tick() + curPart->lenTick();
      *e = tempomap.tick2frame(etick);
      }

