/* This file is part of the KDE project
   Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
   Copyright (C) 2004-2006 Thorsten Zachmann <zachmann@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "KPrBackground.h"
#include "KPrDocument.h"
#include <tdeapplication.h>
#include "KPrGradientCollection.h"
#include "KPrTextObject.h"
#include "KPrPage.h"
#include "KPrUtils.h"
#include "KPrBrush.h"

#include <tqpainter.h>
#include <tqpicture.h>
#include <tqfileinfo.h>

#include <KoRichText.h>
#include <KoTextObject.h>
// for getenv ()
#include <stdlib.h>
using namespace std;
#include <kdebug.h>
#include <tdeglobalsettings.h>
#include <KoStore.h>
#include <KoStoreDevice.h>
#include <KoGenStyles.h>
#include <KoXmlNS.h>

KPrBackGround::KPrBackGround( KPrPage *_page )
    // : footerHeight( 0 )
{
    backType = BT_COLOR;
    backView = BV_CENTER;
    backColor1 = TQt::white;
    backColor2 = TQt::white;
    bcType = BCT_PLAIN;
    unbalanced = false;
    xfactor = 100;
    yfactor = 100;

    gradientPixmap = 0L;
    m_page=_page;
}

void KPrBackGround::setBackPicture( const KoPicture& picture )
{
    if ( backType != BT_PICTURE )
        return;
    backPicture = pictureCollection()->insertPicture(picture);
}

void KPrBackGround::setBackPicture ( const KoPictureKey& key )
{
    if ( backType != BT_PICTURE )
        return;
    backPicture = pictureCollection()->findOrLoad(key.filename(), key.lastModified() );
}

void KPrBackGround::drawBackground( TQPainter *_painter, const KoTextZoomHandler* zoomHandler,
                                   const TQRect& _crect, bool _drawBorders )
{
    TQRect pageRect = zoomHandler->zoomRect( m_page->getPageRect() );
    TQRect crect = pageRect.intersect( _crect );
    if ( crect.isEmpty() )
        return;
    TQSize ext = pageRect.size();
    drawBackground( _painter, ext, crect, _drawBorders );
}

void KPrBackGround::drawBackground( TQPainter *_painter, const TQSize& ext, const TQRect& crect, bool _drawBorders )
{
    if ( m_page->displayBackground() )
    {
        _painter->save();
        switch ( backType )
        {
        case BT_COLOR:
            drawBackColor( _painter, ext, crect );
            break;
        case BT_CLIPART:
        case BT_PICTURE:
        {
            if ( backView == BV_CENTER )
                drawBackColor( _painter, ext, crect );
            drawBackPix( _painter, ext, crect );
            break;
        }
        default:
            break;
        }

        if ( _drawBorders )
            drawBorders( _painter, ext, crect );

        _painter->restore();
    }
}

void KPrBackGround::reload()
{
    if ( backType == BT_PICTURE || backType == BT_CLIPART )
        backPicture = pictureCollection()->insertPicture(backPicture);
    else
        backPicture.clear();
}


TQDomElement KPrBackGround::save( TQDomDocument &doc )
{
    TQString tag = m_page->masterPage() == 0 ? "MASTERPAGE" : "PAGE";
    TQDomElement page=doc.createElement( tag );
    TQDomElement element;

    element=doc.createElement("BACKMASTER");
    element.setAttribute( "useMasterBackground",  static_cast<int>(m_page->useMasterBackground() ) );
    element.setAttribute( "displayMasterPageObject", static_cast<int>( m_page->displayObjectFromMasterPage() ));
    element.setAttribute( "displayBackground", static_cast<int>( m_page->displayBackground() ));
    page.appendChild(element);
    if ( !m_page->useMasterBackground() )
    {

        if (backType!=BT_COLOR) {
            element=doc.createElement("BACKTYPE");
            element.setAttribute("value", static_cast<int>( backType ));
            page.appendChild(element);
        }

        if (backView!=BV_CENTER) {
            element=doc.createElement("BACKVIEW");
            element.setAttribute("value", static_cast<int>( backView ));
            page.appendChild(element);
        }

        if (backColor1!=TQt::white) {
            element=doc.createElement("BACKCOLOR1");
            element.setAttribute("color", backColor1.name());
            page.appendChild(element);
        }

        if (backColor2!=TQt::white) {
            element=doc.createElement("BACKCOLOR2");
            element.setAttribute("color", backColor2.name());
            page.appendChild(element);
        }

        if (bcType!=BCT_PLAIN) {
            element=doc.createElement("BCTYPE");
            element.setAttribute("value", static_cast<int>( bcType ));
            page.appendChild(element);
        }

        if (xfactor!=100 || yfactor!=100 || unbalanced) {
            element=doc.createElement("BGRADIENT");
            element.setAttribute("unbalanced", static_cast<int>( unbalanced ));
            element.setAttribute("xfactor", xfactor);
            element.setAttribute("yfactor", yfactor);
            page.appendChild(element);
        }

        if ( !backPicture.isNull() && ( ( backType == BT_PICTURE ) || ( backType == BT_CLIPART ) ) )
        {
            element = doc.createElement( "BACKPICTUREKEY" );

            backPicture.getKey().saveAttributes( element );
            page.appendChild( element );
        }
    }

    return page;
}


void KPrBackGround::saveOasisBackgroundPageStyle( KoGenStyle& stylepageauto, KoGenStyles& mainStyles )
{
    switch ( backType )
    {
        case BT_COLOR:
        {
            TQBrush qbrush( backColor1 );
            KPrBrush brush( qbrush, backColor1, backColor2, bcType,
                            bcType == BCT_PLAIN ? FT_BRUSH : FT_GRADIENT,
                            unbalanced, xfactor, yfactor );
            brush.saveOasisFillStyle( stylepageauto, mainStyles );
        } break;
        case BT_BRUSH:
        case BT_CLIPART:
        case BT_PICTURE:
        {
            stylepageauto.addProperty("draw:fill", "bitmap" );
            stylepageauto.addProperty("draw:fill-image-name", saveOasisPictureStyle( mainStyles ) );
            TQString repeat = "repeat";
            switch ( backView )
            {
                case BV_ZOOM:
                    repeat = "stretch";
                    break;
                case BV_CENTER:
                    repeat = "no-repeat";
                    break;
                case BV_TILED:
                    repeat = "repeat";
                    break;
            }
            stylepageauto.addProperty( "style:repeat", repeat );
        } break;
    }
}


TQString KPrBackGround::saveOasisPictureStyle( KoGenStyles& mainStyles )
{
    //<draw:fill-image draw:name="Pattern" xlink:href="Pictures/100000000000005E0000005E43C87AF2.png" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>
    KoGenStyle pictureStyle( KPrDocument::STYLE_PICTURE /*no family name*/ );
    pictureStyle.addAttribute( "xlink:show", "embed" );
    pictureStyle.addAttribute( "xlink:actuate", "onLoad" );
    pictureStyle.addAttribute( "xlink:type", "simple" );
    pictureStyle.addAttribute( "xlink:href", pictureCollection()->getOasisFileName(backPicture ) );
    return mainStyles.lookup( pictureStyle, "picture" );
}


void KPrBackGround::loadOasis(KoOasisContext & context )
{
    KoStyleStack& styleStack = context.styleStack();
    kdDebug()<<"KPrBackGround::loadOasis()\n";
    styleStack.setTypeProperties( "drawing-page" );
    if ( styleStack.hasAttributeNS( KoXmlNS::draw, "fill" ) )
    {
        const TQString fill = styleStack.attributeNS( KoXmlNS::draw, "fill" );
        kdDebug(33001) <<"fill page type :" << fill << endl;
        if ( fill == "solid" || fill == "gradient" )
        {
            KPrBrush brush;
            brush.loadOasisFillStyle( context, "drawing-page" );
            if ( brush.getFillType() == FT_BRUSH )
            {
                setBackColor1( brush.getBrush().color() );
                setBackColorType( BCT_PLAIN );
            }
            else
            {
                setBackColor1( brush.getGColor1() );
                setBackColor2( brush.getGColor2() );
                setBackColorType( brush.getGType() );
                setBackUnbalanced( brush.getGUnbalanced() );
                setBackXFactor( brush.getGXFactor() );
                setBackYFactor( brush.getGYFactor() );
            }
            setBackType( BT_COLOR );
        }
        else if ( fill == "bitmap" )
        {
            TQString style = styleStack.attributeNS( KoXmlNS::draw, "fill-image-name" );
            TQDomElement* draw =context.oasisStyles().drawStyles()[style];

            const TQString href( draw->attributeNS( KoXmlNS::xlink, "href", TQString()) );
            kdDebug()<<" href: "<<href<<endl;
            if ( !href.isEmpty() )
            {
                TQString strExtension;
                const int result=href.findRev(".");
                if (result>=0)
                {
                    strExtension=href.mid(result+1); // As we are using KoPicture, the extension should be without the dot.
                }
                TQString filename(href);
                const KoPictureKey key(filename, TQDateTime::currentDateTime(TQt::UTC));
                backPicture.setKey(key);

                KoStore* store = context.store();
                if ( store->open( filename ) )
                {
                    KoStoreDevice dev(store);
                    if ( !backPicture.load( &dev, strExtension ) )
                        kdWarning() << "Cannot load picture: " << filename << " " << href << endl;
                    store->close();
                }
                pictureCollection()->insertPicture( key, backPicture );
            }

            if ( styleStack.hasAttributeNS( KoXmlNS::style, "repeat" ) )
            {
                TQString repeat = styleStack.attributeNS( KoXmlNS::style, "repeat" );
                if ( repeat == "stretch" )
                    setBackView( BV_ZOOM );
                else if ( repeat == "no-repeat" )
                    setBackView( BV_CENTER );
                else
                    setBackView( BV_TILED );
            }
            else
                setBackView( BV_TILED );; // use tiled as default

            setBackType(BT_PICTURE);
        }
    }
}

void KPrBackGround::load( const TQDomElement &element )
{
    //compatible with old file format kpresenter-1.4
    TQDomElement e=element.namedItem("BACKMASTER").toElement();
    if ( !e.isNull() )
    {
        if ( e.hasAttribute( "useMasterBackground" ) )
        {
            m_page->setUseMasterBackground( (bool)e.attribute("useMasterBackground").toInt());
        }
        else //for kpresenter-1.4
            m_page->setUseMasterBackground( true );

        if(e.hasAttribute("displayMasterPageObject"))
        {
            m_page->setDisplayObjectFromMasterPage( (bool)e.attribute("displayMasterPageObject").toInt());
        }
        if(e.hasAttribute("displayBackground"))
        {
            m_page->setDisplayBackground( (bool)e.attribute("displayBackground").toInt());
        }
    }
    else
        m_page->setUseMasterBackground( false );

    e=element.namedItem("BACKTYPE").toElement();
    if(!e.isNull()) {
        int tmp=0;
        if(e.hasAttribute("value"))
            tmp=e.attribute("value").toInt();
        setBackType(static_cast<BackType>(tmp));
    }
    e=element.namedItem("BACKVIEW").toElement();
    if(!e.isNull()) {
        int tmp=0;
        if(e.hasAttribute("value"))
            tmp=e.attribute("value").toInt();
        setBackView(static_cast<BackView>(tmp));
    }
    e=element.namedItem("BACKCOLOR1").toElement();
    if(!e.isNull()) {
        int red=0, green=0, blue=0;
        if(e.hasAttribute("red"))
            red=e.attribute("red").toInt();
        if(e.hasAttribute("green"))
            green=e.attribute("green").toInt();
        if(e.hasAttribute("blue"))
            blue=e.attribute("blue").toInt();
        if(e.hasAttribute("color"))
            setBackColor1(TQColor(e.attribute("color")));
        else
            setBackColor1(TQColor(red, green, blue));
    }
    e=element.namedItem("BACKCOLOR2").toElement();
    if(!e.isNull()) {
        int red=0, green=0, blue=0;
        if(e.hasAttribute("red"))
            red=e.attribute("red").toInt();
        if(e.hasAttribute("green"))
            green=e.attribute("green").toInt();
        if(e.hasAttribute("blue"))
            blue=e.attribute("blue").toInt();
        if(e.hasAttribute("color"))
            setBackColor2(TQColor(e.attribute("color")));
        else
            setBackColor2(TQColor(red, green, blue));
    }
    e=element.namedItem("BGRADIENT").toElement();
    if(!e.isNull()) {
        int xf=0, yf=0, unbalanced=0;
        if(e.hasAttribute("xfactor"))
            xf=e.attribute("xfactor").toInt();
        setBackXFactor(xf);
        if(e.hasAttribute("yfactor"))
            yf=e.attribute("yfactor").toInt();
        setBackYFactor(yf);
        if(e.hasAttribute("unbalanced"))
            unbalanced=e.attribute("unbalanced").toInt();
        setBackUnbalanced(static_cast<bool>(unbalanced));
    }
    e=element.namedItem("BCTYPE").toElement();
    if(!e.isNull()) {
        int tmp=0;
        if(e.hasAttribute("value"))
            tmp=e.attribute("value").toInt();
        setBackColorType(static_cast<BCType>(tmp));
    }
    e=element.namedItem("BACKPICTUREKEY").toElement();
    if(!e.isNull()) {
        KoPictureKey key;
        key.loadAttributes( e );
        backPicture.clear();
        backPicture.setKey( key );
        // Image will be set by reload(), called by completeLoading()
    }
    e=element.namedItem("BACKPIXKEY").toElement();
    if(!e.isNull()) {
        KoPictureKey key;
        key.loadAttributes( e );
        backPicture.clear();
        backPicture.setKey( key );
        // Image will be set by reload(), called by completeLoading()
    }
    else {
        // try to find a BACKPIX tag if the BACKPIXKEY is not available...
        KoPictureKey key;
        e=element.namedItem("BACKPIX").toElement();
        if(!e.isNull()) {
            bool openPic = true;
            TQString _data;
            TQString _fileName;
            if(e.hasAttribute("data"))
                _data=e.attribute("data");
            if ( _data.isEmpty() )
                openPic = true;
            else
                openPic = false;
            if(e.hasAttribute("filename"))
                _fileName=e.attribute("filename");
            if ( !_fileName.isEmpty() )
            {
                if ( int _envVarB = _fileName.find( '$' ) >= 0 )
                {
                    int _envVarE = _fileName.find( '/', _envVarB );
                    TQString path = getenv( TQFile::encodeName(_fileName.mid( _envVarB, _envVarE-_envVarB )) );
                    _fileName.replace( _envVarB-1, _envVarE-_envVarB+1, path );
                }
            }
            if ( openPic )
                // !! this loads it from the disk (unless it's in the image collection already)
                backPicture = pictureCollection()->loadPicture( _fileName );
            else
            {
                KoPictureKey key( _fileName );
                backPicture.clear();
                backPicture.setKey(key);
                TQByteArray rawData=_data.utf8(); // XPM is normally ASCII, therefore UTF-8
                rawData[rawData.size()-1]=char(10); // Replace the NULL character by a LINE FEED
                TQBuffer buffer(rawData);
                backPicture.loadXpm(&buffer);
            }

#if 0
            if ( ext == orig_size.toTQSize() )
                ext = backPicture.size();

            backPicture = backPicture.scale( ext );
#endif
        }
    }
    e=element.namedItem("BACKCLIPKEY").toElement();
    if(!e.isNull()) {
        KoPictureKey clipKey;
        clipKey.loadAttributes( e );
        backPicture.clear();
        backPicture.setKey(clipKey);
        // Picture will be set by reload(), called by completeLoading()
    }
    else {
        // try to find a BACKCLIP tag if the BACKCLIPKEY is not available...
        e=element.namedItem("BACKCLIP").toElement();
        if(!e.isNull()) {
            TQString _fileName;
            if(e.hasAttribute("filename"))
                _fileName=e.attribute("filename");
            if ( !_fileName.isEmpty() )
            {
                if ( int _envVarB = _fileName.find( '$' ) >= 0 )
                {
                    int _envVarE = _fileName.find( '/', _envVarB );
                    TQString path = getenv( TQFile::encodeName(_fileName.mid( _envVarB, _envVarE-_envVarB )) );
                    _fileName.replace( _envVarB-1, _envVarE-_envVarB+1, path );
                }
            }
            //KPClipartKey clipKey( _fileName, TQDateTime( pictureCollection()->tmpDate(),
            //                                            pictureCollection()->tmpTime() ) );
            backPicture = pictureCollection()->loadPicture( _fileName ); // load from disk !
        }
    }
}

void KPrBackGround::drawBackColor( TQPainter *_painter, const TQSize& ext, const TQRect& crect )
{
    if ( (backType == BT_COLOR && bcType == BCT_PLAIN) || backColor1 == backColor2 ) //plain color
    {
        //kdDebug(33001) << "KPrBackGround::drawBackColor (filling " << DEBUGRECT(crect) << ")" << endl;
        _painter->fillRect( crect, TQBrush( getBackColor1() ) );
    }
    else if (backType == BT_COLOR && bcType != BCT_PLAIN) { //gradient
        if ( !gradientPixmap || gradientPixmap->size() != ext )
            generateGradient( ext );
        _painter->drawPixmap( crect.topLeft(), *gradientPixmap, crect );
    }
    else /*if ( backType == BT_CLIPART || backType == BT_PICTURE )*/ //no gradient or bg color
        _painter->fillRect( crect, TQBrush( TQt::white ) );
    return;
}

void KPrBackGround::drawBackPix( TQPainter *_painter, const TQSize& ext, const TQRect& /*crect*/ )
{
    /*kdDebug(33001) << "KPrBackGround::drawBackPix ext=" << ext.width() << "," << ext.height() << endl;
      kdDebug(33001) << "mode=" << (backView==BV_ZOOM?"ZOOM":backView==BV_TILED?"TILED":backView==BV_CENTER?"CENTER":"OTHER")
      << " crect=" << DEBUGRECT(crect) << endl;*/
    if ( !backPicture.isNull() )
    {
        // depend on page size and desktop size
        const TQSize _origSize = backPicture.getOriginalSize();
        // NOTE: make all multiplications before any division
        double w = _origSize.width();
        w *= ext.width();
        TQRect desk = TDEGlobalSettings::desktopGeometry(kapp->activeWindow());
        w /= desk.width();
        double h = _origSize.height();
        h *= ext.height();
        h /= desk.height();
        const TQSize _pixSize = TQSize( (int)w, (int)h );
        TQPixmap backPix;

        switch ( backView )
        {
        case BV_ZOOM:
            backPix=backPicture.generatePixmap( ext, true );
            _painter->drawPixmap( TQRect( 0, 0, ext.width(), ext.height() ), backPix );
            break;
        case BV_TILED:
            backPix=backPicture.generatePixmap( _pixSize, true );
            _painter->drawTiledPixmap( 0, 0, ext.width(), ext.height(), backPix );
            break;
        case BV_CENTER:
        {
            backPix=backPicture.generatePixmap( _pixSize, true );

            TQPixmap *pix = new TQPixmap( ext.width(), ext.height() );
            bool delPix = true;
            int _x = 0, _y = 0;

            if ( backPix.width() > pix->width() && backPix.height() > pix->height() )
                bitBlt( pix, 0, 0, &backPix, backPix.width() - pix->width(), backPix.height() - pix->height(),
                        pix->width(), pix->height() );
            else if ( backPix.width() > pix->width() )
            {
                bitBlt( pix, 0, 0, &backPix, backPix.width() - pix->width(), 0,
                        pix->width(), backPix.height() );
                _y = ( pix->height() - backPix.height() ) / 2;
            }
            else if ( backPix.height() > pix->height() )
            {
                bitBlt( pix, 0, 0, &backPix, 0, backPix.height() - pix->height(),
                        backPix.width(), pix->height() );
                _x = ( pix->width() - backPix.width() ) / 2;
            }
            else
            {
                _x = ( pix->width() - backPix.width() ) / 2;
                _y = ( pix->height() - backPix.height() ) / 2;
                delPix = false;
                delete pix;
                pix = &backPix;
            }

            if ( pix && !pix->isNull() )
                _painter->drawPixmap( _x, _y, *pix );
            if ( delPix ) delete pix;
        }
        break;
        }
    }
}

void KPrBackGround::drawBorders( TQPainter *_painter, const TQSize& /*ext*/, const TQRect& /*crect*/ )
{
    _painter->setPen( TQApplication::palette().active().color( TQColorGroup::Dark ) );
    _painter->setBrush( TQt::NoBrush );

     TQRect rect = m_page->getZoomPageRect();

     _painter->drawRect( rect.x()-1,rect.y()-1,rect.width()+1,rect.height()+1 );
}

void KPrBackGround::generateGradient( const TQSize& size )
{
    if ( backType == BT_COLOR || backType == BT_CLIPART ||
         backType == BT_PICTURE && backView == BV_CENTER ) {
        removeGradient();
        gradientPixmap = &gradientCollection()->getGradient( backColor1, backColor2, bcType, size,
                                                             unbalanced, xfactor, yfactor );
    }

    // Avoid keeping an unused gradient around
    if ( (backType == BT_PICTURE || backType==BT_CLIPART) && gradientPixmap )
        removeGradient();
}

void KPrBackGround::removeGradient()
{
    if ( gradientPixmap ) {
        gradientCollection()->removeRef( backColor1, backColor2, bcType, gradientPixmap->size(),
                                         unbalanced, xfactor, yfactor);
        gradientPixmap = 0;
    }
}

KoPictureCollection * KPrBackGround::pictureCollection() const
{
    return m_page->kPresenterDoc()->pictureCollection();
}

KPrGradientCollection * KPrBackGround::gradientCollection() const
{
    return m_page->kPresenterDoc()->gradientCollection();
}

void KPrBackGround::setBackGround( const struct Settings &settings )
{
    backType = settings.m_backType;
    backColor1 = settings.m_backColor1;
    backColor2 = settings.m_backColor2;
    bcType = settings.m_bcType;
    unbalanced = settings.m_unbalanced;
    xfactor = settings.m_xfactor;
    yfactor = settings.m_yfactor;
    removeGradient();
    setBackPicture( settings.m_backPictureKey );
    backView = settings.m_backView;

}

struct KPrBackGround::Settings KPrBackGround::getBackGround() const
{
    return Settings( backType, backColor1, backColor2,
                     bcType, unbalanced, xfactor, yfactor,
                     backPicture.getKey(), backView);
}
