//=============================================================================
//  Awl
//  Audio Widget Library
//  $Id:$
//
//  Copyright (C) 1999-2011 by Werner Schweer and others
//
//  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; 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 <cmath>

#include "knob.h"

#include <QMouseEvent>
#include <QPainter>

namespace Awl {

//---------------------------------------------------------
//   Knob
///   this is the AwlKnob contructor
//---------------------------------------------------------

Knob::Knob(QWidget* parent)
   : AbstractSlider(parent)
      {
      _scaleSize = 270;
      _markSize  = 6;
      _border    = 2;
      points     = 0;
      }

//---------------------------------------------------------
//   Knob
//---------------------------------------------------------

Knob::~Knob()
      {
      if (points)
            delete points;
      }

//---------------------------------------------------------
//   setText
//---------------------------------------------------------

void Knob::setText(const QString& s)
      {
      if (s != _text) {
            _text = s;
            update();
            }
      }

//---------------------------------------------------------
//   setScaleSize
//!   set the scale size in degrees
//
//!   the scale size is the max moving angle measured
//!   in degrees
//---------------------------------------------------------

void Knob::setScaleSize(int val)
      {
      if (val != _scaleSize) {
            _scaleSize = val;
            update();
            }
      }

//---------------------------------------------------------
//   setMarkSize
//!   set size of the center marker
//---------------------------------------------------------

void Knob::setMarkSize(int val)
      {
      if (val != _markSize) {
            _markSize = val;
            update();
            }
      }

//---------------------------------------------------------
//   setBorder
//!   set border size
//---------------------------------------------------------

void Knob::setBorder(int val)
      {
      if (val != _border) {
            _border = val;
            update();
            }
      }

//---------------------------------------------------------
//   mousePressEvent
//---------------------------------------------------------

void Knob::mousePressEvent(QMouseEvent* ev)
      {
      startY = ev->y();
      emit sliderPressed(_id);
      if (_center) {
            QRect r(points->boundingRect().toRect());
            if (r.contains(ev->pos())) {
                  setValue(.0f);
                  valueChange();
                  update();
                  }
            }
      }

//---------------------------------------------------------
//   mouseReleaseEvent
//---------------------------------------------------------

void Knob::mouseReleaseEvent(QMouseEvent*)
      {
      emit sliderReleased(_id);
      }

//---------------------------------------------------------
//   mouseMoveEvent
//---------------------------------------------------------

void Knob::mouseMoveEvent(QMouseEvent* ev)
      {
      int y       = ev->y();
      double delta = (maxValue() - minValue()) / 100.0f;
      if (delta == 0)
            delta = 1;
      _value  += (startY - y) * delta;
      if (_value < minValue())
            _value = _minValue;
      else if (_value > maxValue())
            _value = _maxValue;
      startY    = y;
      valueChange();
      update();
      }

//---------------------------------------------------------
//   paintEvent
//---------------------------------------------------------

void Knob::paintEvent(QPaintEvent* ev)
      {
      QRect rr(ev->rect());
      QPainter p(this);
      p.setRenderHint(QPainter::Antialiasing, true);

      int markSize2  = _markSize/2;
      int restR      = 360      - _scaleSize;
      int w          = width()  - _scaleWidth - 2 * _border;
      int h          = height() - _scaleWidth/2 - 2 * _border;

   	int xoffset, yoffset;
      if (_center)
      	h -= _markSize;
	if (w > h) {
            yoffset = 0;
      	xoffset = (w - h) / 2;
            w = h;
            }
   	else {
            xoffset = 0;
      	// yoffset = (h - w) / 2;     // center
      	yoffset = h - w;              // top align
            h = w;
            }

      int x = xoffset + _scaleWidth / 2 + _border;
      int y = yoffset + _scaleWidth / 2 + _border + (_center ? _markSize+_scaleWidth/2 : 0);
      QRectF ar(x, y, w, h);

      QColor sc(isEnabled() ? _scaleColor : Qt::gray);
      QColor svc(isEnabled() ? _scaleValueColor : Qt::gray);

      //-----------------------------------------
      // draw arc
      //-----------------------------------------

      double dvalue = maxValue() - minValue();
      if (_center) {
            int size = _scaleSize * 8;
            if (_value >= 0) {
                  int offset = (restR-180) * 8;
                  int r1 = int (size * _value / maxValue());
                  int r2 = size - r1;
                  p.setPen(QPen(sc, _scaleWidth));
                  if (r2 > 1)
                        p.drawArc(ar, offset, r2);
                  if (size > 1)
                        p.drawArc(ar, 90*16, size);
                  if (r1 > 1) {
                        p.setPen(QPen(svc, _scaleWidth));
                        p.drawArc(ar, offset+r2, r1);
                        }
                  }
            else {
                  // int offset = (restR+180) * 8;
                  int r1 = int(size * _value / minValue());
                  int r2 = size - r1;

                  p.setPen(QPen(sc, _scaleWidth));
                  if (size > 1)
                        p.drawArc(ar, (restR-180)*8, size);
                  if (r2 > 1)
                        p.drawArc(ar, 90 * 16 + r1, r2);
                  if (r1 > 1) {
                        p.setPen(QPen(svc, _scaleWidth));
                        p.drawArc(ar, 90*16, r1);
                        }
                  }
            }
      else {
            int offset = (180-restR) * 8;
            int size   = _scaleSize * 16;
            int r1     = int(size * (_value - minValue()) / dvalue);
            int r2     = size - r1;
            if (r2 >= 1) {
                  p.setPen(QPen(sc, _scaleWidth));
                  p.drawArc(ar, -offset, r2);
                  }
            if (r1 >= 1) {
                  p.setPen(QPen(svc, _scaleWidth));
                  p.drawArc(ar, r2-offset, r1);
                  }
            }

      //-----------------------------------------
      // draw pointer
      //-----------------------------------------

      p.setPen(QPen(svc, _scaleWidth));
      double r1 = double(_scaleSize) * (_value-minValue()) / dvalue + 90.0
            + double(restR/2);
      r1     = r1 / 180.0 * M_PI;   // convert to radians
      int rd = w/2;
      int x1 = x + rd;
      int y1 = y + rd;
      int x2 = x1 + lrint(cos(r1) * double(rd));
      int y2 = y1 + lrint(sin(r1) * double(rd));
      p.drawLine(x1, y1, x2, y2);

      //-----------------------------------------
      // draw center mark
      //-----------------------------------------

      p.setPen(QPen(svc, 0));
      p.setBrush(svc);
      if (_center) {
            if (points)
                  delete points;
            qreal x = ar.width() / 2 + ar.x();
            qreal y = ar.y() - _markSize - _scaleWidth/2;
            points = new QPainterPath(QPointF(x - markSize2, y));
            points->lineTo(x + markSize2, y);
            points->lineTo(x, _markSize + y);
            points->closeSubpath();
            p.drawPath(*points);
            }

      //-----------------------------------------
      // draw text
      //-----------------------------------------

      if (!_text.isEmpty()) {
            p.drawText(rect(), Qt::AlignBottom | Qt::AlignHCenter, _text);
            }
      }
}

