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

PolyLine::PolyLine(QCanvas* canvas)
 : QCanvasPolygon(canvas)
{
    setY(0);
    setZ(10);
    maxValue=1;
    minValue=0;
    zoomRatio=1;
}


PolyLine::~PolyLine()
{
}

/*!
    \fn PolyLine::rtti()
 */
int PolyLine::rtti() const
{
    return RTTI_POLYLINE;
}

void PolyLine::drawShape ( QPainter & p ){
    QPen pen=p.pen();
    p.setPen(QPen(darkBlue,1));
    qHeapSort(sortablePoints);
    for (int i=0;i<sortablePoints.count();i++){
        if(sortablePoints[i]!=sortablePoints.last()){
            if(sortablePoints[i].selected() || sortablePoints[i+1].selected()){
                p.setPen(QPen(red,1));
            }else{
                p.setPen(QPen(darkBlue,1));
            }
            p.drawLine(sortablePoints[i].x(),sortablePoints[i].y(),sortablePoints[i+1].x(),sortablePoints[i+1].y());
        }
        p.setPen(QPen(darkBlue,1));
        if(sortablePoints[i].selected()){
            p.fillRect(sortablePoints[i].x()-3,sortablePoints[i].y()-3,7,7,QBrush(red));
        }else{
            p.fillRect(sortablePoints[i].x()-3,sortablePoints[i].y()-3,7,7,QBrush(cyan));
        }
        p.drawRect(sortablePoints[i].x()-3,sortablePoints[i].y()-3,7,7);
    }
    p.setPen(pen);
}


QPointArray PolyLine::areaPoints () const{
    QPointArray result(4);
    result.setPoint(0,QPoint(sortablePoints.first().x()-4,-1));
    result.setPoint(1,QPoint(sortablePoints.last().x()+4,-1));
    result.setPoint(2,QPoint(sortablePoints.last().x()+4,canvas()->height()+1));
    result.setPoint(3,QPoint(sortablePoints.first().x()-4,canvas()->height()+1));
    return result;

}


/*!
    \fn PolyLine::addPoint(QPoint p,long sample_pos,float value)
 */
void PolyLine::addPoint(QPoint p,long sample_pos,float value)
{
    SortablePoint sp(p);
    sp.setSamplePos(sample_pos);
    sp.setValue(value);
    if (!sortablePoints.contains(sp) && !containsSameX(p)){
        sortablePoints.append(sp);
        deselectAll();
    }
    else{
       for (int i=0;i<sortablePoints.count();i++){
           if(sortablePoints[i]==sp){sortablePoints[i].setSelected(TRUE);break;}
        }
    }
    qHeapSort(sortablePoints);
    setPoints(toPointArray());
    update();
}

/*!
    \fn PolyLine::removePoint(QPoint p)
 */
void PolyLine::removePoint(QPoint p)
{
    SortablePoint sp(p);
    if (sortablePoints.contains(sp)){
        if(sortablePoints.first()==sp || sortablePoints.last()==sp){
            canvas()->setAllChanged();
        }
        sortablePoints.remove(sp);
    }
    qHeapSort(sortablePoints);
    setPoints(toPointArray());
    deselectAll();
    canvas()->setAllChanged();
    update();
}

/*!
    \fn PolyLine::moveSelectedPoint(QPoint to, long sample_pos, float value)
 */
void PolyLine::moveSelectedPoint(QPoint to, long sample_pos, float value)
{
    for (int i=0;i<sortablePoints.count();i++){
        if(sortablePoints[i].selected()){
            if(sortablePoints.first()==sortablePoints[i] || sortablePoints.last()==sortablePoints[i]){
                canvas()->setAllChanged();
            }
            /** @todo : better choice **/
            if((sortablePoints.contains(SortablePoint(to))<=1) && containsSameX(to)<=1){
                //cout<<" containsSameX(to): "<<containsSameX(to)<<endl;
                //cout<<"sortablePoints.contains(SortablePoint(to)): "<<sortablePoints.contains(SortablePoint(to))<<endl;
                sortablePoints[i].setX(to.x());
                sortablePoints[i].setY(to.y());
                sortablePoints[i].setSamplePos(sample_pos);
                sortablePoints[i].setValue(value);
                break;
            }
        }
    }
    qHeapSort(sortablePoints);
    setPoints(toPointArray());
    update();
}


/*!
    \fn PolyLine::deselectAll()
 */
void PolyLine::deselectAll()
{
    for (int i=0;i<sortablePoints.count();i++){
        sortablePoints[i].setSelected(FALSE);
    }
    update();
}

/*!
    \fn PolyLine::getYAtX(int)
 */
int PolyLine::getYAtX(int x)
{
    for (int i=1;i<sortablePoints.count();i++){
        if(sortablePoints[i].x()>x){
            int x1=sortablePoints[i-1].x();
            int y1=sortablePoints[i-1].y();
            int x2=sortablePoints[i].x();
            int y2=sortablePoints[i].y();
            float a=float((y2-y1))/float((x2-x1));
            float b=y1-(a*float(x1));
            return a*x+b;
        }
    }
    return -1;
}

/*!
    \fn PolyLine::getValueAtSample(long sample)
 */
float PolyLine::getValueAtSample(long sample)
{
    for (int i=1;i<sortablePoints.count();i++){
        if(sortablePoints[i].getSamplePos()>sample){
            long x1=sortablePoints[i-1].getSamplePos();
            float y1=sortablePoints[i-1].getValue();
            long x2=sortablePoints[i].getSamplePos();
            float y2=sortablePoints[i].getValue();
            float a=float((y2-y1))/float((x2-x1));
            float b=y1-(a*float(x1));
            return a*float(sample)+b;
        }
    }
    return -1;
}

QRect PolyLine::boundingRect(){
    return QRect(QPoint(sortablePoints.first().x(),0),
                 QPoint(sortablePoints.last().x(),canvas()->height()));
}

/*!
    \fn PolyLine::count()
 */
int PolyLine::count()
{
    return sortablePoints.count();
}


/*!
    \fn PolyLine::clear()
 */
void PolyLine::clear()
{
    sortablePoints.clear();
    setPoints(toPointArray());
    canvas()->setAllChanged();
    update();
}


/*!
    \fn PolyLine::updateSize(QSize size)
 */
void PolyLine::updateSize(QSize size)
{
    //cout<<"in PolyLine::updateSize(): zoomRatio: "<<zoomRatio<<endl;
    for(int i=0;i<sortablePoints.count();i++){
        long sample_pos=sortablePoints[i].getSamplePos();
        float value=sortablePoints[i].getValue();
        sortablePoints[i].setX(sample_pos/zoomRatio);
        //cout<<"in PolyLine::updateSize(): point["<<i<<"]: s:"<<sample_pos<<" x: "<<sortablePoints[i].x()<<endl;
        sortablePoints[i].setY(valToYPos(value));
    }
    qHeapSort(sortablePoints);
    setX(sortablePoints[0].x());
    setPoints(toPointArray());
    canvas()->setAllChanged();
    update();
}


/*!
    \fn PolyLine::containsSameX(QPoint p)
 */
int PolyLine::containsSameX(QPoint p)
{
    int count=0;
    for (int i=0;i<sortablePoints.count();i++){
        if (sortablePoints[i].x()==p.x()) count++;
    }
    return count;
}


/*!
    \fn PolyLine::toPointArray()
 */
QPointArray PolyLine::toPointArray()
{
    QPointArray array(sortablePoints.count());
    for (int i=0;i<sortablePoints.count();i++){
        array.setPoint(i,QPoint(sortablePoints[i].x(),sortablePoints[i].y()));
    }
    return array;
}


/*!
    \fn PolyLine::getSortablePoints()
 */
QValueList <SortablePoint> PolyLine::getSortablePoints()
{
    return sortablePoints;
}


/*!
    \fn PolyLine::setSortablePoints(QValueList<SortablePoint>)
 */
void PolyLine::setSortablePoints(QValueList<SortablePoint> pts)
{
    sortablePoints=pts;
    qHeapSort(sortablePoints);
}


/*!
    \fn PolyLine::setMinValue(float m)
 */
void PolyLine::setMinValue(float m)
{
    minValue=m;
}


/*!
    \fn PolyLine::setMaxValue(float m)
 */
void PolyLine::setMaxValue(float m)
{
    maxValue=m;
}

/*!
    \fn PolyLine::valToYPos(float val)
 */
int PolyLine::valToYPos(float val)
{
    return int(MyMath::linear_regression(minValue,maxValue,canvas()->height(),0,val));
}


/*!
    \fn PolyLine::setZoomRatio(long zr)
 */
void PolyLine::setZoomRatio(long zr)
{
    zoomRatio=zr;
}


/*!
    \fn PolyLine::crop(long start, long end)
 */
void PolyLine::crop(long start, long end)
{
    //first move boundaries
    for (int i=0;i<sortablePoints.count();i++){
        if(sortablePoints[i].getSamplePos()<start && 
            i<sortablePoints.count()-1 && sortablePoints[i+1].getSamplePos()>start){
                        long next_sample_pos=sortablePoints[i+1].getSamplePos();
                        float next_value=sortablePoints[i+1].getValue();
                        long old_sample_pos=sortablePoints[i].getSamplePos();
                        float old_value=sortablePoints[i].getValue();
                        long new_sample_pos=start;
                        float new_value=MyMath::linear_regression(old_sample_pos,next_sample_pos,
                                                              old_value,next_value,new_sample_pos);
                        sortablePoints[i].setSamplePos(new_sample_pos);
                        sortablePoints[i].setValue(new_value);
        }
        if(sortablePoints[i].getSamplePos()>end && i>1 && sortablePoints[i-1].getSamplePos()<end){
                        long previous_sample_pos=sortablePoints[i-1].getSamplePos();
                        float previous_value=sortablePoints[i-1].getValue();
                        long old_sample_pos=sortablePoints[i].getSamplePos();
                        float old_value=sortablePoints[i].getValue();
                        long new_sample_pos=end;
                        float new_value=MyMath::linear_regression(previous_sample_pos,old_sample_pos,
                                                              previous_value,old_value,new_sample_pos);
                        sortablePoints[i].setSamplePos(new_sample_pos);
                        sortablePoints[i].setValue(new_value);
        }
    }
    qHeapSort(sortablePoints);
    //then remove before and after and change space
    typedef QValueList <SortablePoint> SortablePointList;
    SortablePointList::iterator it=sortablePoints.begin();
    while( it != sortablePoints.end()){
        if((*it).getSamplePos()<start){
                    it=sortablePoints.remove(it);
        }
        else if((*it).getSamplePos()>end){
                    it=sortablePoints.remove(it);
        }else{
            (*it).setSamplePos((*it).getSamplePos()-start);
            it++;
        }
    }
    updateSize(canvas()->size());
}
