/*
 *
 *    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 "zoom.h"

#include <qpushbutton.h>
#include <qlabel.h>
#include <qhbox.h>
#include <qvbox.h>
#include <qpainter.h>
#include <qtimer.h>
#include <qstyle.h>

#include <kdebug.h>

#include <math.h>

// TODO: use internal QObject timer

using Sonik::Zoom;

namespace
{
  static const int kMinSize = 80;
}

Zoom::Zoom(QWidget *parent, const char *name )
  : QWidget(parent,name),
    mOrientation(Qt::Horizontal),
    mMinPower(0),
    mMaxPower(0),
    mCurPower(0),
    mCurValue(1.0),
    mShowLabel(true),
    mZoomIn(0),
    mZoomOut(0),
    mRepeatTimer(0),
    mButtonState(kButtonNone)
{
  init();
}

Zoom::Zoom(Qt::Orientation orientation, bool showLabel,
           QWidget * parent, const char * name)
  : QWidget(parent,name),
    mOrientation(orientation),
    mMinPower(0),
    mMaxPower(0),
    mCurPower(0),
    mCurValue(1.0),
    mShowLabel(showLabel),
    mZoomIn(0),
    mZoomOut(0),
    mRepeatTimer(0),
    mButtonState(kButtonNone)
{
  init();
}

Zoom::Zoom(int minPower, int maxPower, int initPower,
           Qt::Orientation orientation, bool showLabel,
           QWidget * parent, const char * name)
  : QWidget(parent,name),
    mOrientation(orientation),
    mMinPower(minPower),
    mMaxPower(maxPower),
    mCurPower(initPower),
    mShowLabel(showLabel),
    mZoomIn(0),
    mZoomOut(0),
    mRepeatTimer(0),
    mButtonState(kButtonNone)
{
  mCurValue = pow(2, initPower);

  init();
}

Zoom::~Zoom()
{
}


void Zoom::setMinPower(int newPow)
{
  mMinPower = newPow;
  if (newPow > mCurPower)
    setCurPower(newPow);
}

void Zoom::setMaxPower(int newPow)
{
  mMaxPower = newPow;
  if (newPow < mCurPower)
    setCurPower(newPow);
}

void Zoom::setCurValue(float newVal)
{
  int newPow;

  newPow = (int)rint(log(newVal)/log(2));
  setCurPower(newPow);
}

void Zoom::setCurPower(int newPow)
{
  if (newPow > mMaxPower)
    newPow = mMaxPower;
  if (newPow < mMinPower)
    newPow = mMinPower;

  if (newPow != mCurPower)
  {
    mCurPower = newPow;
    mCurValue = pow(2, mCurPower);
    updateLabel();
    emit zoomChanged(mCurValue);
  }
}

void Zoom::zoomIn()
{
  if (mCurPower < mMaxPower)
  {
    mCurPower++;
    mCurValue = pow(2, mCurPower);
    updateLabel();
    emit zoomChanged(mCurValue);
  }
}

void Zoom::zoomOut()
{
  if (mCurPower > mMinPower)
  {
    mCurPower--;
    mCurValue = pow(2, mCurPower);
    updateLabel();
    emit zoomChanged(mCurValue);
  }
}

QSize Zoom::sizeHint() const
{
  int minor = style().pixelMetric(QStyle::PM_ScrollBarExtent, this);
  int major;

  if (mShowLabel)
  {
    QSize textSize = fontMetrics().size(0, mText);
    major = QMAX(kMinSize, textSize.width() + textSize.height() * 2);
  }
  else
  {
    major = minor * 2; // 2 square buttons
  }

  QSize hint(major, minor);
  if (mOrientation == Qt::Vertical)
    hint.transpose();

  return hint;
}

void Zoom::zoomInClicked()
{
  zoomIn();
}

void Zoom::zoomOutClicked()
{
  zoomOut();
}

void Zoom::zoomInPressed()
{
  mRepeatTimer = new QTimer(this, "autoRepeat");
  connect(mRepeatTimer, SIGNAL(timeout()), this, SLOT(autoRepeat()));
  mButtonState = kButtonIn;
  mRepeatTimer->start(500, FALSE);
}

void Zoom::zoomOutPressed()
{
  mRepeatTimer = new QTimer(this, "autoRepeat");
  connect(mRepeatTimer, SIGNAL(timeout()), this, SLOT(autoRepeat()));
  mButtonState = kButtonOut;
  mRepeatTimer->start(500, FALSE);
}

void Zoom::zoomInReleased()
{
  delete mRepeatTimer;
  mRepeatTimer = 0;
  mButtonState = kButtonNone;
}

void Zoom::zoomOutReleased()
{
  delete mRepeatTimer;
  mRepeatTimer = 0;
  mButtonState = kButtonNone;
}

void Zoom::autoRepeat(void)
{
  if (mButtonState == kButtonIn)
    zoomIn();
  else if (mButtonState == kButtonOut)
    zoomOut();
  else
    delete mRepeatTimer;
}

void Zoom::updateLabel()
{
  if (mShowLabel)
  {
    if (mCurPower >= 0)
      mText = QString::number((uint)mCurValue) + ":1";
    else
      mText = "1:" + QString::number((uint)(1 / mCurValue));
  }

  update();
  updateGeometry();
}

void Zoom::paintEvent(QPaintEvent*)
{
  QString str;
  QPainter p(this);

  if (!mShowLabel)
    return;

  int btnSz = (mOrientation == Qt::Horizontal) ? height() : width();

  if (mOrientation == Qt::Horizontal)
  {
    p.drawText(btnSz, 0, width() - 2 * btnSz, height(), QPainter::AlignCenter, mText);
  }
  else
  {
    p.translate(btnSz/2, height()/2);
    p.rotate(90);
    // NB: width & height are reversed due to rotation
    p.drawText(-height()/2, -btnSz/2, height(), btnSz, QPainter::AlignCenter, mText);
  }
}

void Zoom::resizeEvent(QResizeEvent*)
{
  uint btnSz = (mOrientation == Qt::Horizontal) ? height() : width();

  mZoomOut->setGeometry(0, 0, btnSz, btnSz);

  if (mOrientation == Qt::Horizontal)
    mZoomIn->setGeometry(width() - btnSz, 0, btnSz, btnSz);
  else
    mZoomIn->setGeometry(0, height() - btnSz, btnSz, btnSz);
}

void Zoom::init()
{
  mZoomIn = new QPushButton(this, "ZoomIn");
  mZoomIn->setText("+");
  connect(mZoomIn, SIGNAL(clicked()), this, SLOT(zoomInClicked()));
  connect(mZoomIn, SIGNAL(pressed()), this, SLOT(zoomInPressed()));
  connect(mZoomIn, SIGNAL(released()), this, SLOT(zoomInReleased()));
  mZoomIn->setFocusPolicy(QWidget::NoFocus);

  mZoomOut = new QPushButton(this, "ZoomOut");
  mZoomOut->setText("-");
  connect(mZoomOut, SIGNAL(clicked()), this, SLOT(zoomOutClicked()));
  connect(mZoomOut, SIGNAL(pressed()), this, SLOT(zoomOutPressed()));
  connect(mZoomOut, SIGNAL(released()), this, SLOT(zoomOutReleased()));
  mZoomOut->setFocusPolicy(QWidget::NoFocus);

  QSizePolicy sp(QSizePolicy::Minimum, QSizePolicy::Fixed);
  if (mOrientation == Vertical)
    sp.transpose();
  setSizePolicy(sp);

  updateLabel();
}
