/***************************************************************************
 *   Copyright (C) 2006 by Andreas Krumnow   *
 *   andreas@krumnow.de   *
 *                                                                         *
 *   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.             *
 ***************************************************************************/

/* * version 0.6.27 */

/* * TODO:
    - freundlicheres Verhalten beim horizontalen skalieren:
        * Datenpufferung bis zur Desktop-Auflösung              (bei 'setNextValue()')
        * Werte bei Zeitintervall-Ă„nderung umtransformieren:    (bei 'drawValue()')
            langsam -> schnell : box(origZeit,0,(value * currentInterval / origInterval),(origInterval/currentInterval))
            schnell -> langsam : die nun zusammenfallenden Werte addieren.
    - Angleichen der Textpositionen in den Ecken

    DONE:
    
*/

#include <QtGui>

#include "plotterfield.h"



PlotterField::PlotterField(QWidget *parent)
    : QWidget(parent)
{
    curVal = maxValueInField = minVal = 0;
    aspectRatio = 1;
    scaleRange = 10;
    paneH = 20;
    paneW = 40;
    max_s_col = 408; // = 17 * 8 * 3
    valueListRx.append(0);
    valueListTx.append(0);
    needNewScaleFactor = true;
    cbxHLines = false;
    cbxVLines = false;
    lineCol = new QColor(0,255,0,255);
    plotCol1 = new QColor(255,0,0,255);
    plotCol2 = new QColor(255,128,128,255);
    bkCol = new QColor(200,200,250,255);
    unitsHorizontalLeft = "";
    unitsHorizontalRight = "";
    unitsVerticalBelow = "";
    unitsVerticalAbove = "";

    setPalette( QPalette(*bkCol));
    setBackgroundRole( QPalette::Window);
    setAutoFillBackground(true);
    resize(200, 100);
}

PlotterField::~ PlotterField()
{
    delete lineCol;
    delete bkCol;
    delete plotCol1;
    delete plotCol2;
}

void PlotterField::paintEvent(QPaintEvent *)
{
    paneH = this->height();
    paneW = this->width();
    aspectRatio = (double)(paneH) / scaleFactor(maxValueInField);    // retrieve the new scale-factor
    stift = new QPainter(this);
    drawValue(stift);
    if(cbxHLines) drawHline(stift);
    if(cbxVLines) drawVline(stift);
    delete stift;
    needNewScaleFactor = false;
}

void PlotterField::drawValue(QPainter *stift)
{
    int y1, y2, x, u, e;
    double rx, tx;

    // retrieve the scale-factor (scaleRange) only when needed
    if(needNewScaleFactor) 
        aspectRatio = (double)(paneH) / scaleFactor(maxValueInField);    //set the Value-drawing-Factor

    // draw the values of the List (with scaling-factor)
    e = valueListRx.size();           // count of value-samples in List
    x = paneW - e;                  // aktual X-Position
    u = paneH;                      // Koordinaten Y Ursprung unten
    for(int i = 0; i < e; i++)          // draw all the values
    {
        y1 = y2 = paneH;
        rx = valueListRx.at(i);
        tx = valueListTx.at(i);
        if(tx > 0)
        {
            y1 = paneH - (int(tx * aspectRatio));  // translate the origin value into pixels
            stift->setPen(*plotCol2);        // set current plot-color
            stift->drawLine(x,u,x,y1);
        }
        if(rx > 0)
        {
            y2 = y1 - (int(rx * aspectRatio));  // translate the origin value into pixels
            stift->setPen(*plotCol1);        // set current plot-color
            stift->drawLine(x,y1-1,x,y2-1);
        }
        x++;
    }
}

void PlotterField::drawHline(QPainter *stift)
{
    int x1, x2, anz=10;
    double v, step, scaleRangeTeiler;
    QString suffix;
    x1 = 0;
    x2 = paneW;
    v = maxValueInField;
    suffix = "";
    if(needNewScaleFactor) 
        scaleRange = (int)(scaleFactor(maxValueInField));

    // berechne schrittweite
    scaleRangeTeiler = (double)(scaleRange)/10;
    step = (double)(paneH)/10;
    if(step < 10)
    {
        anz = int(step);
        if(anz > 1)
        {
            step = (double)(paneH) / anz;
            scaleRangeTeiler = (double)(scaleRange/anz);
        }
        else
        {
            step = paneH;
            scaleRangeTeiler = (double)(scaleRange);
        }
    }
    // Groessenordnung
    if(scaleRangeTeiler > 1000)
    {
        scaleRangeTeiler = scaleRangeTeiler / 1000;
        suffix = " k";
        if(scaleRangeTeiler > 1000000)
        {
            scaleRangeTeiler = scaleRangeTeiler / 1000000;
            suffix = " M";
        }
    }
    if(scaleRange < 10)
    {
        scaleRangeTeiler = 1;
        anz = scaleRange;
        step = paneH;
        if(anz > 0) step = paneH / anz;
    }

    stift->setPen(*lineCol);

    // malen ...
    for(int y=1; y < anz; y++)
    {
        int h = (int)(paneH - ( step * y));
        stift->drawLine( x1,h,x2,h);
        if(cbxHtxt)
        {
            stift->drawText(2,h+10,QString::number( (double)(scaleRangeTeiler * y ),'F',0) + suffix);
        }
    }
    if(cbxHtxt)
    {
        stift->drawText(2,9,QString::number( (double)(scaleRangeTeiler * anz ),'F',0) 
                            + suffix + "  " + unitsHorizontalLeft);
        if(180 < height()) stift->drawText(2,paneH,"0/");
    }
}

void PlotterField::drawVline(QPainter *stift)
{
    stift->setPen(*lineCol);
    int y1, y2, yt, xx, genau=3;
    double step, timeMark;
    y1=0; y2=paneH, yt = paneH - 2;
    step = (double)(paneW)/10;
    if (step < 10) step=10;
    if(intervalTime >  10) genau = 2;
    if(intervalTime > 100) genau = 1;
    if(intervalTime > 200) ;
    if(intervalTime > 500) genau = 0;
    if(cbxVtxt)
    {
        stift->drawText((int)(paneW-(2*step/5)),yt," " + unitsVerticalBelow);
        if((intervalTime > 200) && (599 < width())) stift->drawText((int)(paneW-(2*step/5)),9, unitsVerticalAbove);
    }
    for (int i = 1; i < 10; i++)
    {
        xx = paneW - (int)(step * i);
        stift->drawLine( xx,y1,xx,y2);
        timeMark = ((double)(paneW - xx) * intervalTime / 1000);
        if(cbxVtxt)
        {
            stift->drawText(xx,yt,QString::number((double) timeMark, 'F',genau));
            if(intervalTime > 200)
            {
                int m = (int)(timeMark / 60);
                int s = ((int)(timeMark) % 60);
                QString f = ":";
                if(s < 10) f = ":0";
                stift->drawText(xx,9,QString::number(m) + f + QString::number(s));
            }
        }
    }
    if(cbxVtxt && (499 < width()))
    {
        timeMark = ((double)(paneW) * intervalTime / 1000); // (double) (paneW * intervalTime / 1000)
        stift->drawText((int)(2*step/5),yt,QString::number(timeMark, 'F',genau));
        if((intervalTime > 200) && (20 < height()))
        {
            int m = (int)(timeMark / 60);
            int s = ((int)(timeMark) % 60);
            QString f = ":";
            if(s < 10) f = ":0";
            stift->drawText(paneW/20,20,QString::number(m) + f + QString::number(s));
        }
    }

}

double PlotterField::scaleFactor(const double maxV)
{
    if(!needNewScaleFactor) return scaleRange;
    double dv, zehntel;
    dv = maxV;              // temp-var
    scaleRange = 1;
    while(dv > 1)
    {
        dv = dv / 10;
        scaleRange *= 10;
    }
    dv = maxV;
    zehntel = scaleRange / 10;
    int i = 0;
    while(dv > 0)
    {
        dv -= zehntel;
        i++;
    }
    scaleRange = (int)(i * zehntel);
    needNewScaleFactor = false;
    return scaleRange;
}

void PlotterField::setNextValue(const double rxVal, const double txVal)
{
    curVal = rxVal + txVal;
    ixMaxVal--;
    if(curVal > maxValueInField)
    {
        maxValueInField = curVal;
        ixMaxVal = valueListRx.size();
        needNewScaleFactor = true;
        aspectRatio = (double)(paneH) / scaleFactor(maxValueInField);    //set the Value-drawing-Factor
    }

    // Let the List Size not be larger than the drawing-pane
    if(valueListRx.size() >= paneW)
    {
        valueListRx.removeFirst();
        valueListTx.removeFirst();
    }

    valueListRx.append(rxVal);       // store the current value at end of List
    valueListTx.append(txVal);       // store the current value at end of List

    // if biggest value went out of visible pane ...
    if((ixMaxVal <= 0) && (paneW <= valueListRx.size())) 
    {
        // determine the biggest value left in list
        maxValueInField = ixMaxVal = 0;
        for(int i=0 ; i < valueListRx.size() ; ++i)
        {
            if(maxValueInField < (valueListRx.at(i) + valueListTx.at(i)))
            {
                maxValueInField = (valueListRx.at(i) + valueListTx.at(i));
                ixMaxVal = i;
            }
        }
        needNewScaleFactor = true;
        aspectRatio = (double)(paneH) / scaleFactor(maxValueInField);    //set the Value-drawing-Factor
    }

    update();
}

void PlotterField::setPaneColor(QRgb rgb)
{
    bkCol->setRgb(rgb);
    setPalette( QPalette(rgb));
    update();
}

void PlotterField::setPlotColor1( QRgb rgb )
{
    plotCol1->setRgb(rgb);
    plotCol1->setAlpha(255);
    update();
}

void PlotterField::setPlotColor2( QRgb rgb )
{
    plotCol2->setRgb(rgb);
    plotCol2->setAlpha(255);
    update();
}

void PlotterField::setLinesColor( QRgb rgb )
{
    lineCol->setRgb(rgb);
    lineCol->setAlpha(s_alpha);
    update();
}

void PlotterField::setLinesAlpha(int a)
{
    // security...
    if (a == s_alpha) return;
    if (a < 0) a = 0;
    if (a > 255) a = 255;

    s_alpha = a;
    lineCol->setAlpha(s_alpha);
    update();
}

void PlotterField::onCbxHLines(const int b)
{
    cbxHLines = bool(b); 
    update();
}

void PlotterField::onCbxHTxt(const int b)
{
    cbxHtxt = bool(b);
    update();
}

void PlotterField::onCbxVLines(const int b)
{
    cbxVLines = bool(b); 
    update();
}

void PlotterField::onCbxVTxt(const int b)
{
    cbxVtxt = bool(b);
    update();
}

void PlotterField::setNewScaleFactor()
{
    needNewScaleFactor = true;
}

void PlotterField::setIntervalTime(const int ms)
{
    intervalTime = ms;
}

void PlotterField::setUnitsHorizontalLeft(const QString & s)
{
    unitsHorizontalLeft = s;
}

void PlotterField::setUnitsHorizontalRight(const QString & s)
{
    unitsHorizontalRight = s;
}

void PlotterField::setUnitsVerticalBelow(const QString & s)
{
    unitsVerticalBelow = s;
}

void PlotterField::setUnitsVerticalAbove(const QString & s)
{
    unitsVerticalAbove = s;
}