/***************************************************************************
 *   Copyright (C) 2004 by Predrag Viceic                                  *
 *   viceic@net2000.ch                                            *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "displayenvelopewidget.h"
#include <qpixmap.h>
#include <qpainter.h>
#include "mymath.h"
#include "envelopedata.h"
#include <stdlib.h>

DisplayEnvelopeWidget::DisplayEnvelopeWidget(QWidget *parent, const char *name)
 : QLabel(parent, name,WNoAutoErase)
{
    envData=0;
    attackColor.setRgb(150,150,255);
    holdColor.setRgb(150,255,150);
    decayColor.setRgb(150,255,255);
    sustainColor.setRgb(255,255,150);
    releaseColor.setRgb(150,150,150);
    moving=NONE;
}


DisplayEnvelopeWidget::~DisplayEnvelopeWidget()
{
}

void DisplayEnvelopeWidget::drawContents ( QPainter * p){
    static QPixmap pixmap;
    if(envData){
        //cout<<envData->toString()<<endl;
        int sustainPos=int(MyMath::linear_regression(0,1,sustainRect.bottom(),
                                                                sustainRect.top(),envData->sustain));
        if(contentsRect().size().isValid()){
            QRect drawRect=contentsRect();
            drawRect.setTop(2);
            drawRect.setBottom(contentsRect().height()-2);
            //drawRect.setLeft(2);
            //drawRect.setRight(contentsRect().width()-2);

            pixmap.resize(contentsRect().size());
            QPainter p2(&pixmap);
            p2.fillRect(contentsRect(),black);

            p2.setViewport(drawRect);

            p2.fillRect(attackRect,attackColor.dark(200));
            p2.fillRect(holdRect,holdColor.dark(200));
            p2.fillRect(decayRect,decayColor.dark(200));
            p2.fillRect(sustainRect,sustainColor.dark(200));
            p2.fillRect(releaseRect,releaseColor.dark(200));


            p2.setPen(QPen(white,1,Qt::DotLine));
            p2.drawText(attackRect,Qt::AlignHCenter|Qt::AlignVCenter,"A");
            p2.drawText(holdRect,Qt::AlignHCenter|Qt::AlignVCenter,"H");
            p2.drawText(decayRect,Qt::AlignHCenter|Qt::AlignVCenter,"D");
            p2.drawText(sustainRect,Qt::AlignHCenter|Qt::AlignVCenter,"S");
            p2.drawText(releaseRect,Qt::AlignHCenter|Qt::AlignVCenter,"R");



            p2.drawLine(attackRect.right(),contentsRect().top(),
                                    attackRect.right(),contentsRect().bottom());
            if(envData->hold>0.001){
                p2.drawLine(holdRect.right(),contentsRect().top(),
                            holdRect.right(),contentsRect().bottom());
            }
            if(envData->decay>0.001){
                p2.drawLine(decayRect.right(),contentsRect().top(),
                            decayRect.right(),contentsRect().bottom());
            }
            p2.drawLine(sustainRect.right(),contentsRect().top(),
                        sustainRect.right(),contentsRect().bottom());

            p2.drawLine(contentsRect().x(),sustainPos,contentsRect().x()+contentsRect().width(),sustainPos);

            p2.setPen(QPen(QColor(150,150,255),5,Qt::SolidLine));

            drawBezierSegment(&p2,attackRect.bottomLeft(),attackRect.topRight(),QPoint());
            drawBezierSegment(&p2,holdRect.topLeft(),holdRect.topRight(),QPoint());
            drawBezierSegment(&p2,decayRect.topLeft(),QPoint(decayRect.topRight().x(),sustainPos),
                               QPoint());
            drawBezierSegment(&p2,QPoint(sustainRect.topLeft().x(),sustainPos),
                               QPoint(sustainRect.topRight().x(),sustainPos),QPoint());
            drawBezierSegment(&p2,QPoint(releaseRect.topLeft().x(),sustainPos),
                               releaseRect.bottomRight(),QPoint());



            p2.fillRect(attackPRect,QBrush(moving==ATTACK?red:attackColor));
            p2.setBrush(QBrush(moving==ATTACKC?red:attackColor));
            p2.setPen(moving==ATTACKC?red:attackColor);
            p2.drawEllipse(attackCRect);
            p2.fillRect(holdPRect,QBrush(moving==HOLD?red:holdColor));
            p2.fillRect(decayPRect,QBrush(moving==DECAY?red:decayColor));
            p2.setBrush(QBrush(moving==DECAYC?red:decayColor));
            p2.setPen(moving==DECAYC?red:decayColor);
            p2.drawEllipse(decayCRect);
            if(!sustainPRect.isEmpty()) p2.fillRect(sustainPRect,QBrush(moving==SUSTAIN?red:sustainColor));
            p2.fillRect(releasePRect,QBrush(moving==RELEASE?red:releaseColor));
            p2.setPen(moving==RELEASEC?red:releaseColor);
            p2.setBrush(QBrush(moving==RELEASEC?red:releaseColor));
            p2.drawEllipse(releaseCRect);

            p->drawPixmap(0,0,pixmap);
            //bitBlt(this,contentsRect().topLeft(),&pixmap);
        }
    }
}






void DisplayEnvelopeWidget::resizeEvent ( QResizeEvent * ev ){
    resize(ev->size());
    computeRects();
}


/*!
    \fn DisplayEnvelopeWidget::computeRects()
 */
void DisplayEnvelopeWidget::computeRects()
{


    int w=contentsRect().width();
    QRect drawRect=contentsRect();
    if(envData){
        //cout<<"Envelope: "<<envData->toString()<<"\n";
        int attackWidth=
                (int)MyMath::linear_regression(0,1,drawRect.left(),drawRect.right(),envData->attack);
        int attackCx=
                (int)MyMath::linear_regression(0,1,drawRect.left(),drawRect.right(),envData->attackC.x());
        int attackCy=
                (int)MyMath::linear_regression(0,1,drawRect.bottom(),drawRect.top(),envData->attackC.y());
        int holdWidth=
                (int)MyMath::linear_regression(0,1,drawRect.left(),drawRect.right(),envData->hold);
        int decayWidth=
                (int)MyMath::linear_regression(0,1,drawRect.left(),drawRect.right(),envData->decay);
        int decayCx=
                (int)MyMath::linear_regression(0,1,drawRect.left(),drawRect.right(),envData->decayC.x());
        int decayCy=
                (int)MyMath::linear_regression(0,1,drawRect.bottom(),drawRect.top(),envData->decayC.y());
        int releaseWidth=
                (int)MyMath::linear_regression(0,1,drawRect.left(),drawRect.right(),envData->release);
        int releaseCx=
                (int)MyMath::linear_regression(0,1,drawRect.left(),drawRect.right(),envData->releaseC.x());
        int releaseCy=
                (int)MyMath::linear_regression(0,1,drawRect.bottom(),drawRect.top(),envData->releaseC.y());
        int sustainPos=(int)MyMath::linear_regression(0,1,drawRect.bottom(),
                drawRect.top(),envData->sustain);
        int sustainWidth=int(w-attackWidth-holdWidth-decayWidth-releaseWidth);
        sustainWidth=sustainWidth<0?0:sustainWidth>w?w:sustainWidth;
        attackRect.setRect(0,0,attackWidth,drawRect.height());
        attackPRect.setRect(attackRect.right()-3,drawRect.y()+3,7,7);
        attackCRect.setRect(attackCx-3,attackCy-2,7,7);

        holdRect.setRect(attackRect.right(),0,holdWidth,drawRect.height());
        holdPRect.setRect(holdRect.right()-3,drawRect.height()/3,7,7);

        decayRect.setRect(holdRect.right(),0,decayWidth,drawRect.height());
        decayPRect.setRect(decayRect.right()-3,2*(drawRect.height()/3),7,7);
        decayCRect.setRect(decayCx-3,decayCy-2,7,7);
        sustainRect.setRect(decayRect.right(),0,sustainWidth,drawRect.height());
        if(sustainRect.isEmpty()){
            sustainPRect.setRect(0,0,0,0);
        }else{
            sustainPRect.setRect(sustainRect.left()+(sustainRect.right()-sustainRect.left())/2,
                             sustainPos-5,7,7);
        }
        releaseRect.setRect(sustainRect.right(),0,releaseWidth,drawRect.height());
        releasePRect.setRect(releaseRect.left()-3,drawRect.y()+drawRect.height()-3-7,7,7);
        releaseCRect.setRect(releaseCx-3,releaseCy-3,7,7);
    }
}


/*!
    \fn DisplayEnvelopeWidget::drawBezierSegment(QPainter p,QPoint start, QPoint end, QPoint control)
 */
void DisplayEnvelopeWidget::drawBezierSegment(QPainter* p,QPoint start,QPoint end, QPoint /*control*/)
{
    for (float i=start.x();i<end.x();i+=0.1){
        float xvalue=MyMath::linear_regression(p->window().left(),p->window().right(),0,1,i);
        float yvalue=envData->getEnvAt(xvalue,0.01);
        int x=(int)MyMath::linear_regression(0,1,p->window().left(),p->window().right(),xvalue);
        int y=(int)MyMath::linear_regression(0,1,p->window().bottom(),p->window().top(),yvalue);
        //cout<<"x: "<<x<<" y: "<<y<<endl;
        //p->drawPoint(QPoint(x,y));
        drawThickPoint(p,QPoint(x,y));
    }
}


/*!
    \fn DisplayEnvelopeWidget::drawThickPoint(QPainter* p,QPoint point)
 */
void DisplayEnvelopeWidget::drawThickPoint(QPainter* p,QPoint point)
{
    int x=point.x();
    int y=point.y();
    int width=p->pen().width();
    QColor tempPenColor=p->pen().color();
    QPen oldPen=p->pen();

    for (int i=int(-ceil(width/2));i<ceil(width/2);i++){
        int darkenFactor=(int)MyMath::linear_regression(0,width/2,1,80,abs(i)); //well, it's pretty..
        p->setPen(tempPenColor.light(100-darkenFactor));
        p->drawPoint(x,y+i);
    }
    p->setPen(oldPen);
}


/*!
    \fn DisplayEnvelopeWidget::setEnvData(EnvelopeData* ed)
 */
void DisplayEnvelopeWidget::setEnvData(EnvelopeData* ed)
{
    envData=ed;
    computeRects();
    update();
}

void DisplayEnvelopeWidget::mousePressEvent( QMouseEvent * ev){
    QRect drawRect=contentsRect();
    drawRect.setTop(2);
    drawRect.setBottom(contentsRect().height()-2);
    QWMatrix m;
    m.translate(-2,-2);
    m.scale(contentsRect().width()/drawRect.width(),contentsRect().height()/drawRect.height());
    QPoint pos=m.map(ev->pos());
    if(attackPRect.contains(pos)){
        moving=ATTACK;
    }
    else if(attackCRect.contains(pos)){
        moving=ATTACKC;
    }
    else if(holdPRect.contains(pos)){
        moving=HOLD;
    }
    else if(decayPRect.contains(pos)){
        moving=DECAY;
    }
    else if(decayCRect.contains(pos)){
        moving=DECAYC;
    }
    else if(sustainPRect.contains(pos)){
        moving=SUSTAIN;
    }
    else if(releasePRect.contains(pos)){
        moving=RELEASE;
    }
    else if(releaseCRect.contains(pos)){
        moving=RELEASEC;
    }
    else moving=NONE;
    update();
}
void DisplayEnvelopeWidget::mouseMoveEvent( QMouseEvent * ev){
    QRect drawRect=contentsRect();
    QPoint pos=ev->pos();
    float normx=0;
    float normy=0;
    if(moving!=NONE){
        drawRect.setTop(2);
        drawRect.setBottom(contentsRect().height()-2);
        drawRect.setLeft(2);
        drawRect.setRight(contentsRect().width()-2);
        pos.setX(pos.x()<0?0:pos.x()>width()?width():pos.x());
        pos.setY(pos.y()<0?0:pos.y()>height()?height():pos.y());
        normx=MyMath::linear_regression(0,width(),0,1,pos.x());
        normy=MyMath::linear_regression(0,height(),1,0,pos.y());
    }
    switch(moving){
        case NONE:return;
        case ATTACK:{
                envData->setAttack(normx);
                computeRects();update();break;
        }
        case ATTACKC:{
            envData->setAttackCx(normx);
            envData->setAttackCy(normy);
            computeRects();update();break;
        }
        case HOLD:{
            envData->setHold(normx);
            computeRects();update();break;
        }
        case DECAY:{
                envData->setDecay(normx);
                computeRects();update();break;
        }
        case DECAYC:{
            envData->setDecayCx(normx);
            envData->setDecayCy(normy);
            computeRects();update();break;
        }
        case RELEASE:{
            envData->setRelease(normx);
            computeRects();update();break;
        }
        case RELEASEC:{
            envData->setReleaseCx(normx);
            envData->setReleaseCy(normy);
            computeRects();update();break;
        }
        case SUSTAIN:{
            envData->setSustain(normy);
            computeRects();update();break;
        }
    }
}
void DisplayEnvelopeWidget::mouseReleaseEvent( QMouseEvent * /*ev*/){
    if(moving!=NONE){
        moving=NONE;
        emit(envelopeChanged(envData));
        update();
    }
}
