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

#include "sonik_util.h"

#include <kglobalsettings.h>
#include <kdebug.h>

#include <qpainter.h>

#include <cmath>

namespace
{
  const uint32_t kTickWidth = 8;
  const uint32_t kTickHGap = 2;
  const uint32_t kTickVGap = 2;
  const uint32_t kHBorder = 4;
  const uint32_t kMaxTicks = 21;
}

using Sonik::ValueScale;

ValueScale::ValueScale(QWidget *parent, const char *name )
  : QWidget(parent,name),
    mMin(-1.0f),
    mMax(1.0f),
    mIsLog(false),
    mTickFormat("%.02f"),
    mBorder(0),
    mTickFont(KGlobalSettings::generalFont()),
    mNumTicks(0)
{
  setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding));

  mTickFont.setPointSize((mTickFont.pointSize() > 8) ? mTickFont.pointSize() - 2 : 6);

  recalc();
  calcTicks();
}

void ValueScale::setMin(float min)
{
  mMin = min;
  recalc();
  update();
}

void ValueScale::setMax(float max)
{
  mMax = max;
  recalc();
  update();
}

void ValueScale::setIsLog(bool isLog)
{
  mIsLog = isLog;
  recalc();
  update();
}

void ValueScale::setTickFormat(const QString& format)
{
  mTickFormat = format;
  recalc();
  update();
}

void ValueScale::setLabel(const QString& label)
{
  mLabel = label;
  recalc();
  update();
}

void ValueScale::setBorder(uint32_t border)
{
  mBorder = border;
  update();
}

QSize ValueScale::sizeHint() const
{
  return QSize(((mLabel != "") ? mLabelHeight + kHBorder : 0) + mTickSize.width(), 0);
}

void ValueScale::paintEvent(QPaintEvent* /*paintEvent*/)
{
  QPainter p(this);

  // draw them
  p.setFont(mTickFont);
  if (mNumTicks >= 3)
  {
    for (uint32_t i = 0; i < mNumTicks; i++)
    {
      int yPos = (int)(mBorder +
                       i * (height() - (mBorder * 2)) / (mNumTicks - 1));

      int textPos = yPos - (mTickSize.height() / 2);
      if (textPos < 0)
        textPos = 0;
      else if (textPos > height() - (int)mTickSize.height())
        textPos = height() - mTickSize.height();

      p.drawLine(width() - kTickWidth, yPos, width(), yPos);
      p.drawText(kHBorder,
                 textPos,
                 width() - kTickWidth - kTickHGap - kHBorder,
                 mTickSize.height(),
                 Qt::AlignRight | Qt::AlignVCenter,
                 formatTick(mMin + (mNumTicks - 1 - i) * mRange / (mNumTicks - 1)));
    }
  }

  // draw channel labels
  p.setFont(KGlobalSettings::generalFont());
  p.rotate(270);
  p.drawText(-height() + mBorder, mLabelAscent, mLabel);
}

void ValueScale::resizeEvent(QResizeEvent */*resizeEvent*/)
{
  calcTicks();
}

void ValueScale::fontChange(const QFont& oldFont)
{
  recalc();

  QWidget::fontChange(oldFont);
}

void ValueScale::recalc()
{
  QFontMetrics tickFm(mTickFont);
  QSize sz, maxSz(0, 0);

  mRange = mMax - mMin;

  for (uint32_t i = 0; i < kMaxTicks; ++i)
  {
    float tick = mMin + i * mRange / (kMaxTicks - 1);
    sz = tickFm.boundingRect(formatTick(tick)).size();
    if (sz.height() > maxSz.height())
      maxSz.setHeight(sz.height());
    if (sz.width() > maxSz.width())
      maxSz.setWidth(sz.width());
  }

  mTickSize  = QSize(kHBorder + maxSz.width() + kTickHGap + kTickWidth,
                     maxSz.height() + kTickVGap);

  QFontMetrics channelFm(KGlobalSettings::generalFont());
  mLabelAscent = channelFm.ascent();
  mLabelHeight = channelFm.height();

  updateGeometry();
}

QString ValueScale::formatTick(float tick)
{
  QString str;

  str.sprintf(mTickFormat, mIsLog ? valTodB(std::abs(tick)) : tick);

  return str;
}

void ValueScale::calcTicks()
{
  // find number of ticks that fit in widget

  if (mTickSize.height() > 0)
  {
    mNumTicks = (height() - (mBorder * 2)) / mTickSize.height();

    if (mNumTicks > kMaxTicks)
      mNumTicks = kMaxTicks;
    else if (mNumTicks <= 0)
      mNumTicks = 0;
    else if ((mNumTicks & 1) == 0)
      // force odd
      mNumTicks--;
  }
}
