/* -*- Mode: C++ -*-
   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.
 **
 **********************************************************************/
#include "KDChartEnums.h"
#include "KDChartPiePainter.h"
#include "KDChartParams.h"

#include <tqpainter.h>
#include <tqvaluestack.h>
#include <tqmessagebox.h>

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

#include <math.h>

/**
  \class KDChartPiePainter KDChartPiePainter.h

  \brief A chart painter implementation that can paint pie charts.

  PENDING(kalle) Write more documentation.
  */

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

  \param params the KDChartParams structure that defines the chart
  */
    KDChartPiePainter::KDChartPiePainter( KDChartParams* params ) :
KDChartPainter( 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.
  */
KDChartPiePainter::~KDChartPiePainter()
{
    // intentionally left blank
}

//static bool bHelp=true;

/**
  Paints the actual data area.

  \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 KDChartPiePainter::paintData( TQPainter* painter,
        KDChartTableDataBase* data,
        bool paint2nd,
        KDChartDataRegionList* regions )
{
//bHelp=true;
    uint chart = paint2nd ? 1 : 0;

    TQRect ourClipRect( _dataRect );
    ourClipRect.addCoords( -1,-1,1,1 );

    const TQWMatrix & world = painter->worldMatrix();
    ourClipRect =
#if COMPAT_TQT_VERSION >= 0x030000
        world.mapRect( ourClipRect );
#else
    world.map( ourClipRect );
#endif

    painter->setClipRect( ourClipRect );

    // find which dataset to paint
    uint dataset;
    if ( !params()->findDataset( KDChartParams::DataEntry
                ,
                dataset, dataset ) ) {
        return ; // nothing to draw
    }

    if ( dataset == KDCHART_ALL_DATASETS )
        // setChartSourceMode() has not been used (or all datasets have been
        // configured to be used); use the first dataset by
        // default
        dataset = 0;


    // Number of values: If -1, use all values, otherwise use the
    // specified number of values.
    if ( params()->numValues() != -1 )
        _numValues = params()->numValues();
    else
        _numValues = data->usedCols();

    _startAngles.resize( _numValues );
    _angleLens.resize( _numValues );

    // compute position
    _size = TQMIN( _dataRect.width(), _dataRect.height() ); // initial size
    // if the pies explode, we need to give them additional space =>
    // make the basic size smaller
    if ( params()->explode() ) {
        double doubleSize = ( double ) _size;
        doubleSize /= ( 1.0 + params()->explodeFactor() * 2 );
        _size = ( int ) doubleSize;
    }

    int sizeFor3DEffect = 0;
    if ( !params()->threeDPies() ) {

        int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 );
        int y = ( _dataRect.height() == _size ) ? 0 : ( ( _dataRect.height() - _size ) / 2 );
        _position = TQRect( x, y, _size, _size );
        _position.moveBy( _dataRect.left(), _dataRect.top() );
    } else {
        // threeD: width is the maximum possible width; height is 1/2 of that
        int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 );
        int height = _size;
        // make sure that the height plus the threeDheight is not more than the
        // available size
        if ( params()->threeDPieHeight() >= 0 ) {
            // positive pie height: absolute value
            sizeFor3DEffect = params()->threeDPieHeight();
            height = _size - sizeFor3DEffect;
        } else {
            // negative pie height: relative value
            sizeFor3DEffect = -( int ) ( ( ( double ) params()->threeDPieHeight() / 100.0 ) * ( double ) height );
            height = _size - sizeFor3DEffect;
        }
        int y = ( _dataRect.height() == height ) ? 0 : ( ( _dataRect.height() - height - sizeFor3DEffect ) / 2 );

        _position = TQRect( _dataRect.left() + x, _dataRect.top() + y,
                _size, height );
        //  _position.moveBy( _dataRect.left(), _dataRect.top() );
    }

    double sum = data->rowAbsSum( dataset );
    if( sum==0 ) //nothing to draw
        return;
    double sectorsPerValue = 5760.0 / sum; // 5760 == 16*360, number of sections in TQt circle

    int currentValue = params()->pieStart() * 16;
    bool atLeastOneValue = false; // guard against completely empty tables
    TQVariant vValY;
    for ( int value = 0; value < _numValues; value++ ) {
        // is there anything at all at this value
        /* see above for meaning of 16 */

        if( data->cellCoord( dataset, value, vValY, 1 ) &&
            TQVariant::Double == vValY.type() ){
            _startAngles[ value ] = currentValue;
            const double cellValue = fabs( vValY.toDouble() );
            _angleLens[ value ] = ( int ) floor( cellValue * sectorsPerValue + 0.5 );
            atLeastOneValue = true;
        } else { // mark as non-existent
            _angleLens[ value ] = 0;
            if ( value > 0 )
                _startAngles[ value ] = _startAngles[ value - 1 ];
            else
                _startAngles[ value ] = currentValue;
        }

        currentValue = _startAngles[ value ] + _angleLens[ value ];
    }

    // If there was no value at all, bail out, to avoid endless loops
    // later on (e.g. in findPieAt()).
    if( !atLeastOneValue )
        return;


    // Find the backmost pie which is at +90° and needs to be drawn
    // first
    int backmostpie = findPieAt( 90 * 16 );
    // Find the frontmost pie (at -90°/+270°) that should be drawn last
    int frontmostpie = findPieAt( 270 * 16 );
    // and put the backmost pie on the TODO stack to initialize it,
    // but only if it is not the frontmostpie
    TQValueStack < int > todostack;
    if ( backmostpie != frontmostpie )
        todostack.push( backmostpie );
    else {
        // Otherwise, try to find something else
        int leftOfCurrent = findLeftPie( backmostpie );
        if ( leftOfCurrent != frontmostpie ) {
            todostack.push( leftOfCurrent );
        } else {
            int rightOfCurrent = findRightPie( backmostpie );
            if ( rightOfCurrent != frontmostpie ) {
                todostack.push( rightOfCurrent );
            }
        }
        // If we get here, there was nothing else, and we will bail
        // out of the while loop below.
    }

    // The list with pies that have already been drawn

    TQValueList < int > donelist;

    // Draw pies until the todostack is empty or only the frontmost
    // pie is there
    while ( !todostack.isEmpty() &&
            !( ( todostack.count() == 1 ) &&
                ( ( todostack.top() == frontmostpie ) ) ) ) {
        // The while loop cannot be cancelled if frontmostpie is on
        // top of the stack, but this is also backmostpie (can happen
        // when one of the pies covers more than 1/2 of the circle. In
        // this case, we need to find something else to put on the
        // stack to get things going.

        // take one pie from the stack
        int currentpie = todostack.pop();
        // if this pie was already drawn, ignore it
        if ( donelist.find( currentpie ) != donelist.end() )
            continue;

        // If this pie is the frontmost pie, put it back, but at the
        // second position (otherwise, there would be an endless
        // loop). If this pie is the frontmost pie, there must be at
        // least one other pie, otherwise the loop would already have
        // been terminated by the loop condition.
        if ( currentpie == frontmostpie ) {
            Q_ASSERT( !todostack.isEmpty() );
            // TQValueStack::exchange() would be nice here...
            int secondpie = todostack.pop();
            if ( currentpie == secondpie )
                // no need to have the second pie twice on the stack,
                // forget about one instance and take the third
                // instead
                if ( todostack.isEmpty() )
                    break; // done anyway
                else
                    secondpie = todostack.pop();
            todostack.push( currentpie );
            todostack.push( secondpie );
            continue;
        }

        // When we get here, we can just draw the pie and proceed.
        drawOnePie( painter, data, dataset, currentpie, chart,
                sizeFor3DEffect,
                regions );

        // Mark the pie just drawn as done.
        donelist.append( currentpie );

        // Now take the pie to the left and to the right, check
        // whether these have not been painted already, and put them
        // on the stack.
        int leftOfCurrent = findLeftPie( currentpie );
        if ( donelist.find( leftOfCurrent ) == donelist.end() )
            todostack.push( leftOfCurrent );
        int rightOfCurrent = findRightPie( currentpie );
        if ( donelist.find( rightOfCurrent ) == donelist.end() )
            todostack.push( rightOfCurrent );
    }

    // now only the frontmost pie is left to draw
    drawOnePie( painter, data, dataset, frontmostpie, chart,
            sizeFor3DEffect,
            regions );
}


/**
  Internal method that draws one of the pies in a pie chart.

  \param painter the TQPainter to draw in
  \param dataset the dataset to draw the pie for
  \param pie the pie to draw
  \param the chart to draw the pie in
  \param regions a pointer to a list of regions that will be filled
  with regions representing the data segments, if not null
  */
void KDChartPiePainter::drawOnePie( TQPainter* painter,
        KDChartTableDataBase* /*data*/,
        uint dataset, uint pie, uint chart,
        uint threeDPieHeight,
        KDChartDataRegionList* regions )
{
    // Is there anything to draw at all?
    int angleLen = _angleLens[ ( int ) pie ];
    if ( angleLen ) {
        int startAngle = _startAngles[ ( int ) pie ];

        KDChartDataRegion* datReg = 0;
        TQRegion* region = 0;
        bool mustDeleteRegion = false;
        if ( regions ){
            region = new TQRegion();
            mustDeleteRegion = true;
        }

        TQRect drawPosition = _position;
        if ( params()->explode() ) {
            // need to compute a new position for each or some of the pie
            TQValueList<int> explodeList = params()->explodeValues();
            if( explodeList.count() == 0 || // nothing on list, explode all
                    explodeList.find( pie ) != explodeList.end() ) {
                double explodeAngle = ( startAngle + angleLen / 2 ) / 16;
                double explodeAngleRad = DEGTORAD( explodeAngle );
                double cosAngle = cos( explodeAngleRad );
                double sinAngle = -sin( explodeAngleRad );

                // find the explode factor for this particular pie
                double explodeFactor = 0.0;
                TQMap<int,double> explodeFactors = params()->explodeFactors();
                if( !explodeFactors.contains( pie ) ) // not on factors list, use default
                    explodeFactor = params()->explodeFactor();
                else // on factors list, use segment-specific value
                    explodeFactor = explodeFactors[pie];

                double explodeX = explodeFactor * _size * cosAngle;
                double explodeY = explodeFactor * _size * sinAngle;
                drawPosition.moveBy( static_cast<int>( explodeX ), static_cast<int>( explodeY ) );
            } else
                drawPosition = _position;
        } else
            drawPosition = _position;

        // The 3D effect needs to be drawn first because it could
        // otherwise partly hide the pie itself.
        if ( params()->threeDPies() ) {
            draw3DEffect( painter, drawPosition, dataset, pie, chart,
                          threeDPieHeight,
                          params()->explode(), region );
        }

        painter->setBrush( params()->dataColor( pie ) );
        if ( angleLen == 5760 ) {
            // full circle, avoid nasty line in the middle
            painter->drawEllipse( drawPosition );
            if ( regions ) {
                TQPointArray hitregion;
                hitregion.makeEllipse( drawPosition.x(), drawPosition.y(),
                                       drawPosition.width(),
                                       drawPosition.height() );
                datReg = new KDChartDataRegion( region->unite( TQRegion( hitregion ) ),
                                                dataset,
                                                pie,
                                                chart );
                datReg->points[ KDChartEnums::PosCenter ]
                    = drawPosition.center();
                datReg->points[ KDChartEnums::PosCenterRight ]
                    = pointOnCircle( drawPosition,    0 );
                datReg->points[ KDChartEnums::PosTopRight ]
                    = pointOnCircle( drawPosition,  720 );
                datReg->points[ KDChartEnums::PosTopCenter ]
                    = pointOnCircle( drawPosition, 1440 );
                datReg->points[ KDChartEnums::PosTopLeft ]
                    = pointOnCircle( drawPosition, 2160 );
                datReg->points[ KDChartEnums::PosCenterLeft ]
                    = pointOnCircle( drawPosition, 2880 );
                datReg->points[ KDChartEnums::PosBottomLeft ]
                    = pointOnCircle( drawPosition, 3600 );
                datReg->points[ KDChartEnums::PosBottomCenter ]
                    = pointOnCircle( drawPosition, 4320 );
                datReg->points[ KDChartEnums::PosBottomRight ]
                    = pointOnCircle( drawPosition, 5040 );
                datReg->startAngle = 2880;
                datReg->angleLen   = 5760;
                regions->append( datReg );
            }
        } else {
            // draw the top of this piece
            // Start with getting the points for the arc.
            const int arcPoints = angleLen;
            TQPointArray collect(arcPoints+2);
            int i=0;
            for ( ; i<=angleLen; ++i){
                collect.setPoint(i, pointOnCircle( drawPosition, startAngle+i ));
            }
            // Adding the center point of the piece.
            collect.setPoint(i, drawPosition.center() );



            painter->drawPolygon( collect );

//if( bHelp ){
//              painter->drawPolyline( collect );
//bHelp=false;
//}



            if ( regions ) {
                TQPointArray hitregion;
                hitregion.makeArc( drawPosition.x(), drawPosition.y(),
                        drawPosition.width(),
                        drawPosition.height(),
                        ( int ) startAngle, ( int ) angleLen );
                hitregion.resize( hitregion.size() + 1 );
                hitregion.setPoint( hitregion.size() - 1,
                        drawPosition.center() );
                datReg = new KDChartDataRegion( region->unite( TQRegion( hitregion ) ),
                                                dataset,
                                                pie,
                                                chart );

                datReg->points[ KDChartEnums::PosTopLeft ]
                    = pointOnCircle( drawPosition, startAngle + angleLen );
                datReg->points[ KDChartEnums::PosTopCenter ]
                    = pointOnCircle( drawPosition, startAngle + angleLen / 2 );
                datReg->points[ KDChartEnums::PosTopRight ]
                    = pointOnCircle( drawPosition, startAngle );

                datReg->points[   KDChartEnums::PosBottomLeft   ] = drawPosition.center();
                datReg->points[   KDChartEnums::PosBottomCenter ]
                    = datReg->points[ KDChartEnums::PosBottomLeft   ];
                datReg->points[   KDChartEnums::PosBottomRight  ]
                    = datReg->points[ KDChartEnums::PosBottomLeft   ];

                datReg->points[ KDChartEnums::PosCenterLeft ]
                    = TQPoint( (   datReg->points[ KDChartEnums::PosTopLeft      ].x()
                                + datReg->points[ KDChartEnums::PosBottomLeft   ].x() ) / 2,
                            (   datReg->points[ KDChartEnums::PosTopLeft      ].y()
                                + datReg->points[ KDChartEnums::PosBottomLeft   ].y() ) / 2 );
                datReg->points[ KDChartEnums::PosCenter ]
                    = TQPoint( (   datReg->points[ KDChartEnums::PosTopCenter    ].x()
                                + datReg->points[ KDChartEnums::PosBottomCenter ].x() ) / 2,
                            (   datReg->points[ KDChartEnums::PosTopCenter    ].y()
                                + datReg->points[ KDChartEnums::PosBottomCenter ].y() ) / 2 );
                datReg->points[ KDChartEnums::PosCenterRight ]
                    = TQPoint( (   datReg->points[ KDChartEnums::PosTopRight     ].x()
                                + datReg->points[ KDChartEnums::PosBottomRight  ].x() ) / 2,
                            (   datReg->points[ KDChartEnums::PosTopRight     ].y()
                                + datReg->points[ KDChartEnums::PosBottomRight  ].y() ) / 2 );

                datReg->startAngle = startAngle;
                datReg->angleLen   = angleLen;
                regions->append( datReg );
            }
        }
        if( mustDeleteRegion )
            delete region;
    }
}


/**
  Internal method that draws the shadow creating the 3D effect of a pie

  \param painter the TQPainter to draw in
  \param rect the position to draw at
  \param dataset the dataset to draw the pie for
  \param pie the pie to draw the shadow for
  \param the chart to draw the pie in
  \param threeDHeight the height of the shadow
  \param regions a pointer to a list of regions that will be filled
  with regions representing the data segments, if not null
  */
void KDChartPiePainter::draw3DEffect( TQPainter* painter,
        const TQRect& drawPosition,
        uint dataset, uint pie, uint chart,
        uint threeDHeight,
        bool /*explode*/,
        TQRegion* region )
{
    // NOTE: We cannot optimize away drawing some of the effects (even
    // when not exploding), because some of the pies might be left out
    // in future versions which would make some of the normally hidden
    // pies visible. Complex hidden-line algorithms would be much more
    // expensive than just drawing for nothing.

    // No need to save the brush, will be changed on return from this
    // method anyway.
    painter->setBrush( TQBrush( params()->dataShadow1Color( pie ),
                params()->shadowPattern() ) );

    int startAngle = _startAngles[ ( int ) pie ];
    int endAngle = startAngle + _angleLens[ ( int ) pie ];
    // Normalize angles
    while ( startAngle >= 5760 )
        startAngle -= 5760;
    while ( endAngle >= 5760 )
        endAngle -= 5760;
    Q_ASSERT( startAngle >= 0 && startAngle <= 360 * 16 );
    Q_ASSERT( endAngle >= 0 && endAngle <= 360 * 16 );

    //int centerY = drawPosition.center().y();

    if ( startAngle == endAngle ||
            startAngle == endAngle - 5760 ) { // full circle
        drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                threeDHeight, 2880, 5760, region );
    } else if ( startAngle <= 90 * 16 ) {
        if ( endAngle <= 90 * 16 ) {
            if ( startAngle <= endAngle ) {
                /// starts and ends in first quadrant, less than 1/4
                drawStraightEffectSegment( painter, drawPosition, dataset,
                                           pie, chart, threeDHeight, startAngle,
                                           region );
            } else {
                /// starts and ends in first quadrant, more than 3/4
                drawStraightEffectSegment( painter, drawPosition, dataset,
                                           pie, chart, threeDHeight, startAngle,
                                           region );
                drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                                      threeDHeight, 2880, 5760, region );
            }
        } else if ( endAngle <= 180 * 16 ) {
            /// starts in first quadrant, ends in second quadrant,
            /// less than 1/2
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                                       threeDHeight, startAngle, region );
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                                       threeDHeight, endAngle, region );
        } else if ( endAngle <= 270 * 16 ) {
            /// starts in first quadrant, ends in third quadrant
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle,
                    region );
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, endAngle, region );
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, 2880, endAngle, region );
        } else { // 270*16 < endAngle < 360*16
            /// starts in first quadrant, ends in fourth quadrant,
            /// more than 3/4
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, region );
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, 2880, endAngle, region );
        }
    } else if ( startAngle <= 180 * 16 ) {
        if ( endAngle <= 90 * 16 ) {
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, 2880, 5760, region );
        } else if ( endAngle <= 180 * 16 ) {
            if ( startAngle <= endAngle ) {
                /// starts in second quadrant, ends in second
                /// quadrant, less than 1/4
                drawStraightEffectSegment( painter, drawPosition, dataset,
                        pie, chart, threeDHeight, endAngle,
                        region );
            } else {
                /// starts in second quadrant, ends in second
                /// quadrant, more than 1/4
                drawStraightEffectSegment( painter, drawPosition, dataset,
                        pie, chart, threeDHeight, endAngle,
                        region );
                drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                        threeDHeight, 2880, 5760, region );
            }
        } else if ( endAngle <= 270 * 16 ) {
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, endAngle, region );
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, 2880, endAngle, region );
        } else { // 270*16 < endAngle < 360*16
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, 2880, endAngle, region );
        }
    } else if ( startAngle <= 270 * 16 ) {
        if ( endAngle <= 90 * 16 ) {
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, 5760, region );
        } else if ( endAngle <= 180 * 16 ) {
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, endAngle, region );
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, 5760, region );
        } else if ( endAngle <= 270 * 16 ) {
            if ( startAngle <= endAngle ) {
                /// starts in third quadrant, ends in third quadrant,
                /// less than 1/4
                drawStraightEffectSegment( painter, drawPosition, dataset,
                        pie, chart, threeDHeight, endAngle,
                        region );
                drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                        threeDHeight, startAngle, endAngle,
                        region );
            } else {
                /// starts in third quadrant, ends in third quadrant,
                /// more than 3/4
                drawStraightEffectSegment( painter, drawPosition, dataset,
                        pie, chart, threeDHeight, endAngle,
                        region );
                drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                        threeDHeight, 2880, endAngle,
                        region );
                drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                        threeDHeight, startAngle, 5760,
                        region );
            }
        } else { // 270*16 < endAngle < 360*16
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, endAngle,
                    region );
        }
    } else { // 270*16 < startAngle < 360*16
        if ( endAngle <= 90 * 16 ) {
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, region );
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, 5760, region );
        } else if ( endAngle <= 180 * 16 ) {
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, region );
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, endAngle, region );
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, 5760, region );
        } else if ( endAngle <= 270 * 16 ) {
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, region );
            drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, endAngle, region );
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, 2880, endAngle, region );
            drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                    threeDHeight, startAngle, 5760, region );
        } else { // 270*16 < endAngle < 360*16
            if ( startAngle <= endAngle ) {
                /// starts in fourth quadrant, ends in fourth
                /// quadrant, less than 1/4
                drawStraightEffectSegment( painter, drawPosition, dataset,
                        pie, chart, threeDHeight, startAngle,
                        region );
                drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                        threeDHeight, startAngle, endAngle,
                        region );
            } else {
                /// starts in fourth quadrant, ends in fourth
                /// quadrant, more than 3/4
                drawStraightEffectSegment( painter, drawPosition, dataset,
                        pie, chart, threeDHeight, startAngle,
                        region );
                drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                        threeDHeight, startAngle, 5760,
                        region );
                drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
                        threeDHeight, 2880, endAngle, region );
            }
        }
    }
}


/**
  Internal method that draws a segment with a straight 3D effect

  \param painter the TQPainter to draw in
  \param rect the position to draw at
  \param threeDHeight the height of the shadow
  \param angle the angle of the segment
  \param regions a pointer to a list of regions that will be filled
  with regions representing the data segments, if not null
  */
void KDChartPiePainter::drawStraightEffectSegment( TQPainter* painter,
        const TQRect& rect,
        uint /*dataset*/, uint /*pie*/, uint /*chart*/,
        int threeDHeight,
        int angle,
        TQRegion* region )
{
    TQPoint center = rect.center();
    TQPointArray points( 4 );
    TQPoint circlePoint = pointOnCircle( rect, angle );
    points.setPoint( 0, center );
    points.setPoint( 1, circlePoint );
    points.setPoint( 2, circlePoint.x(), circlePoint.y() + threeDHeight );
    points.setPoint( 3, center.x(),
            center.y() + threeDHeight );
    painter->drawPolygon( points );
    if ( region )
        *region += TQRegion( points );
}


/**
  Internal method that draws a segment with an arc 3D effect

  \param painter the TQPainter to draw in
  \param rect the position to draw at
  \param threeDHeight the height of the shadow
  \param startAngle the starting angle of the segment
  \param endAngle the ending angle of the segment
  \param regions a pointer to a list of regions that will be filled
  with regions representing the data segments, if not null
  */
void KDChartPiePainter::drawArcEffectSegment( TQPainter* painter,
        const TQRect& rect,
        uint /*dataset*/, uint /*pie*/, uint /*chart*/,
        int threeDHeight,
        int startAngle,
        int endAngle,
        TQRegion* region )
{
    // Start with getting the points for the inner arc.
    const int startA = TQMIN(startAngle, endAngle);
    const int endA   = TQMAX(startAngle, endAngle);
    const int arcPoints = endA-startA+1;
    TQPointArray collect(arcPoints * 2);
    for ( int angle=endA; angle>=startA; --angle){
        collect.setPoint(endA-angle, pointOnCircle( rect, angle ));
    }

    // Now copy these arcs again into the same array, but in the
    // opposite direction and moved down by the 3D height.
    for ( int i = arcPoints - 1; i >= 0; --i ) {
        TQPoint pointOnFirstArc = collect.point( i );
        pointOnFirstArc.setY( pointOnFirstArc.y() + threeDHeight );
        collect.setPoint( arcPoints * 2 - i - 1, pointOnFirstArc );
    }
    painter->drawPolygon( collect );
    if ( region )
        *region += TQRegion( collect );
}


/**
  Internal method that finds the pie that is located at the position
  specified by \c angle.

  \param angle the angle at which to search for a pie
  \return the number of the pie found
  */
uint KDChartPiePainter::findPieAt( int angle )
{
    for ( int i = 0; i < _numValues; i++ ) {
        int endseg = _startAngles[ i ] + _angleLens[ i ];
        if ( ( _startAngles[ i ] <= angle ) &&
                ( endseg >= angle ) )
            // found!
            return i;
    }

    // If we have not found it, try wrap around
    return findPieAt( angle + 5760 );
}


/**
  Internal method that finds the pie that is located to the left of
  the pie specified by \c pie.

  \param pie the pie to start the search from
  \return the number of the pie to the left of \c pie
  */
uint KDChartPiePainter::findLeftPie( uint pie )
{
    if ( pie == 0 )
        if ( _numValues > 1 )
            return _numValues - 1;
        else
            return 0;
    else {
        return pie - 1;
    }
}


/**
  Internal method that finds the pie that is located to the right of
  the pie specified by \c pie.

  \param pie the pie to start the search from
  \return the number of the pie to the right of \c pie
  */
uint KDChartPiePainter::findRightPie( uint pie )
{
    int rightpie = pie + 1;
    if ( rightpie == _numValues )
        rightpie = 0;
    return rightpie;
}


/**
  This method is a specialization that returns a fallback legend text
  appropriate for pies that do not have more than one dataset

  This method is only used when automatic legends are used, because
  manual and first-column legends do not need fallback texts.

  \param uint dataset the dataset number for which to generate a
  fallback text
  \return the fallback text to use for describing the specified
  dataset in the legend
  */
TQString KDChartPiePainter::fallbackLegendText( uint dataset ) const
{
    return TQObject::tr( "Item " ) + TQString::number( dataset + 1 );
}


/**
  This methods returns the number of elements to be shown in the
  legend in case fallback texts are used.

  This method is only used when automatic legends are used, because
  manual and first-column legends do not need fallback texts.

  \return the number of fallback texts to use
  */
uint KDChartPiePainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const
{
    return data->usedCols();
}
