/*
 *
 *    soniK digital audio editor
 *    Copyright (C) 2003-2006  Robert Walker <rob@tenfoot.org.uk>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
#include "wavewidget.h"
#include "sonik_util.h"

#include "data.h"

#include <kdebug.h>

#include <qpainter.h>

using Sonik::WaveWidget;
using Sonik::Data;

WaveWidget::WaveWidget(const Data& data, uint8_t channel,
                       QWidget *parent, const char* name)
  : QWidget(parent,name),
    mData(data),
    mChannel(channel),
    mSelStart(0),
    mSelLength(0),
    mCursorPos(0),
    mPlaybackPos(0),
    mScrollPos(0),
    mZoom(1.0)
{
  setBackgroundMode(Qt::NoBackground);
  setMouseTracking(true);
}

WaveWidget::~WaveWidget()
{
}

void WaveWidget::lengthChanged(size_t length)
{
  int endPos = Sonik::timeToScreenM(length, mScrollPos, mZoom);

  if (endPos < width())
    update(endPos, 0, width() - endPos, height());

  clearCache(LengthChanged, 0, length);
}

void WaveWidget::dataChanged(off_t start, size_t length)
{
  uint left, right;

  clearCache(DataChanged, start, length);

  left = QMAX(0, Sonik::timeToScreenL(start - 1, mScrollPos, mZoom));
  right = QMIN(width(), Sonik::timeToScreenU(start + length + 1,
                                             mScrollPos, mZoom));

  update(left, 0, right - left, height());
}

void WaveWidget::selectionChanged(off_t start, size_t length)
{
  uint oldStart = mSelStart;
  uint oldLength = mSelLength;
  uint oldEnd = oldStart + oldLength;
  uint end = start + length;

  uint oLeft, oRight, nLeft, nRight;

  mSelStart = start;
  mSelLength = length;

  oLeft = QMAX(0, Sonik::timeToScreenL(oldStart, mScrollPos, mZoom));
  if (oldLength > 0)
    oRight = QMIN(width(), Sonik::timeToScreenU(oldEnd, mScrollPos, mZoom));
  else
    oRight = QMIN(width(), Sonik::timeToScreenU(oldStart, mScrollPos, mZoom));

  update(oLeft, 0, oRight-oLeft+1, height());

  nLeft = QMAX(0, Sonik::timeToScreenL(start, mScrollPos, mZoom));
  if (length > 0)
    nRight = QMIN(width(), Sonik::timeToScreenU(end, mScrollPos, mZoom));
  else
    nRight = QMIN(width(), Sonik::timeToScreenU(start, mScrollPos, mZoom));

  update(nLeft, 0, nRight-nLeft+1, height());
}

void WaveWidget::cursorPosChanged(off_t)
{
}

void WaveWidget::playbackPosChanged(off_t)
{
}

void WaveWidget::zoomChanged(float newZoom)
{
  mZoom = newZoom;

  //Redraw whole widget
  update();
}

void WaveWidget::scrollPosChanged(int newPos)
{
  int deltaX;

  if (mZoom < 1.0f)
    newPos -= (newPos % (int)(1.0/mZoom));

  if (mScrollPos != newPos)
  {
    deltaX = (int)(newPos * mZoom) - (int)(mScrollPos * mZoom);

    mScrollPos = newPos;

    scroll(-deltaX, 0);
  }
}

void WaveWidget::paintEvent(QPaintEvent *e)
{
  if (isVisible())
  {
    QPainter p(this);
    QRect r = e->rect();

    uint start = Sonik::screenToTime(r.left(), mScrollPos, mZoom);
    uint length = Sonik::screenToTime(r.width(), 0, mZoom) + 2;

    // grey out region outside of waveform
    if (start + length > mData.length())
    {
      // get last pixel drawn by plugin
      length = mData.length() - start;
      int last = start + length - 1;
      if (last < 0)
        last = 0;
      int xMax = Sonik::timeToScreenM(last, mScrollPos, mZoom);

      // fill with gray stipple
      p.setBackgroundMode(Qt::OpaqueMode);
      p.setBackgroundColor(Qt::white);
      p.fillRect(xMax+1, r.top(), r.right()-xMax, r.height(),
                 QBrush(Qt::darkGray, Qt::Dense5Pattern));
      p.setBackgroundMode(Qt::TransparentMode);
      r.setRight(xMax);
    }

    // render waveform region
    if (start < mData.length() && r.right() >= r.left())
    {
      // render in 256px (or less) slices to a pixmap and then blt to screen.
      // This improves performance and avoids tearing and flicker seen when
      // drawing directly.

      QPixmap strip(r.width() > 256 ? 256 : r.width(), r.height());
      QRect stripRect(r.left(), r.top(), strip.width(), r.height());

      int xPos = r.left();
      int pxLeft = r.width();
      while (pxLeft > 0)
      {
        QPainter stripP;
        stripP.begin(&strip);
        // map screen co-ords for current strip to pixmap
        stripP.translate(-stripRect.left(), -stripRect.top());
        render(stripP, stripRect);
        stripP.end();

        bitBlt(this, xPos, r.top(),
               &strip, 0, 0, stripRect.width(), stripRect.height(), Qt::CopyROP);

        xPos += strip.width();
        pxLeft -= strip.width();
        stripRect.moveBy(strip.width(), 0);
        if (pxLeft < stripRect.width())
          stripRect.setWidth(pxLeft);
      }
    }
  }
}

void WaveWidget::clearCache(CacheClearOp /*op*/, off_t /*start*/, size_t /*length*/)
{
}
