/*
   KDChart - a multi-platform charting engine
*/

/****************************************************************************
 ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB.  All rights reserved.
 **
 ** This file is part of the KDChart library.
 **
 ** This file may be distributed and/or modified under the terms of the
 ** GNU General Public License version 2 as published by the Free Software
 ** Foundation and appearing in the file LICENSE.GPL included in the
 ** packaging of this file.
 **
 ** Licensees holding valid commercial KDChart licenses may use this file in
 ** accordance with the KDChart Commercial License Agreement provided with
 ** the Software.
 **
 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 **
 ** See http://www.klaralvdalens-datakonsult.se/?page=products for
 **   information about KDChart Commercial License Agreements.
 **
 ** Contact info@klaralvdalens-datakonsult.se if any conditions of this
 ** licensing are not clear to you.
 **
 **********************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "KDChartLinesPainter.h"
#include <KDChartParams.h>
#include <KDChartPropertySet.h>

#include <tqpainter.h>
#include <tqvaluevector.h>

#include <stdlib.h>

/**
   \class KDChartLinesPainter KDChartLinesPainter.h

   \brief A chart painter implementation that can paint line charts.
*/

/**
   Constructor. Sets up internal data structures as necessary.

   \param params the KDChartParams structure that defines the chart
   \param data the data that will be displayed as a chart
*/
KDChartLinesPainter::KDChartLinesPainter( KDChartParams* params ) :
    KDChartAxesPainter( params )
{
    // This constructor intentionally left blank so far; we cannot setup the
    // geometry yet since we do not know the size of the painter.
}


/**
   Destructor.
*/
KDChartLinesPainter::~KDChartLinesPainter()
{
    // intentionally left blank
}


/**
   Paints the actual data area. Data regions will only be added if \a
   regions is not 0.

   \param painter the TQPainter onto which the chart should be painted
   \param data the data that will be displayed as a chart
   \param paint2nd specifies whether the main chart or the additional chart is to be drawn now
   \param regions a pointer to a list of regions that will be filled
   with regions representing the data segments, if not null
*/
void KDChartLinesPainter::paintData( TQPainter* painter,
                                     KDChartTableDataBase* data,
                                     bool paint2nd,
                                     KDChartDataRegionList* regions )
{
    paintDataInternal( painter, data,
                       true,   // center points
                       params()->lineMarker() && !params()->threeDLines(),  // line markers yes/no, 3D lines have no markers
                       false,  // not an area
                       paint2nd,
                       regions );
}

/**
   \internal

   Does the actual painting of a line or an area chart and is provided
   with the appropriate parameters from \c
   KDChartLinesPainter::paintData() and
   KDChartAreaPainter::paintData().

   \param painter the TQPainter onto which the chart should be painted
   \param data the data that will be displayed as a chart
   \param centerThePoints if true, the value points will be centered in
   their segment (typically used for line charts), if false, the first
   value point will be on the ordinate, the second one at the right
   margin of the chart.
   \param isArea true if an area will be drawn, false for lines. Using
   this parameter should be avoided.
   \param paint2nd specifies whether the main chart or the additional chart is to be drawn now
   \param regions a pointer to a list of regions that will be filled
   with regions representing the data segments, if not null
*/
void KDChartLinesPainter::paintDataInternal( TQPainter* painter,
                                             KDChartTableDataBase* data,
                                             bool centerThePoints,
                                             bool drawMarkers,
                                             bool isArea,
                                             bool paint2nd,
                                             KDChartDataRegionList* regions )
{
    mCenterThePoints = centerThePoints;
    mDrawMarkers = drawMarkers;
    mIsArea = isArea;
    mChartType = paint2nd ? params()->additionalChartType()
        : params()->chartType();

    KDChartAxesPainter::paintData( painter, data, paint2nd, regions );
}


#define DEGTORAD(d) (d)*M_PI/180

/*!
  Projects a point in a space defined by its x, y, and z coordinates
  into a point onto a plane, given two rotation angles around the x
  resp. y axis.
*/
TQPoint KDChartLinesPainter::project( int x, int y, int z )
{

    double xrad = DEGTORAD( params()->threeDLineXRotation() );
    double yrad = DEGTORAD( params()->threeDLineYRotation() );
    TQPoint ret( static_cast<int>( x*cos( yrad ) + z * sin( yrad ) ),
                static_cast<int>( y*cos( xrad ) - z * sin( xrad ) ) );
    return ret;
}

bool KDChartLinesPainter::isNormalMode() const
{
    return KDChartParams::LineNormal == params()->lineChartSubType();
}

int KDChartLinesPainter::clipShiftUp( bool, double ) const
{
    return 0;
}


class MyPoint
{
public:
    MyPoint() : bValid( false ), bSkipThis( false ), cellValue( 0.0 ) {}
    void set( int x, int y, double value ) {
        bValid = true;
        p.setX( x );
        p.setY( y );
        cellValue = value;
    }
    void setSkipThis( bool skipThis ) {
        bSkipThis = skipThis;
    }
    TQPoint p;
    bool   bValid;
    bool   bSkipThis;
    double cellValue;
};


void KDChartLinesPainter::specificPaintData( TQPainter* painter,
                                             const TQRect& /*ourClipRect*/,
                                             KDChartTableDataBase* data,
                                             KDChartDataRegionList* regions,
                                             const KDChartAxisParams* ordinatePara,
                                             bool /*bNormalMode*/,
                                             uint chart,
                                             double logWidth,
                                             double /*areaWidthP1000*/,
                                             double logHeight,
                                             double axisYOffset,
                                             double minColumnValue,
                                             double maxColumnValue,
                                             double columnValueDistance,
                                             uint /*chartDatasetStart*/,
                                             uint /*chartDatasetEnd*/,
                                             uint datasetStart,
                                             uint datasetEnd )
{
    if( !data ) return;


    abscissaInfos ai;
    ai.bCenterThePoints = mCenterThePoints;
    calculateAbscissaInfos( *params(), *data,
                            datasetStart, datasetEnd,
                            logWidth, _dataRect,
                            ai );
    mCenterThePoints = ai.bCenterThePoints;

    bool bOrdinateDecreasing = ordinatePara
        ? ordinatePara->axisValuesDecreasing()
        : false;
    bool bOrdinateIsLogarithmic
        = ordinatePara
        ? (KDChartAxisParams::AxisCalcLogarithmic == ordinatePara->axisCalcMode())
        : false;

    //const double ordinatePixelsPerUnit = logHeight / columnValueDistance;
    const double ordinatePixelsPerUnit
        = (    ordinatePara
               && (0.0 != ordinatePara->trueAxisDeltaPixels())
               && (0.0 != ordinatePara->trueAxisDelta()))
        ? ordinatePara->trueAxisDeltaPixels() / ordinatePara->trueAxisDelta()
        : logHeight / columnValueDistance;;
    //tqDebug("ordinatePixelsPerUnit: %f",ordinatePixelsPerUnit);


    const bool showThreeDLines = !mIsArea && params()->threeDLines();

    enum { Normal, Stacked, Percent } mode = Normal;
    if (    (    ( mChartType                   == KDChartParams::Line )
                 && ( params()->lineChartSubType() == KDChartParams::LineNormal ) )
            || (    ( mChartType                   == KDChartParams::Area )
                    && ( params()->areaChartSubType() == KDChartParams::AreaNormal ) ) )
        mode = Normal;
    else if (    (    ( mChartType                   == KDChartParams::Line )
                      && ( params()->lineChartSubType() == KDChartParams::LineStacked ) )
                 || (    ( mChartType                   == KDChartParams::Area )
                         && ( params()->areaChartSubType() == KDChartParams::AreaStacked ) ) )
        mode = Stacked;
    else if (    (    ( mChartType                   == KDChartParams::Line )
                      && ( params()->lineChartSubType() == KDChartParams::LinePercent ) )
                 || (    ( mChartType                   == KDChartParams::Area )
                         && ( params()->areaChartSubType() == KDChartParams::AreaPercent ) ) )
        mode = Percent;
    else
        tqDebug( "Internal error in KDChartLinesPainter::paintDataInternal(): Unknown subtype" );


    TQMap < int, double > currentValueSums;
    if ( mode == Stacked || mode == Percent ) {
        // this array is only used for stacked and percent lines, no need
        // to waste time initializing it for normal types
        for ( int value = 0; value < ai.numValues; ++value )
            currentValueSums[ value ] = 0.0;
    }
    TQMap < int, double > totalValueSums;

    // compute the position of the 0 axis
    double zeroXAxisI;
    if ( mode == Percent ) {
        if ( minColumnValue == 0.0 )
            zeroXAxisI = logHeight + axisYOffset;
        else if( maxColumnValue == 0.0 )
            zeroXAxisI = _dataRect.y() + axisYOffset;
        else
            zeroXAxisI = logHeight / 2.0 + _dataRect.y();
    } else
        zeroXAxisI = ordinatePara->axisZeroLineStartY() - _dataRect.y();


    // compute how to shift of the points in case we want them in the
    // middle of their respective columns
    int xShift = mCenterThePoints ? static_cast < int > ( ai.pointDist * 0.5 ) : 0;


    // calculate all points' positions
    // ===============================
    int arrayNumDatasets = 0;
    int arrayNumValues   = ai.bAbscissaHasTrueAxisDtValues
        ? data->cols()
        : ai.numValues;
    int dataset;
    for( dataset = datasetEnd;
         ( dataset >= static_cast < int > ( datasetStart ) && dataset >= 0 );
         --dataset )
        ++arrayNumDatasets;
    TQValueVector<MyPoint> allPoints(
                              arrayNumDatasets * arrayNumValues );
                                    
    KDChartPropertySet curPropSet;
    int curPropSetId = KDChartPropertySet::UndefinedID;
                                    
    for( dataset = datasetEnd; ( dataset >= (int)datasetStart && dataset >= 0 ); --dataset ) {
                                        
        int prevPointX = -1;
        int prevPointY = -1;
                                        
        const KDChartParams::LineMarkerStyle
            defaultMarkerStyle = params()->lineMarkerStyle( dataset );
        const TQPen default2DPen(   params()->lineColor().isValid()
                                   ? params()->lineColor()
                                   : params()->dataColor( dataset ),
                                   params()->lineWidth(),
                                   params()->lineStyle( dataset ) );

        if( ai.bAbscissaHasTrueAxisDtValues )
            ai.numValues = data->cols();

        TQVariant vValY;
        TQVariant vValX;
        int cellPropID;
        for( int value = 0; value < ai.numValues; ++value ) {
            //if ( mode == Percent )
            //    valueTotal = data->colAbsSum( value );
            double valueTotal = 0.0; // Will only be used for Percent
            if( mode == Percent ) {
                valueTotal = 0.0;
                // iterate over datasets of this axis only:
                for ( uint dataset2  = datasetStart;
                      dataset2 <= datasetEnd;
                      ++dataset2 ) {
                    if( data->cellCoord( dataset2, value, vValY, 1 ) &&
                        TQVariant::Double == vValY.type() )
                        valueTotal += vValY.toDouble();
                }
            }

            if( data->cellContent( dataset, value, vValY, vValX, cellPropID ) &&
                TQVariant::Double == vValY.type() &&
                ( !ai.bCellsHaveSeveralCoordinates || TQVariant::Invalid != vValX.type() ) ){
                //tqDebug("a. cellPropID: %i",cellPropID);

                // calculate Ordinate axis value
                // -----------------------------
                double cellValue = vValY.toDouble();
                double drawValue = 0.0;
                // PENDING(kalle) This does not work for AreaPercent yet
                if ( mode == Stacked )
                    drawValue = ( cellValue + currentValueSums[ value ] ) * ordinatePixelsPerUnit;
                else if ( mode == Percent )
                    drawValue = ( ( cellValue + currentValueSums[ value ] ) / valueTotal ) * 100.0 * ordinatePixelsPerUnit;
                else {
                    // LineNormal or AreaNormal
                    if( bOrdinateIsLogarithmic ){
                        if( 0.0 < cellValue )
                            drawValue = log10( cellValue ) * ordinatePixelsPerUnit;
                        else
                            drawValue = -10250.0;
                        //tqDebug("\nlogarithmic calc  -  cellValue: %f   drawValue: %f",
                        //        cellValue, drawValue );
                    }else{
                        drawValue = cellValue * ordinatePixelsPerUnit * (bOrdinateDecreasing ? -1.0 : 1.0);
                        //tqDebug("\nlinear calc  -  cellValue: %f\n             -  drawValue: %f",
                        //        cellValue, drawValue );
                    }
                }


                // calculate Abscissa axis value
                // -----------------------------
                double xValue;
                bool skipMe = !calculateAbscissaAxisValue( vValX, ai, value,
                                                           xValue );


                // calculate and store the point and region / draw the marker
                // ----------------------------------------------------------
                if( !skipMe ){
                    // prevent the point from being toooo far
                    // below the bottom (or above the top, resp.)
                    // of the cliprect
                    double pY = TQMIN( zeroXAxisI - drawValue,
                                      (logHeight + axisYOffset) * 3 );
                    pY = TQMAX( pY, -(logHeight + axisYOffset) * 3 );
                    // specify the Point
                    int myPointX = static_cast < int > ( xValue ) + xShift;
                    int myPointY = static_cast < int > ( pY );

                    if( cellPropID == curPropSetId &&
                        myPointX == prevPointX &&
                        myPointY == prevPointY ){
                        allPoints[   static_cast < int > ( datasetEnd-dataset )
                                     * arrayNumValues + value ].setSkipThis( true );
                        skipMe = true;
                        //tqDebug("skipped");
                    }else{
                        // use typecast to make it compile on windows using qt232
                        allPoints[   static_cast < int > ( datasetEnd-dataset )
                                     * arrayNumValues + value ].set( myPointX, myPointY, cellValue );
                        //tqDebug("ok");
                    }
                    if( !skipMe ){
                        // --------------------------------------------------------
                        // determine any 'extra' properties assigned to this cell
                        // by traversing the property set chain (if necessary)
                        // --------------------------------------------------------
                        if( cellPropID != curPropSetId ){
                            //tqDebug("b. ( curPropSetId: %i )",curPropSetId);
                            //tqDebug("b. cellPropID: %i",cellPropID);
                            //tqDebug(curPropSet.name().latin1());
                            if( cellPropID != KDChartPropertySet::UndefinedID &&
                                params()->calculateProperties( cellPropID,
                                                               curPropSet ) ){
                                curPropSetId = cellPropID;
                                //tqDebug("c. curPropSetId: %i",curPropSetId);
                                //tqDebug(curPropSet.name().latin1());
                            }else{
                                curPropSetId = KDChartPropertySet::UndefinedID;
                            }
                        }
                        // make sure any extra horiz. and/or vert. lines and/or markers
                        // are drawn *before* the data lines and/or markers are painted
                        if( mChartType == KDChartParams::Line ){
                            if( curPropSetId != KDChartPropertySet::UndefinedID ){
                                drawExtraLinesAndMarkers(
                                                         curPropSet,
                                                         default2DPen,
                                                         defaultMarkerStyle,
                                                         myPointX, myPointY,
                                                         painter,
                                                         ai.abscissaPara,
                                                         ordinatePara,
                                                         logWidth/1000.0,
                                                         logHeight/1000.0,
                                                         false );
                            }
                        }
                        prevPointX = myPointX;
                        prevPointY = myPointY;
                    }
                }
                // calculate running sum for stacked and percent
                if ( mode == Stacked || mode == Percent ) {
                    if( cellValue == KDCHART_POS_INFINITE )
                        currentValueSums[ value ] = KDCHART_POS_INFINITE;
                    else if( currentValueSums[ value ] != KDCHART_POS_INFINITE )
                        currentValueSums[ value ] += cellValue;
                }
            }
        }
    }



    TQPointArray previousPoints; // no vector since only areas need it,
    // and these do not support 3d yet

    // Store some (dataset-independend) default values
    // to be used unless other properties
    // have been specified for the respective data cell:
    //
    const bool defaultDrawMarkers = mDrawMarkers;

    for ( dataset = datasetEnd; ( dataset >= (int)datasetStart && dataset >= 0 ); --dataset ) {

        // Store some (dataset-dependend) default values
        // to be used unless other properties
        // have been specified for the respective data cell:
        //
        const TQPen default2DPen(   params()->lineColor().isValid()
                                   ? params()->lineColor()
                                   : params()->dataColor( dataset ),
                                   params()->lineWidth(),
                                   params()->lineStyle( dataset ) );
        bool currentDrawMarkers = defaultDrawMarkers;
        const KDChartParams::LineMarkerStyle markerStyle = params()->lineMarkerStyle( dataset );

        // the +2 is for the areas (if any)
        TQPtrVector< TQPointArray > points( 2 );
        points.setAutoDelete( true );
        /* Pending Michel - we need to keep track of the 
         * non rotated points for 3D lines
         */
        TQPtrVector< TQPointArray > oripoints( 2 );
        oripoints.setAutoDelete( true );
        
        int i = 0;
        for( i = 0; i < 2; ++i ) {
            points.insert( i, new TQPointArray( ai.numValues + 2 ) );
            oripoints.insert( i, new TQPointArray( ai.numValues + 2 ) );
        }

        if( ai.bAbscissaHasTrueAxisDtValues )
            ai.numValues = data->cols();

        int point = 0;

        for ( int value = 0; value < ai.numValues; ++value ) {

            // determine and store marker properties assigned to this cell
            // -----------------------------------------------------------
            currentDrawMarkers = defaultDrawMarkers;
            int cellPropID;
            if( data->cellProp( dataset, value, cellPropID ) &&
                cellPropID != curPropSetId ){
                if( cellPropID != KDChartPropertySet::UndefinedID &&
                    params()->calculateProperties( cellPropID,
                                                   curPropSet ) )
                    curPropSetId = cellPropID;
                else
                    curPropSetId = KDChartPropertySet::UndefinedID;
            }
            if( curPropSetId != KDChartPropertySet::UndefinedID ){
                // we can safely call the following functions and ignore their
                // return values since they will touch the parameters' values
                // if the propSet *contains* corresponding own values only.
                int iDummy;
                curPropSet.hasOwnShowMarker( iDummy, currentDrawMarkers );
            }


            int iVec = static_cast < int > ( datasetEnd-dataset ) * arrayNumValues + value;
            if( allPoints[ iVec ].bValid && !allPoints[ iVec ].bSkipThis ){
                const MyPoint& mp = allPoints[iVec];

                
                //tqDebug("\np.x() %i        p.y() %i", p.x(), p.y() );
                // For 3D lines, we need two points (that lie
                // behind each other on the Z axis). For 2D lines and
                // areas, we need only one point.
                if( showThreeDLines ) {
                    points[0]->setPoint( point, project( mp.p.x(), mp.p.y(),
                                                         (datasetStart+dataset)*params()->threeDLineDepth() ) );
                    points[1]->setPoint( point, project( mp.p.x(), mp.p.y(),
                                                         (datasetStart+dataset + 1)*params()->threeDLineDepth() ) );
                    oripoints[0]->setPoint( point,  mp.p.x(), mp.p.y() );
                    oripoints[1]->setPoint( point,  mp.p.x() -  (datasetStart+dataset + 1)*params()->threeDLineDepth(), 
                                            mp.p.y() -  (datasetStart+dataset + 1)*params()->threeDLineDepth() );
		 
                } else
                    // 2D lines or areas
                    points[0]->setPoint( point, mp.p );
                ++point;

                int x = mp.p.x();
                int y = TQMAX(TQMIN(mp.p.y(),
                                  static_cast < int > (logHeight +axisYOffset)),
                             0);
                bool markerIsOutside = y != mp.p.y();
                // draw the marker and store the region
                if ( currentDrawMarkers ){
                    uint   theAlignment = TQt::AlignCenter;
                    bool   hasOwnSize = false;
                    int    theWidth  = 0;
                    int    theHeight = 0;
                    TQColor theColor(params()->dataColor( dataset ));
                    int    theStyle = markerStyle;
                    if( curPropSetId != KDChartPropertySet::UndefinedID ){
                        // we can safely call the following functions and ignore their
                        // return values since they will touch the parameters' values
                        // if the propSet *contains* corresponding own values only.
                        int iDummy;
                        curPropSet.hasOwnMarkerAlign( iDummy, theAlignment );
                        curPropSet.hasOwnMarkerColor( iDummy, theColor );
                        curPropSet.hasOwnMarkerStyle( iDummy, theStyle );
                        TQSize size(theWidth, theHeight);
                        hasOwnSize = curPropSet.hasOwnMarkerSize(iDummy, size);
                        if( hasOwnSize ){
                            theWidth  = size.width();
                            theHeight = size.height();
                        }
                    }

                    drawMarker( painter,
                                params(),
                                _areaWidthP1000, _areaHeightP1000,
                                _dataRect.x(),
                                _dataRect.y(),
                                markerIsOutside
                                ? KDChartParams::LineMarker1Pixel
                                : theStyle,
                                theColor,
                                TQPoint(x,y),
                                dataset, value, chart, regions,
                                hasOwnSize ? &theWidth  : 0,
                                hasOwnSize ? &theHeight : 0,
                                theAlignment );

                }
                // store the region
                else if( regions ) {
                    TQRect rect( TQPoint( x-1, y-1 ), TQPoint( x+1, y+1 ) );
                    rect.moveBy( _dataRect.x(), _dataRect.y() );
                    regions->append(
                                    new KDChartDataRegion(dataset, value, chart, rect) );
                }

            }
        }
        if ( point ) {
            bool bDrawLines = (0 != params()->lineWidth());
          
            if ( mIsArea ) {
                // first draw with the fill brush, no pen, with the
                // zero axis points or upper border points added for the first
                // dataset or with the previous points reversed for all other
                // datasets.
                painter->setPen( TQPen( TQt::NoPen ) );
                const TQBrush datasetBrush( params()->dataColor( dataset ), TQt::SolidPattern );
                painter->setBrush( datasetBrush );
                TQBrush currentBrush( datasetBrush );

                if ( mode == Normal || dataset == (int)datasetEnd ) {
                    /// first dataset (or any dataset in normal mode, where
                    /// the datasets overwrite each other)

                    // no 3d handling for areas yet
                    TQPoint lastPoint = points[0]->point( point - 1 );

                    // zeroXAxisI can be too far below the abscissa, but it's
                    // the only thing we have. Likewise can 0 be too far above
                    // the upper boundary, but again it's the only thing we
                    // have, at the rest is clipped anyway.
                    int yCoord;
                    if ( params()->areaLocation() == KDChartParams::AreaBelow ||
                         mode == Percent )
                        yCoord = static_cast<int>(zeroXAxisI);
                    else
                        yCoord = static_cast<int>(axisYOffset);

                    // old: draw the complete area in on go:
                    /*
                    // no 3d handling for areas yet
                    points[0]->setPoint( point, lastPoint.x(), yCoord );
                    point++;

                    TQPoint firstPoint = points[0]->point( 0 );
                    points[0]->setPoint( point, firstPoint.x(), yCoord );
                    point++;

                    painter->drawPolygon( *points[0], false, 0, point );
                    */

                    // new: draw individual area segments:
                    curPropSetId = KDChartPropertySet::UndefinedID;
                    for( int value = 0; value < point-1; ++value ) {

                        int cellPropID;
                        if( data->cellProp( dataset, value, cellPropID ) &&
                            cellPropID != curPropSetId ){

                            if( cellPropID != KDChartPropertySet::UndefinedID &&
                                params()->calculateProperties( cellPropID,
                                                               curPropSet ) ){
                                curPropSetId = cellPropID;
                            }else{
                                curPropSetId = KDChartPropertySet::UndefinedID;
                            }
                            // preset with default value
                            TQBrush theAreaBrush = datasetBrush;

                            if( curPropSetId != KDChartPropertySet::UndefinedID ){
                                // we can safely call the following functions and ignore their
                                // return values since they will touch the parameters' values
                                // if the propSet *contains* corresponding own values only.
                                int iDummy;
                                curPropSet.hasOwnAreaBrush( iDummy, theAreaBrush );
                            }
                            painter->setBrush( theAreaBrush );

                        }
                        TQPointArray segment( 4 );
                        segment.setPoint( 0, points[0]->point( value                 ) );
                        segment.setPoint( 1, points[0]->point( value+1               ) );
                        segment.setPoint( 2, points[0]->point( value+1 ).x(), yCoord );
                        segment.setPoint( 3, points[0]->point( value   ).x(), yCoord );                        
                        painter->drawPolygon( segment );
                    }

                    // old: draw the complete area in on go:
                    /*
                    // remove the last two points added
                    point -= 2;
                    */
                    //tqDebug("\n111");
                } //  if ( mode == Normal || dataset == (int)datasetEnd )
                else {
                    // don't mess around with the original array; we'll need
                    // that for the next time through.

                    //tqDebug("222");
                    // no 3d handling for areas yet
                    TQPointArray thisSection = points[0]->copy();

                    thisSection.resize( point + previousPoints.size() );
                    // append the previous array (there is guaranteed to be
                    // one because we are at least the second time through
                    // here) in reverse order
                    for ( unsigned int i = 0; i < previousPoints.size(); ++i ) {
                        thisSection.setPoint( point + i,
                                              previousPoints.point( previousPoints.size() - i - 1 ) );
                        //tqDebug("\nx: %i",previousPoints.point( previousPoints.size() - i - 1 ).x());
                        //tqDebug("y: %i",previousPoints.point( previousPoints.size() - i - 1 ).y());
                    }
                    painter->drawPolygon( thisSection );
                }
                // draw the line with no brush and outline color
                painter->setBrush( TQt::NoBrush );
                painter->setPen( TQPen( params()->outlineDataColor(),
                                       params()->outlineDataLineWidth() ) );
            } else {
                // line
                if( showThreeDLines ) {
                    // This is a 3D line:
                    // We draw the line with the data color brush
                    //                   and the outline data pen.
                    painter->setBrush( params()->dataColor( dataset ) );
                    painter->setPen( TQPen( params()->outlineDataColor(),
                                           params()->outlineDataLineWidth() ) );
                } else {
                    // This is a 2D line:
                    // We draw the line with the no brush
                    // and the data color if no special line color was specified.
                    painter->setBrush( TQt::NoBrush );
                    painter->setPen( default2DPen );
                }
            }

            // Neither draw the contour line if this is a pure Point chart
            // nor draw it for the last row of a percent area chart.
           
            if( bDrawLines &&
                ( (mode != Percent) || !mIsArea || (dataset != (int)datasetEnd) ) ){
                if( showThreeDLines ) {
		 
                    // A 3D line needs to be drawn piece-wise
                    for ( int value = 0; value < point-1; ++value ) {
                        //          if( data->cell( dataset, value ).hasValue() &&
                        //              data->cell( dataset, value+1 ).hasValue() ) {
                        //      tqDebug( "Draw a segment in dataset %d from %d to %d", dataset, value, value+1 );
                
                        //store the rotated points ( see project() )
                        TQPointArray rotatedSegment( 4 );                        
                        rotatedSegment.setPoint( 0, points[0]->point( value ));
                        rotatedSegment.setPoint( 1, points[0]->point( value+1 ) );
                        rotatedSegment.setPoint( 2, points[1]->point( value+1 ) );
                        rotatedSegment.setPoint( 3, points[1]->point( value ) );

                        //store the true points without rotation    
                        TQPointArray trueSegment( 4 );			 
                        trueSegment.setPoint( 0, oripoints[0]->point( value ));
                        trueSegment.setPoint( 1, oripoints[0]->point( value+1 ) );
                        trueSegment.setPoint( 2, oripoints[1]->point( value+1 ) );
                        trueSegment.setPoint( 3, oripoints[1]->point( value ) );

                        // calculate the rotated points position relative to each other
                        // we will then be able to keep the rotation ( see: project () )
                        // by reporting this position relative to the true segment line 
                        //left side pt3 and pt0 
                        int dx30 = rotatedSegment.point(3).x() - rotatedSegment.point(0).x();
                        int dy30 = rotatedSegment.point(3).y() - rotatedSegment.point(0).y(); 
                        //right side pt1 and pt2
                        int dx12 = rotatedSegment.point(2).x() - rotatedSegment.point(1).x();
                        int dy12 = rotatedSegment.point(2).y() - rotatedSegment.point(1).y();

                        // store and paint the "3D" segment
                        TQPointArray segment( 4 );
                        segment.setPoint( 0, trueSegment.point(0) );
                        segment.setPoint( 1, trueSegment.point(1) );
                        segment.setPoint( 2, trueSegment.point(1).x() + dx12, trueSegment.point(1).y() + dy12 );
                        segment.setPoint( 3, trueSegment.point(0).x() + dx30, trueSegment.point(0).y() + dy30);

		       
                        //PENDING Michel 3dlines drawing a segment with showThreeDLines                       
                        painter->drawPolygon( segment );
		       
                      			
                        //          } else
                        //              tqDebug( "Can't draw a segment in dataset %d from %d to %d", dataset, value, value+1 );
                    }
                } else {
                    TQPoint p1, p2;
                    // Note: If markers are drawn very near to each other
                    //       and tiny markers are used
                    //       we don't draw the connecting lines.
                    bool b4PMarkers = KDChartParams::LineMarker4Pixels == markerStyle;
                    bool bTinyMarkers =
                        KDChartParams::LineMarker1Pixel  == markerStyle || b4PMarkers;
                    curPropSetId = KDChartPropertySet::UndefinedID;
                    painter->setPen( default2DPen );
                    for ( int value = 0; value < point-1; ++value ) {
                        p1 = points[0]->point( value   );
                        p2 = points[0]->point( value+1 );

                        // Determine properties assigned to this cell
                        // and change the painter if necessarry:
                        currentDrawMarkers = defaultDrawMarkers;
                        int cellPropID;
                        if( data->cellProp( dataset, value, cellPropID ) &&
                            cellPropID != curPropSetId ){
                            if( cellPropID != KDChartPropertySet::UndefinedID &&
                                params()->calculateProperties( cellPropID,
                                                               curPropSet ) ){
                                curPropSetId = cellPropID;
                            }else{
                                curPropSetId = KDChartPropertySet::UndefinedID;
                            }
                            // preset with default values
                            int          theLineWidth = default2DPen.width();
                            TQColor       theLineColor = default2DPen.color();
                            TQt::PenStyle theLineStyle = default2DPen.style();
                            if( curPropSetId != KDChartPropertySet::UndefinedID ){
                                // we can safely call the following functions and ignore their
                                // return values since they will touch the parameters' values
                                // if the propSet *contains* corresponding own values only.
                                int iDummy;
                                curPropSet.hasOwnLineWidth ( iDummy, theLineWidth );
                                curPropSet.hasOwnLineColor ( iDummy, theLineColor );
                                curPropSet.hasOwnLineStyle ( iDummy, theLineStyle );
                                curPropSet.hasOwnShowMarker( iDummy, currentDrawMarkers );
                            }
                            painter->setPen( TQPen( theLineColor,
                                                   theLineWidth,
                                                   theLineStyle ) );
                        }

                        if( !currentDrawMarkers ){
                            //PENDING Michel: drawing a line - not currentMarkers
                            painter->drawLine( p1, p2 );
                        }else{
                            int dx = p2.x() - p1.x();
                            int dy = p2.y() - p1.y();
                            if( !bTinyMarkers || (abs(dx) > 4) || (abs(dy) > 4) ){
                                if( bTinyMarkers ) {
                                    double m  = !dx ? 100.0
                                        : !dy ? 0.01
                                        : ((double)dy / (double)dx);
                                    double am = fabs(m);
                                    int dxx;
                                    int dyy;
                                    if( 0.25 > am ){
                                        dxx = 3;
                                        dyy = 0;
                                    }else if( 0.67 > am ){
                                        dxx = 3;
                                        dyy = 1;
                                    }else if( 1.33 > am ){
                                        dxx = 2;
                                        dyy = 2;
                                    }else if( 4.0 > am ){
                                        dxx = 1;
                                        dyy = 3;
                                    }else{
                                        dxx = 0;
                                        dyy = 3;
                                    }
                                    if( 0 > dx )
                                        dxx *= -1;
                                    if( 0 > dy )
                                        dyy *= -1;
                                    if( b4PMarkers ){
                                        if( 0 < dx )
                                            ++p1.rx();
                                        else if( 0 > dx )
                                            ++p2.rx();
                                        if( 0 < dy )
                                            ++p1.ry();
                                        else if( 0 > dy )
                                            ++p2.ry();
                                    }
                                    p1.rx() += dxx; p1.ry() += dyy;
                                    p2.rx() -= dxx; p2.ry() -= dyy;
                                }
                                //PENDING Michel: drawing a line - currentMarkers
                                painter->drawLine( p1, p2 );
                            }
                        }
                    }
                }
            }
        }

        // Save point array for next way through (needed for e.g. stacked
        // areas), not for 3D currently
        points[0]->resize( point );
        previousPoints = points[0]->copy();
    }


    // Now draw any extra lines (and/or their markers, resp.) that
    // are to be printed IN FRONT of the normal lines:
    if( mChartType == KDChartParams::Line ){
        for( dataset = datasetEnd; ( dataset >= (int)datasetStart && dataset >= 0 ); --dataset ) {

            const KDChartParams::LineMarkerStyle
                defaultMarkerStyle = params()->lineMarkerStyle( dataset );
            const TQPen default2DPen(   params()->lineColor().isValid()
                                       ? params()->lineColor()
                                       : params()->dataColor( dataset ),
                                       params()->lineWidth(),
                                       params()->lineStyle( dataset ) );

            if( ai.bAbscissaHasTrueAxisDtValues )
                ai.numValues = data->cols();

            for ( int value = 0; value < ai.numValues; ++value ) {
                int iVec = static_cast < int > ( datasetEnd-dataset ) * arrayNumValues + value;
                if( allPoints[ iVec ].bValid ){
                    const MyPoint& mp = allPoints[iVec];
                    //tqDebug("\np.x() %i        p.y() %i", p.x(), p.y() );

                    // --------------------------------------------------------
                    // determine any 'extra' properties assigned to this cell
                    // by traversing the property set chain (if necessary)
                    // --------------------------------------------------------
                    int cellPropID;
                    if( data->cellProp( dataset, value, cellPropID ) &&
                        cellPropID != curPropSetId ){
                        if( cellPropID != KDChartPropertySet::UndefinedID &&
                            params()->calculateProperties( cellPropID,
                                                           curPropSet ) )
                            curPropSetId = cellPropID;
                        else
                            curPropSetId = KDChartPropertySet::UndefinedID;
                    }
                    if( curPropSetId != KDChartPropertySet::UndefinedID ){
                        drawExtraLinesAndMarkers(
                                                 curPropSet,
                                                 default2DPen,
                                                 defaultMarkerStyle,
                                                 mp.p.x(), mp.p.y(),
                                                 painter,
                                                 ai.abscissaPara,
                                                 ordinatePara,
                                                 logWidth/1000.0,
                                                 logHeight/1000.0,
                                                 true );
                    }
                }
            }
        }
    }
    //tqDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_NORMAL_DATA )->name().latin1());
    //tqDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_TRANSPARENT_DATA )->name().latin1());
    //tqDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_HORI_LINE )->name().latin1());
    //tqDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_VERT_LINE )->name().latin1());
    //tqDebug("--");
    }
