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

EffectPortFrame::EffectPortFrame(QString _name,LADSPAPort* _port,QWidget *parent, const char *name)
 : QCanvasView(parent, name)
{
    enableClipper(TRUE);
    zoomRatio=1;
    name=_name;
    port=_port;
    //c_height=100;
    linesCreated=FALSE;
    //setGeometry(((QVBox*)parent)->contentsRect());
    canvas=new ControllerCanvas(viewport());
    canvas->setBackgroundColor(lightGray);
    setResizePolicy(QScrollView::AutoOneFit);
    setCanvas(canvas);
    drawValueLines();
    setVScrollBarMode(AlwaysOff);
    setHScrollBarMode(AlwaysOff);
    drawing=FALSE;
    update_interval.reset();
    applyPolicy=NO_AUTO_APPLY;
    drawMode=DRAW_MODE_POINTS;
    drawingLine=new DrawingLine(canvas);
    polyLine=new PolyLine(canvas);
    polyLine->updateSize(canvas->size());
    spline=new SplineLine(canvas);
    spline->updateSize(canvas->size());
    spline->setMaxValue(port->getUpperBound());
    spline->setMinValue(port->getLowerBound());
}


EffectPortFrame::~EffectPortFrame()
{

}

void EffectPortFrame::raiseMe(){
    ((QWidgetStack*)parent())->raiseWidget(this);
    canvas->resize(visibleWidth(),visibleHeight());
    drawValueLines();
    polyLine->updateSize(canvas->size());
    spline->updateSize(canvas->size());
    update();
    emit(drawModeChanged(drawMode));
    //std::cout<<"i'm raised!: "<<port->getPortName()<<"\n";
}


/*!
    \fn EffectPortFrame::resizeEvent(QResizeEvent*)
 */
void EffectPortFrame::resizeEvent(QResizeEvent* ev)
{
    QCanvasView::resizeEvent(ev);
    canvas->resize((int)((double)port->getNbFrames()/(double)zoomRatio),
                    visibleHeight());
    drawValueLines();
    polyLine->updateSize(canvas->size());
    spline->updateSize(canvas->size());
    canvas->update();
    //updateGeometry();
}

void EffectPortFrame::drawValueLines(){
    float max=port->getUpperBound();
    float min=port->getLowerBound();
    float diff=max-min;

    if(!linesCreated){
        for (double i=0;i<port->getNbFrames();i+=1024){
                ValueLine* line=new ValueLine(canvas,(long)i,(float*)port->getVOTPointer((int)i/1024));
                int drawValue=(int)(visibleHeight()-((line->getValue()-min)*visibleHeight())/diff);
                line->setPen(darkGray);
                line->setPoints((int)(i/zoomRatio),drawValue,(int)(i/zoomRatio),visibleHeight());
                line->show();
        }
        linesCreated=TRUE;
    }else{
        QCanvasItemList l=canvas->allItems();
        for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
            if((*it)->rtti()==RTTI_VALUELINE){
                ValueLine* templine=(ValueLine*)(*it);
                if (templine->isVisible()){
                    int drawValue=
                            (int)(visibleHeight()-((templine->getValue()-min)*visibleHeight())/diff);
                    templine->setPoints(templine->getSamplePos()/zoomRatio,
                                                        drawValue,
                                                    templine->getSamplePos()/zoomRatio,
                                                    visibleHeight());
                }
            }
        }
    }
    canvas->update();
}

/*!
    \fn EffectPortFrame::repaintEvent(QPaintEvent*)
 */
void EffectPortFrame::paintEvent(QPaintEvent*)
{
    //canvas->resize(visibleWidth(),visibleHeight());
    //drawValueLines();
    /*
    canvas->resize((int)(port->getNbFrames()/zoomRatio),
                                            c_height);
    */
    //canvas->update();
}

void EffectPortFrame::closeEvent( QCloseEvent* ce ){

    //delete canvas;
    ce->accept();
}



/*!
    \fn EffectPortFrame::setZoomRatio(long)
 */
void EffectPortFrame::setZoomRatio(long _zr)
{
    zoomRatio=_zr;
    canvas->resize((int)((double)port->getNbFrames()/(double)zoomRatio),visibleHeight());
    cout<<"Effect frame canvas resized\n";
    drawValueLines();
    polyLine->updateSize(canvas->size());
    spline->updateSize(canvas->size());
    canvas->update();
}


/*!
    \fn EffectPortFrame::getZoomRatio
 */
long EffectPortFrame::getZoomRatio()
{
    return zoomRatio;
}


/*!
    \fn EffectPortFrame::getCanvas()
 */
ControllerCanvas* EffectPortFrame::getCanvas()
{
    return canvas;
}

void EffectPortFrame::contentsMousePressEvent( QMouseEvent * mouseEvent){
    QPoint pos=mouseEvent->pos();
    pos.setX(pos.x()<contentsX()?
            contentsX():pos.x()>contentsX()+width()?contentsX()+width():pos.x());
    if(drawMode==DRAW_MODE_LINES){
        if (mouseEvent->button()==LeftButton){
            drawingLine->setStartPoint(pos.x(),pos.y());
            drawingLine->setEndPoint(pos.x(),pos.y());
            drawingLine->setStartSample(pos.x()*zoomRatio);
            drawingLine->setEndSample(pos.x()*zoomRatio);
            drawingLine->setStartValue(yPosToVal(pos.y()));
            drawingLine->show();
            canvas->update();
            drawing=TRUE;
        }
    }else if(drawMode==DRAW_MODE_POLYLINES){
        if (mouseEvent->button()==LeftButton){
            polyLine->addPoint(pos,pos.x()*zoomRatio,yPosToVal(pos.y()));
            polyLine->show();
            canvas->update();
            drawing=TRUE;

        }
        if (mouseEvent->button()==RightButton){
            polyLine->removePoint(pos);
            canvas->update();
            drawing=TRUE;
        }
    }else if(drawMode==DRAW_MODE_SPLINES){
        if (mouseEvent->button()==LeftButton){
            spline->addPoint(pos,pos.x()*zoomRatio,yPosToVal(pos.y()));
            spline->show();
            canvas->update();
            drawing=TRUE;

        }
        if (mouseEvent->button()==RightButton){
            spline->removePoint(pos);
            canvas->update();
            drawing=TRUE;
        }
    }else if(drawMode==DRAW_MODE_POINTS){
        if (mouseEvent->button()==LeftButton||mouseEvent->button()==RightButton){
            update_interval.add(
                        updateValueAtPos(pos,
                                         mouseEvent->button()==LeftButton,
                                         mouseEvent->button()==RightButton));
            drawing=TRUE;
            if(applyPolicy==APPLY_ON_CHANGE){
                    emit(valueChanged(update_interval.start_sample,
                                                        update_interval.end_sample));
                update_interval.reset();
            }
        }
    }
}

void EffectPortFrame::contentsMouseMoveEvent( QMouseEvent * mouseEvent){
    QPoint pos=mouseEvent->pos();
    pos.setX(pos.x()<contentsX()?
            contentsX():pos.x()>contentsX()+width()?contentsX()+width():pos.x());
    pos.setY(pos.y()<0?
            0:pos.y()>contentsHeight()?contentsHeight():pos.y());
    if(drawMode==DRAW_MODE_LINES){
        if(drawing){
            drawingLine->setEndPoint(pos.x(),pos.y());
            drawingLine->setEndSample(pos.x()*zoomRatio);
            drawingLine->setEndValue(yPosToVal(pos.y()));
            canvas->update();
        }
    }else if(drawMode==DRAW_MODE_POLYLINES){
        polyLine->moveSelectedPoint(pos,pos.x()*zoomRatio,yPosToVal(pos.y()));
        canvas->update();
    }else if(drawMode==DRAW_MODE_SPLINES){
        spline->moveSelectedPoint(pos,pos.x()*zoomRatio,yPosToVal(pos.y()));
        canvas->update();
    }else if(drawMode==DRAW_MODE_POINTS){
        if(mouseEvent->state()==LeftButton ||mouseEvent->state()==RightButton){
            if(drawing){
                update_interval.add(
                        updateValueAtPos(pos,
                                         mouseEvent->state()==LeftButton,
                                         mouseEvent->state()==RightButton));
                if(applyPolicy==APPLY_ON_CHANGE){
                        emit(valueChanged(update_interval.start_sample,
                             update_interval.end_sample));
                        update_interval.reset();
                }
            }
        }
    }
}

void EffectPortFrame::contentsMouseReleaseEvent( QMouseEvent * mouseEvent){
    QPoint pos=mouseEvent->pos();
    pos.setX(pos.x()<contentsX()?
            contentsX():pos.x()>contentsX()+width()?contentsX()+width():pos.x());
    if(drawMode==DRAW_MODE_LINES){
        if(drawing){
            QCanvasItemList collisions=canvas->collisions(drawingLine->boundingRect());
            if(!collisions.isEmpty()){
                    QCanvasItemList::iterator it;
                    for ( it = collisions.begin(); it != collisions.end(); ++it ){
                            if((*it)->rtti()==RTTI_VALUELINE){
                                ValueLine* temp=(ValueLine*)(*it);
                                float controlerValue=drawingLine->getValueAtSample(temp->getSamplePos());
                                *(temp->getValuePointer())=controlerValue;
                                int y=drawingLine->getYAtX(temp->startPoint().x());
                                temp->setPoints(temp->startPoint().x(),y,
                                                                temp->endPoint().x(),visibleHeight());
                                if (temp->getSamplePos()<update_interval.start_sample) {
                                    update_interval.start_sample=temp->getSamplePos();
                                }
                                if (temp->getSamplePos()>update_interval.end_sample) {
                                        update_interval.end_sample=temp->getSamplePos();
                                }
                            }
                    }
            }
            drawingLine->hide();
            canvas->update();
            if(applyPolicy==APPLY_ON_RELEASE){
                if(update_interval.isValid()){
                    emit(valueChanged(update_interval.start_sample,
                                                            update_interval.end_sample));
                }
            }
            drawing=FALSE;
        }
    }else if(drawMode==DRAW_MODE_POLYLINES ||
             drawMode==DRAW_MODE_SPLINES){
        polyLine->deselectAll();
        spline->deselectAll();
        if(drawing && ((drawMode==DRAW_MODE_POLYLINES
           && polyLine->count()>0) ||
           (drawMode==DRAW_MODE_SPLINES
           && spline->count()>0))){

            QCanvasItemList collisions=drawMode==DRAW_MODE_POLYLINES?
                    canvas->collisions(polyLine->boundingRect()):
                    canvas->collisions(spline->boundingRect());
            if(!collisions.isEmpty()){
                QCanvasItemList::iterator it;
                for ( it = collisions.begin(); it != collisions.end(); ++it ){
                    if((*it)->rtti()==RTTI_VALUELINE){
                        ValueLine* temp=(ValueLine*)(*it);
                        int y=
                                drawMode==DRAW_MODE_POLYLINES?
                                    polyLine->getYAtX(temp->startPoint().x()):
                                    spline->getYAtX(temp->startPoint().x());
                        if(y>-1){
                            float controlerValue=
                                drawMode==DRAW_MODE_POLYLINES?
                                    polyLine->getValueAtSample(temp->getSamplePos()):
                                    spline->getValueAtSample(temp->getSamplePos());

                            *(temp->getValuePointer())=controlerValue;
                            temp->setPoints(temp->startPoint().x(),y,temp->endPoint().x(),visibleHeight());
                            if (temp->getSamplePos()<update_interval.start_sample) {
                                update_interval.start_sample=temp->getSamplePos();
                            }
                            if (temp->getSamplePos()>update_interval.end_sample) {
                                update_interval.end_sample=temp->getSamplePos();
                            }
                        }
                    }
                }
            }
            canvas->update();
            if(applyPolicy==APPLY_ON_RELEASE){
                if(update_interval.isValid()){
                    emit(valueChanged(update_interval.start_sample,
                         update_interval.end_sample));
                }
            }
        }
        drawing=FALSE;
        canvas->update();
    }else if(drawMode==DRAW_MODE_POINTS){
        if(drawing){
            drawing=FALSE;
            if(applyPolicy==APPLY_ON_RELEASE){
                if(update_interval.isValid()){
                    emit(valueChanged(update_interval.start_sample,
                                                           update_interval.end_sample));
                }
            }
        }
    }
    update_interval.reset();
}

void EffectPortFrame::contentsWheelEvent( QWheelEvent *){
}

EffectPortFrame::interval EffectPortFrame::updateValueAtPos(QPoint pos,bool leftButton,bool rightButton){
        float max=port->getUpperBound();
        float min=port->getLowerBound();
        float diff=max-min;
        int x=pos.x();
        if(x<0) x=0;
        int y=pos.y();
        if(y>visibleHeight()) y=visibleHeight();
        if(y<0) y=0;
        long intdown=LONG_MAX;
        long intup=LONG_MIN;
        QCanvasItemList colisions;
        interval returninterval;
        returninterval.start_sample=0;
        returninterval.end_sample=0;
        QRect rect(pos.x(),0,1,height());
        if(leftButton){
            colisions=canvas->collisions(rect);
        }else if(rightButton)
            colisions=canvas->allItems();
        if(!colisions.isEmpty()){
            float controlerValue=(((visibleHeight()-y)*diff)/visibleHeight())+min;
                //std::cout<<"cv: "<<controlerValue<<"\n";
                for (QCanvasItemList::Iterator it=colisions.begin(); it!=colisions.end(); ++it) {
                    if((*it)->rtti()==RTTI_VALUELINE){
                        ValueLine* temp=(ValueLine*)(*it);
                        *(temp->getValuePointer())=controlerValue;
                        temp->setPoints(temp->startPoint().x(),y,temp->endPoint().x(),visibleHeight());
                        if (temp->getSamplePos()<intdown) {intdown=temp->getSamplePos();}
                        if (temp->getSamplePos()>intup) {intup=temp->getSamplePos();}
                    }
                }
                canvas->update();
                returninterval.start_sample=intdown;
                returninterval.end_sample=intup;
        }
        return returninterval;
}


/*!
    \fn EffectPortFrame::setApplyPolicy()
 */
void EffectPortFrame::setApplyPolicy(int pol)
{
    applyPolicy=pol;
}

void EffectPortFrame::pointsToggled()
{
    if(isVisible()){
        drawMode=DRAW_MODE_POINTS;
        polyLine->clear();
        polyLine->hide();
        spline->clear();
        spline->hide();
        canvas->update();
    }
}

void EffectPortFrame::linesToggled()
{
    if(isVisible()){
        drawMode=DRAW_MODE_LINES;
        polyLine->clear();
        polyLine->hide();
        spline->clear();
        spline->hide();
        canvas->update();
    }
}

void EffectPortFrame::polylinesToggled()
{
    if(isVisible()){
        drawMode=DRAW_MODE_POLYLINES;
        spline->clear();
        spline->hide();
        if(polyLine->count()>0) polyLine->show();
        canvas->update();
    }
}

void EffectPortFrame::splinesToggled()
{
    if(isVisible()){
        drawMode=DRAW_MODE_SPLINES;
        polyLine->clear();
        polyLine->hide();
        if(spline->count()>0) spline->show();
        canvas->update();
    }
}

QSize EffectPortFrame::minimumSizeHint () const {
    return QSize(parentWidget()->width(),60);
}



/*!
    \fn EffectPortFrame::yPosToVal(int y)
 */
float EffectPortFrame::yPosToVal(int y)
{
        float max=port->getUpperBound();
        float min=port->getLowerBound();
        float diff=max-min;
        if(y>visibleHeight()) y=visibleHeight();
        if(y<0) y=0;
        return (((visibleHeight()-y)*diff)/visibleHeight())+min;
}


/*!
    \fn EffectPortFrame::getDrawMode()
 */
EffectPortFrame::DrawMode EffectPortFrame::getDrawMode()
{
    return drawMode;
}
