/*
   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 <tqpainter.h>
#include <tqbitmap.h>
#include <tqpixmap.h>
#include <math.h>
#include <limits.h>

#include <KDDrawText.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

void KDDrawText::drawRotatedText( TQPainter* painter,
        float  degrees,
        TQPoint anchor,
        const TQString& text,
        const TQFont* font,
        int align,
        bool showAnchor,
        const TQFontMetrics* fontMet,
        bool noFirstrotate,
        bool noBackrotate,
        KDDrawTextRegionAndTrueRect* infos,
        bool optimizeOutputForScreen )
{
    drawRotatedTxt( painter,
            optimizeOutputForScreen,
            degrees,
            anchor,
            text,
            font,
            align,
            showAnchor,
            INT_MAX,
            INT_MAX,
            fontMet,
            false,
            0 != infos,
            noFirstrotate,
            noBackrotate,
            infos );
}


KDDrawTextRegionAndTrueRect KDDrawText::measureRotatedText(
        TQPainter* painter,
        float  degrees,
        TQPoint anchor,
        const TQString& text,
        const TQFont* font,
        int align,
        const TQFontMetrics* fontMet,
        bool noFirstrotate,
        bool noBackrotate,
        int addPercentOfHeightToRegion )
{
    KDDrawTextRegionAndTrueRect infos;
    drawRotatedTxt( painter,
            false,
            degrees,
            anchor,
            text,
            font,
            align,
            false,
            INT_MAX,
            INT_MAX,
            fontMet,
            true,
            false,
            noFirstrotate,
            noBackrotate,
            &infos,
            addPercentOfHeightToRegion );
    return infos;
}


void KDDrawText::drawRotatedTxt( TQPainter* painter,
        bool optimizeOutputForScreen,
        float  degrees,
        TQPoint anchor,
        const TQString& text,
        const TQFont* font,
        int align,
        bool showAnchor,
        int txtWidth,
        int txtHeight,
        const TQFontMetrics* fontMet,
        bool calculateOnly,
        bool doNotCalculate,
        bool noFirstrotate,
        bool noBackrotate,
        KDDrawTextRegionAndTrueRect* infos,
        int addPercentOfHeightToRegion )
{
 
//    showAnchor=true;
  //tqDebug("\nanchor: "+ text + " / "+TQString::number(anchor.x())
  //         +" / "+TQString::number(anchor.y()));
    bool useInfos = doNotCalculate && infos;
    bool fontChanged = ( 0 != font );
    TQFont oldFont;
    if( fontChanged ) {
        oldFont = painter->font();
        painter->setFont( *font );
    }
    else
        font = static_cast<const TQFont*>(&painter->font());

    bool mustBackrotate = false;
    if( !optimizeOutputForScreen && !noFirstrotate ){
        painter->rotate( degrees );
        if( !noBackrotate )
            mustBackrotate = true;
    }

    TQPoint pos = useInfos ? infos->pos : painter->xFormDev( anchor );

    if( useInfos )
    {
        txtWidth  = infos->width;
        txtHeight = infos->height;
    }
    else
    {
        int newHeight=0;

        // a bug in the AIX 5.2 compiler means using (?:) syntax doesn't work here
        // therefor we do it the following way:
        TQFontMetrics* pFM=0;
        if( ! pFM ) {
            pFM = new TQFontMetrics( painter->fontMetrics() );
        } else {
            pFM = const_cast<TQFontMetrics*>(fontMet);
        }

        int nLF = text.contains('\n');
        if( INT_MAX == txtWidth ) {
            if( nLF ){
                int tw;
                txtWidth = 0;
                int i0  = 0;
                int iLF = text.find('\n');
                while( -1 != iLF ){
                    const TQRect r(pFM->boundingRect( text.mid(i0, iLF-i0) ));
                    tw = r.width()+ 2;
                    newHeight = r.height();
                    if( tw > txtWidth )
                        txtWidth = tw;
                    i0 = iLF+1;
                    iLF = text.find('\n', i0);
                }
                if( iLF < (int)text.length() ){
                    const TQRect r(pFM->boundingRect( text.mid( i0 ) ));
                    tw = r.width()+2;
                    newHeight = r.height();
                    if( tw > txtWidth )
                        txtWidth = tw;
                    i0 = iLF+1;
                }
            }else{
                const TQRect r(painter->boundingRect( 0,0,1,1, TQt::AlignAuto, text ));
                // correct width and height before painting with 2 unit to avoid truncating.
                // PENDING Michel - improve
                txtWidth  = r.width()+2;
                newHeight = r.height()+2;
            }
        }
        if( INT_MAX == txtWidth || INT_MAX == txtHeight ) {
            txtHeight = newHeight ? newHeight : pFM->height() * (1+nLF);
        }
        if( pFM != fontMet )
            delete pFM;
        if( infos ) {
            infos->pos    = pos;
            // PENDING infos
            infos->width = txtWidth;
            infos->height = txtHeight;
        }
    }
    if( showAnchor ) {
        int d = txtHeight/4;
        TQPen savePen = painter->pen();
        painter->setPen( TQColor( TQt::darkRed ) );
        painter->drawLine( pos.x(),   pos.y()-d,
                           pos.x(),   pos.y()+d );
        painter->drawLine( pos.x()-d, pos.y(),
                           pos.x()+d, pos.y() );
        painter->setPen( savePen );
    }
    int x = useInfos ? infos->x : pos.x();
    int y = useInfos ? infos->y : pos.y();
    //tqDebug("1.:     (x / y) :" +  text + " / "+TQString::number(x)
    //       +" / "+TQString::number(y));
    //tqDebug("2.:     (posx / posy) :" +  text );
    // tqDebug ( "%d", pos.x() ); tqDebug ( "%d", pos.y() );
    //tqDebug("3.:     (infosx / infosy) :" +  text + " / "+TQString::number(infos->x)
    //           +" / "+TQString::number(infos->y));

    if( !useInfos && !optimizeOutputForScreen ) {
        switch( align & ( TQt::AlignLeft | TQt::AlignRight | TQt::AlignHCenter ) ) {
              case TQt::AlignLeft:
                break;
            case TQt::AlignRight:
//tqDebug( TQPaintDeviceMetrics::logicalDpiX() );
                x -= txtWidth;
                break;
            case TQt::AlignHCenter:
                x -= txtWidth - txtWidth/2;
                break;
        }
        switch( align & ( TQt::AlignTop | TQt::AlignBottom | TQt::AlignVCenter ) ) {
            case TQt::AlignTop:
                break;
            case TQt::AlignBottom:
                y -= txtHeight;
                break;
            case TQt::AlignVCenter:
                y -= txtHeight/2;
                break;
        }
    }
    if( infos && !useInfos ) {
         painter->xForm( pos );
        infos->x = x - 4;
        infos->y = y - 4;
        //PENDING Michel updating info using x , y from pos 
        //tqDebug("4.:     (infosx / infosy) :" +  text + " / "+TQString::number(infos->x)
	//+" / "+TQString::number(infos->y));
        //tqDebug("5.:  (x / y) :" +  text + " / "+TQString::number(x)
	//   +" / "+TQString::number(y));
	//tqDebug("6.:  (anchorx /anchory) :" +  text + " / "+TQString::number(x)
	//   +" / "+TQString::number(y));
        TQRect rect( painter->boundingRect( x, y,
                    txtWidth, txtHeight,
                    TQt::AlignLeft + TQt::AlignTop,
                    text ) );
        //painter->fillRect (rect, TQt::blue );
        
        TQPoint topLeft(     painter->xForm( rect.topLeft()     ) );
        TQPoint topRight(    painter->xForm( rect.topRight()    ) );
        TQPoint bottomRight( painter->xForm( rect.bottomRight() ) );
        TQPoint bottomLeft(  painter->xForm( rect.bottomLeft()  ) );
      
        int additor = addPercentOfHeightToRegion * txtHeight / 100;
        TQPointArray points;
        points.setPoints( 4, topLeft.x()-additor,     topLeft.y()-additor,
                topRight.x()+additor,    topRight.y()-additor,
                bottomRight.x()+additor, bottomRight.y()+additor,
                bottomLeft.x()-additor,  bottomLeft.y()+additor );
        infos->region = TQRegion( points );
    }

    // When the TQt initialization bug is fixed the following scope
    // will be put into an "if( showAnchor )" entirely.
    {
        int d = txtHeight/4;
        TQPen savePen = painter->pen();
        if( showAnchor ) {
            painter->setPen( TQColor( TQt::blue ) );
            painter->drawLine( x,   y-d,
                               x,   y+d );
            painter->drawLine( x-d, y,
                               x+d, y );
            painter->setPen( TQColor( TQt::darkGreen ) );
            painter->drawRect(x,y,txtWidth,txtHeight);
            //painter->drawText( x, y-d, text);           
        
/*
        }else{
            // Working around a strange TQt bug: Rotated painter must be
            // initialized by drawing before text can be painted there.
            painter->setPen( TQColor( TQt::white ) );
            painter->drawLine( 30000,0,30001,0 );
*/
        }
        painter->setPen( savePen );
    }

    if( mustBackrotate && optimizeOutputForScreen ){
        painter->rotate( -degrees );
        mustBackrotate = false;
    }

    if( !calculateOnly ){
        //tqDebug("txtWidth: %i  txtHeight: %i", txtWidth, txtHeight);
        if( !optimizeOutputForScreen ){
/*
            painter->drawText( x, y,
                               txtWidth, txtHeight,
                               TQt::AlignLeft + TQt::AlignTop,
                               text );
*/
            painter->drawText( x, y,
                               txtWidth, txtHeight,
                               TQt::AlignLeft + TQt::AlignTop,
                               text );
/*
            painter->drawText( x, y,
                               text,
                               -1,
                               TQt::AlignRight + TQt::AlignTop );
*/
        }else{
            // new code (rotating the text ourselves for better quality on screens)
            TQPixmap pm( txtWidth+2, txtHeight+2, 1 );
            // note: When using colored axis labels it will be necessary
            //       to change this code and use a 256 color pixmap instead
            //       of a monochrome one.                 (khz, 2002/08/15)
            pm.fill(TQt::color0);
            TQPainter p;
            p.begin( &pm );
            if( showAnchor ){
                p.drawRect(0,0, txtWidth,txtHeight);
                p.drawLine(0,0, txtWidth,txtHeight);
                p.drawLine(0,txtHeight, txtWidth,0);
            }
            p.setFont(painter->font());

            p.drawText( 0, 0, txtWidth, txtHeight,
                        TQt::AlignLeft + TQt::AlignTop,
                        text );
/*
            p.drawText( 0,0,
                        text,
                       -1,
                        TQt::AlignLeft + TQt::AlignTop );
*/

            TQBitmap mask;
            mask = pm;
            pm.setMask( mask );
            TQWMatrix m;
            m.rotate( degrees );
            TQPixmap theRotatedPixmap = pm.xForm(m);

            // where are our four corner points now:
            double degreesRad = degrees;
            while( degreesRad > 360 )
                degreesRad -= 360;
            degreesRad *= M_PI / 180.0;
            double cosA = cos( degreesRad );
            double sinA = sin( degreesRad );
            TQPoint pTopLeft(  0,
                              0 );
            TQPoint pBotLeft(  static_cast < int > ( 0         * cosA  -  txtHeight * sinA ),
                              static_cast < int > ( txtHeight * cosA  +  0         * sinA  ) );
            TQPoint pTopRight( static_cast < int > ( txtWidth  * cosA  -  0         * sinA ),
                              static_cast < int > ( 0         * cosA  +  txtWidth  * sinA  ) );
            TQPoint pBotRight( static_cast < int > ( txtWidth  * cosA  -  txtHeight * sinA ),
                              static_cast < int > ( txtHeight * cosA  +  txtWidth  * sinA  ) );

            // make our four corner points relative
            // to the bounding rect of the rotated pixmap
            {
                TQPoint pDeltaTL( TQMIN(0, TQMIN(pBotLeft.x(), TQMIN(pTopRight.x(), pBotRight.x()))),
                                 TQMIN(0, TQMIN(pBotLeft.y(), TQMIN(pTopRight.y(), pBotRight.y()))) );
                pTopLeft  -= pDeltaTL;
                pBotLeft  -= pDeltaTL;
                pTopRight -= pDeltaTL;
                pBotRight -= pDeltaTL;
            }

            /*
            painter->setPen( TQColor( TQt::black ) );
            painter->drawLine( x-13,  y,    x+13,  y    );
            painter->drawLine( x,     y-13, x,     y+13 );
            painter->setPen( TQColor( TQt::blue ) );
            painter->drawLine( x+pTopLeft.x()-3,   y+pTopLeft.y(),   x+pTopLeft.x()+3,   y+pTopLeft.y()   );
            painter->drawLine( x+pTopLeft.x(),     y+pTopLeft.y()-3, x+pTopLeft.x(),     y+pTopLeft.y()+3 );
            painter->setPen( TQColor( TQt::red ) );
            painter->drawLine( x+pTopRight.x()-3,   y+pTopRight.y(),   x+pTopRight.x()+3,   y+pTopRight.y()   );
            painter->drawLine( x+pTopRight.x(),     y+pTopRight.y()-3, x+pTopRight.x(),     y+pTopRight.y()+3 );
            painter->setPen( TQColor( TQt::green ) );
            painter->drawLine( x+pBotLeft.x()-3,   y+pBotLeft.y(),   x+pBotLeft.x()+3,   y+pBotLeft.y()   );
            painter->drawLine( x+pBotLeft.x(),     y+pBotLeft.y()-3, x+pBotLeft.x(),     y+pBotLeft.y()+3 );
            painter->setPen( TQColor( TQt::yellow ) );
            painter->drawLine( x+pBotRight.x()-3,   y+pBotRight.y(),   x+pBotRight.x()+3,   y+pBotRight.y()   );
            painter->drawLine( x+pBotRight.x(),     y+pBotRight.y()-3, x+pBotRight.x(),     y+pBotRight.y()+3 );
            */

            // The horizontal and vertical alignment together define one of
            // NINE possible points: this point must be moved on the anchor.
            int hAlign = align & ( TQt::AlignLeft | TQt::AlignRight  | TQt::AlignHCenter );
            int vAlign = align & ( TQt::AlignTop  | TQt::AlignBottom | TQt::AlignVCenter );

            TQPoint pixPoint;
            switch( hAlign ) {
                case TQt::AlignLeft:
                    switch( vAlign ) {
                        case TQt::AlignTop:
                            pixPoint = pTopLeft;
                            break;
                        case TQt::AlignBottom:
                            pixPoint = pBotLeft;
                            break;
                        case TQt::AlignVCenter:
                        default:
                            pixPoint = TQPoint( (pTopLeft.x() + pBotLeft.x()) / 2,
                                               (pTopLeft.y() + pBotLeft.y()) / 2 );
                            break;
                    }
                    break;
                case TQt::AlignRight:
                    switch( vAlign ) {
                        case TQt::AlignTop:
                            pixPoint = pTopRight;
                            break;
                        case TQt::AlignBottom:
                            pixPoint = pBotRight;
                            break;
                        case TQt::AlignVCenter:
                        default:
                            pixPoint = TQPoint( (pTopRight.x() + pBotRight.x()) / 2,
                                               (pTopRight.y() + pBotRight.y()) / 2 );
                            break;
                    }
                    break;
                case TQt::AlignHCenter:
                default:
                    switch( vAlign ) {
                        case TQt::AlignTop:
                            pixPoint = TQPoint( (pTopLeft.x() + pTopRight.x()) / 2,
                                               (pTopLeft.y() + pTopRight.y()) / 2 );
                            break;
                        case TQt::AlignBottom:
                            pixPoint = TQPoint( (pBotLeft.x() + pBotRight.x()) / 2,
                                               (pBotLeft.y() + pBotRight.y()) / 2 );
                            break;
                        case TQt::AlignVCenter:
                        default:
                            pixPoint = TQPoint( (pTopLeft.x() + pBotRight.x()) / 2,
                                               (pTopLeft.y() + pBotRight.y()) / 2 );
                            break;
                    }
                    break;
            }
	    //tqDebug("2.:     (x / y) : "+TQString::number(x)
	    //             +" / "+TQString::number(y));
            painter->drawPixmap( TQPoint( x - pixPoint.x(),
                                         y - pixPoint.y() ),
                                 theRotatedPixmap );
            p.end();
        }
    }

    if( mustBackrotate )
        painter->rotate( -degrees );

    if( fontChanged )
        painter->setFont( oldFont );
}


