/*
   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 "KDXMLTools.h"
#include <tqbuffer.h>
#include <tqimage.h>
#include <zlib.h>

namespace KDXML {

    void createBoolNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, bool value )
    {
        TQDomElement newElement =
            doc.createElement( elementName );
        parent.appendChild( newElement );
        TQDomText elementContent =
            doc.createTextNode( value ? "true" : "false" );
        newElement.appendChild( elementContent );
    }


    void createOrientationNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, TQt::Orientation value )
    {
        TQDomElement newElement =
            doc.createElement( elementName );
        parent.appendChild( newElement );
        TQDomText elementContent =
            doc.createTextNode( (TQt::Vertical == value) ? "vertical" : "horizontal" );
        newElement.appendChild( elementContent );
    }



    void createSizeNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, const TQSize& value )
    {
        TQDomElement newElement =
            doc.createElement( elementName );
        parent.appendChild( newElement );
        newElement.setAttribute( "Width", value.width() );
        newElement.setAttribute( "Height", value.height() );
    }


    void createIntNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, int value )
    {
        TQDomElement newElement =
            doc.createElement( elementName );
        parent.appendChild( newElement );
        TQDomText elementContent =
            doc.createTextNode( TQString::number( value ) );
        newElement.appendChild( elementContent );
    }


    void createDoubleNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, double value )
    {
        TQDomElement newElement =
            doc.createElement( elementName );
        parent.appendChild( newElement );
        TQDomText elementContent =
            doc.createTextNode( TQString::number( value ) );
        newElement.appendChild( elementContent );
    }


    void createStringNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName,
            const TQString& text )
    {
        TQDomElement newElement =
            doc.createElement( elementName );
        parent.appendChild( newElement );
        TQDomText elementContent =
            doc.createTextNode( text );
        newElement.appendChild( elementContent );
    }


    void createColorNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, const TQColor& color )
    {
        TQDomElement colorElement = doc.createElement( elementName );
        parent.appendChild( colorElement );
        colorElement.setAttribute( "Red",
                TQString::number( color.red() ) );
        colorElement.setAttribute( "Green",
                TQString::number( color.green() ) );
        colorElement.setAttribute( "Blue",
                TQString::number( color.blue() ) );
    }


    void createBrushNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, const TQBrush& brush )

    {
        TQDomElement brushElement = doc.createElement( elementName );
        parent.appendChild( brushElement );
        createColorNode( doc, brushElement, "Color", brush.color() );
        createStringNode( doc, brushElement, "Style",
                KDXML::brushStyleToString( brush.style() ) );
        if( brush.style() == TQt::CustomPattern && brush.pixmap() )
            createPixmapNode( doc, brushElement, "Pixmap", *brush.pixmap() );
    }


    void createPixmapNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, const TQPixmap& pixmap )
    {
        TQDomElement pixmapElement = doc.createElement( elementName );
        parent.appendChild( pixmapElement );

        createStringNode( doc, pixmapElement, "Format", "XPM.GZ" );
        if( pixmap.isNull() ){
            // we store an empty image without any data
            createIntNode( doc, pixmapElement, "Length", 0 );
        }else{
            // Convert the pixmap to an image, save that image to an in-memory
            // XPM representation and compress this representation. This
            // conforms to the file format TQt Designer uses.
            TQByteArray ba;
            TQBuffer buffer( ba );
            buffer.open( IO_WriteOnly );
            TQImageIO imgio( &buffer, "XPM" );
            TQImage image = pixmap.convertToImage();
            imgio.setImage( image );
            imgio.write();
            buffer.close();
            ulong len = ba.size() * 2;
            TQByteArray bazip( len );
            ::compress(  (uchar*) bazip.data(), &len, (uchar*) ba.data(), ba.size() );
            TQString dataString;
            static const char hexchars[] = "0123456789abcdef";
            for ( int i = 0; i < (int)len; ++i ) {
                uchar c = (uchar) bazip[i];
                dataString += hexchars[c >> 4];
                dataString += hexchars[c & 0x0f];
            }
            createIntNode( doc, pixmapElement, "Length", ba.size() );
            createStringNode( doc, pixmapElement, "Data", dataString );
        }
    }


    void createRectNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, const TQRect& rect )
    {
        TQDomElement rectElement = doc.createElement( elementName );
        parent.appendChild( rectElement );
        TQDomElement xElement = doc.createElement( "X" );
        rectElement.appendChild( xElement );
        TQDomText xContent = doc.createTextNode( TQString::number( rect.x() ) );
        xElement.appendChild( xContent );
        TQDomElement yElement = doc.createElement( "Y" );
        rectElement.appendChild( yElement );
        TQDomText yContent = doc.createTextNode( TQString::number( rect.y() ) );
        yElement.appendChild( yContent );
        TQDomElement widthElement = doc.createElement( "Width" );
        rectElement.appendChild( widthElement );
        TQDomText widthContent = doc.createTextNode( TQString::number( rect.width() ) );
        widthElement.appendChild( widthContent );
        TQDomElement heightElement = doc.createElement( "Height" );
        rectElement.appendChild( heightElement );
        TQDomText heightContent = doc.createTextNode( TQString::number( rect.height() ) );
        heightElement.appendChild( heightContent );
    }


    void createStringListNodes( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName,
            const TQStringList* list )
    {
        if( !list )
            return;

        for( TQStringList::ConstIterator it = list->begin();
                it != list->end(); ++it ) {
            TQDomElement element = doc.createElement( elementName );
            parent.appendChild( element );
            TQDomText elementContent = doc.createTextNode( *it );
            element.appendChild( elementContent );
        }
    }


    void createFontNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, const TQFont& font )
    {
        TQDomElement fontElement = doc.createElement( elementName );
        parent.appendChild( fontElement );
        createStringNode( doc, fontElement, "Family", font.family() );
        createIntNode( doc, fontElement, "PointSize", font.pointSize() );
        createIntNode( doc, fontElement, "Weight", font.weight() );
        createBoolNode( doc, fontElement, "Italic", font.italic() );
    }


    void createPenNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, const TQPen& pen )
    {
        TQDomElement penElement = doc.createElement( elementName );
        parent.appendChild( penElement );
        createIntNode( doc, penElement, "Width", pen.width() );
        createColorNode( doc, penElement, "Color", pen.color() );
        createStringNode( doc, penElement, "Style", penStyleToString( pen.style() ) );
    }


    void createDateTimeNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, 
            const TQDateTime& datetime )
    {
        TQDomElement dateTimeElement = doc.createElement( elementName );
        parent.appendChild( dateTimeElement );
        createDateNode( doc, dateTimeElement, "Date", datetime.date() );
        createTimeNode( doc, dateTimeElement, "Time", datetime.time() );
    }


    void createDateNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, const TQDate& date )
    {
        TQDomElement dateElement = doc.createElement( elementName );
        parent.appendChild( dateElement );
        dateElement.setAttribute( "Year", TQString::number( date.year() ) );
        dateElement.setAttribute( "Month", TQString::number( date.month() ) );
        dateElement.setAttribute( "Day", TQString::number( date.day() ) );
    }


    void createTimeNode( TQDomDocument& doc, TQDomNode& parent,
            const TQString& elementName, const TQTime& time )
    {
        TQDomElement timeElement = doc.createElement( elementName );
        parent.appendChild( timeElement );
        timeElement.setAttribute( "Hour",
                TQString::number( time.hour() ) );
        timeElement.setAttribute( "Minute",
                TQString::number( time.minute() ) );
        timeElement.setAttribute( "Second",
                TQString::number( time.second() ) );
        timeElement.setAttribute( "Millisecond",
                TQString::number( time.msec() ) );
    }


    TQString penStyleToString( TQt::PenStyle style )
    {
        switch( style ) {
            case TQt::NoPen:
                return "NoPen";
            case TQt::SolidLine:
                return "SolidLine";
            case TQt::DashLine:
                return "DashLine";
            case TQt::DotLine:
                return "DotLine";
            case TQt::DashDotLine:
                return "DashDotLine";
            case TQt::DashDotDotLine:
                return "DashDotDotLine";
            default: // should not happen
                return "SolidLine";
        }
    }



    TQString brushStyleToString( TQt::BrushStyle style )
    {
        // PENDING(kalle) Support custom patterns
        switch( style ) {
            case TQt::NoBrush:
                return "NoBrush";
            case TQt::SolidPattern:
                return "SolidPattern";
            case TQt::Dense1Pattern:
                return "Dense1Pattern";
            case TQt::Dense2Pattern:
                return "Dense2Pattern";
            case TQt::Dense3Pattern:
                return "Dense3Pattern";
            case TQt::Dense4Pattern:
                return "Dense4Pattern";
            case TQt::Dense5Pattern:
                return "Dense5Pattern";
            case TQt::Dense6Pattern:
                return "Dense6Pattern";
            case TQt::Dense7Pattern:
                return "Dense7Pattern";
            case TQt::HorPattern:
                return "HorPattern";
            case TQt::VerPattern:
                return "VerPattern";
            case TQt::CrossPattern:
                return "CrossPattern";
            case TQt::BDiagPattern:
                return "BDiagPattern";
            case TQt::FDiagPattern:
                return "FDiagPattern";
            case TQt::DiagCrossPattern:
                return "DiagCrossPattern";
            default: // should not happen (but can for a custom pattern)
                return "SolidPattern";
        }
    }


    bool readStringNode( const TQDomElement& element, TQString& value )
    {
        value = element.text();
        return true;
    }


    bool readIntNode( const TQDomElement& element, int& value )
    {
        bool ok = false;
        int temp = element.text().toInt( &ok );
        if( ok )
            value = temp;
        return ok;
    }


    bool readDoubleNode( const TQDomElement& element, double& value )
    {
        bool ok = false;
        double temp = element.text().toDouble( &ok );
        if( ok )
            value = temp;
        return ok;
    }


    bool readBoolNode( const TQDomElement& element, bool& value )
    {
        if( element.text() == "true" ) {
            value = true;
            return true;
        } else if( element.text() == "false" ) {
            value = false;
            return true;
        } else
            return false;
    }


    bool readOrientationNode( const TQDomElement& element, TQt::Orientation& value )
    {
        if( element.text() == "vertical" ) {
            value = TQt::Vertical;
            return true;
        } else if( element.text() == "horizontal" ) {
            value = TQt::Horizontal;
            return true;
        } else
            return false;
    }


    bool readSizeNode( const TQDomElement& element, TQSize& value )
    {
        bool ok = false;
        int width, height;
        if( element.hasAttribute(      "Width" ) ) {
            width = element.attribute( "Width" ).toInt( &ok );
            if( ok && element.hasAttribute( "Height" ) ) {
                height = element.attribute( "Height" ).toInt( &ok );
                if( ok ){
                    value.setWidth(  width );
                    value.setHeight( height );
                }
            }
        }
        return ok;
    }


    bool readColorNode( const TQDomElement& element, TQColor& value )
    {
        bool ok = true;
        int red=0, green=0, blue=0;
        if( element.hasAttribute( "Red" ) ) {
            bool redOk = false;
            red = element.attribute( "Red" ).toInt( &redOk );
            ok = ok & redOk;
        }
        if( element.hasAttribute( "Green" ) ) {
            bool greenOk = false;
            green = element.attribute( "Green" ).toInt( &greenOk );
            ok = ok & greenOk;
        }
        if( element.hasAttribute( "Blue" ) ) {
            bool blueOk = false;
            blue = element.attribute( "Blue" ).toInt( &blueOk );
            ok = ok & blueOk;
        }

        if( ok )
            value.setRgb( red, green, blue );

        return ok;
    }


    bool readBrushNode( const TQDomElement& element, TQBrush& brush )
    {
        bool ok = true;
        TQColor tempColor;
        TQt::BrushStyle tempStyle=TQt::SolidPattern;
        TQPixmap tempPixmap;
        TQDomNode node = element.firstChild();
        while( !node.isNull() ) {
            TQDomElement element = node.toElement();
            if( !element.isNull() ) { // was really an element
                TQString tagName = element.tagName();
                if( tagName == "Color" ) {
                    ok = ok & readColorNode( element, tempColor );
                } else if( tagName == "Style" ) {
                    TQString value;
                    ok = ok & readStringNode( element, value );
                    tempStyle = stringToBrushStyle( value );
                } else if( tagName == "Pixmap" ) {
                    ok = ok & readPixmapNode( element, tempPixmap );
                } else {
                    tqDebug( "Unknown tag in brush" );
                }
            }
            node = node.nextSibling();
        }

        if( ok ) {
            brush.setColor( tempColor );
            brush.setStyle( tempStyle );
            if( !tempPixmap.isNull() )
                brush.setPixmap( tempPixmap );
        }

        return ok;
    }


    bool readPixmapNode( const TQDomElement& element, TQPixmap& pixmap )
    {
        bool ok = true;
        unsigned long tempLength;
        TQString tempData;
        TQDomNode node = element.firstChild();
        while( !node.isNull() ) {
            TQDomElement element = node.toElement();
            if( !element.isNull() ) { // was really an element
                TQString tagName = element.tagName();
                if( tagName == "Format" ) {
                    TQString formatName;
                    ok = ok & readStringNode( element, formatName );
#ifndef NDEBUG
                    if( formatName != "XPM.GZ" )
                        tqDebug( "Unsupported pixmap format in XML file" );
#endif
                } else if( tagName == "Length" ) {
                    int itempLength;
                    ok = ok & readIntNode( element, itempLength );
                    tempLength = itempLength;
                } else if( tagName == "Data" ) {
                    ok = ok & readStringNode( element, tempData );
                } else {
                    tqDebug( "Unknown tag in Pixmap" );
                }
            }
            node = node.nextSibling();
        }

        if( ok ) {
            if( 0 < tempLength ) {
                // Decode the image file format in the same way TQt Designer does.
                char *ba = new char[ tempData.length() / 2 ];
                for ( int i = 0; i < (int)tempData.length() / 2; ++i ) {
                    char h = tempData[ 2 * i ].latin1();
                    char l = tempData[ 2 * i  + 1 ].latin1();
                    uchar r = 0;
                    if ( h <= '9' )
                        r += h - '0';
                    else
                        r += h - 'a' + 10;
                    r = r << 4;
                    if ( l <= '9' )
                        r += l - '0';
                    else
                        r += l - 'a' + 10;
                    ba[ i ] = r;
                }

                if( tempLength < tempData.length() * 5 )
                    tempLength = tempData.length() * 5;
                TQByteArray baunzip( tempLength );
                ::uncompress( (uchar*) baunzip.data(), &tempLength,
                        (uchar*) ba, tempData.length()/2 );
                TQImage image;
                image.loadFromData( (const uchar*)baunzip.data(), tempLength, "XPM" );

                if( image.isNull() )
                    pixmap.resize( 0, 0 ); // This is _not_ an error, we just read a NULL pixmap!
                else
                    ok = ok & pixmap.convertFromImage( image, 0 );
            } else
                pixmap.resize( 0, 0 ); // This is _not_ an error, we just read a empty pixmap!
        }

        return ok;
    }


    bool readPenNode( const TQDomElement& element, TQPen& pen )
    {
        bool ok = true;
        int tempWidth;
        TQColor tempColor;
        TQt::PenStyle tempStyle=TQt::SolidLine;
        TQDomNode node = element.firstChild();
        while( !node.isNull() ) {
            TQDomElement element = node.toElement();
            if( !element.isNull() ) { // was really an element
                TQString tagName = element.tagName();
                if( tagName == "Width" ) {
                    ok = ok & readIntNode( element, tempWidth );
                } else if( tagName == "Color" ) {
                    ok = ok & readColorNode( element, tempColor );
                } else if( tagName == "Style" ) {
                    TQString value;
                    ok = ok & readStringNode( element, value );
                    tempStyle = stringToPenStyle( value );
                } else {
                    tqDebug( "Unknown tag in brush" );
                }
            }
            node = node.nextSibling();
        }

        if( ok ) {
            pen.setWidth( tempWidth );
            pen.setColor( tempColor );
            pen.setStyle( tempStyle );
        }

        return ok;
    }

    bool readFontNode( const TQDomElement& element, TQFont& font )
    {
        bool ok = true;
        TQString family;
        int pointSize, weight;
        bool italic;
        int charSet;
        TQDomNode node = element.firstChild();
        while( !node.isNull() ) {
            TQDomElement element = node.toElement();
            if( !element.isNull() ) { // was really an element
                TQString tagName = element.tagName();
                if( tagName == "Family" ) {
                    ok = ok & readStringNode( element, family );
                } else if( tagName == "PointSize" ) {
                    ok = ok & readIntNode( element, pointSize );
                } else if( tagName == "Weight" ) {
                    ok = ok & readIntNode( element, weight );
                } else if( tagName == "Italic" ) {
                    ok = ok & readBoolNode( element, italic );
                } else if( tagName == "CharSet" ) {
                    ok = ok & readIntNode( element, charSet );
                } else {
                    tqDebug( "Unknown tag in color map" );
                }
            }
            node = node.nextSibling();
        }

        if( ok ) {
            font.setFamily( family );
            font.setPointSize( pointSize );
            font.setWeight( weight );
            font.setItalic( italic );
        }

        return ok;
    }

    bool readRectNode( const TQDomElement& element, TQRect& value )
    {
        bool ok = true;
        int width, height, x, y;
        TQDomNode node = element.firstChild();
        while( !node.isNull() ) {
            TQDomElement element = node.toElement();
            if( !element.isNull() ) { // was really an element
                TQString tagName = element.tagName();
                if( tagName == "Width" ) {
                    ok = ok & readIntNode( element, width );
                } else if( tagName == "Height" ) {
                    ok = ok & readIntNode( element, height );
                } else if( tagName == "X" ) {
                    ok = ok & readIntNode( element, x );
                } else if( tagName == "Y" ) {
                    ok = ok & readIntNode( element, y );
                } else {
                    tqDebug( "Unknown tag in rect" );
                }
            }
            node = node.nextSibling();
        }

        if( ok ) {
            value.setX( x );
            value.setY( y );
            value.setWidth( width );
            value.setHeight( height );
        }

        return ok;
    }



    bool readDateTimeNode( const TQDomElement& element, TQDateTime& datetime )
    {
        bool ok = true;
        TQDate tempDate;
        TQTime tempTime;
        TQDomNode node = element.firstChild();
        while( !node.isNull() ) {
            TQDomElement element = node.toElement();
            if( !element.isNull() ) { // was really an element
                TQString tagName = element.tagName();
                if( tagName == "Date" ) {
                    ok = ok & readDateNode( element, tempDate );
                } else if( tagName == "Time" ) {
                    ok = ok & readTimeNode( element, tempTime );
                } else {
                    tqDebug( "Unknown tag in datetime" );
                }
            }
            node = node.nextSibling();
        }

        if( ok ) {
            datetime.setDate( tempDate );
            datetime.setTime( tempTime );
        }

        return ok;
    }


    bool readDateNode( const TQDomElement& element, TQDate& value )
    {
        bool ok = true;
        int year=0, month=0, day=0;
        if( element.hasAttribute( "Year" ) ) {
            bool yearOk = false;
            year = element.attribute( "Year" ).toInt( &yearOk );
            ok = ok & yearOk;
        }
        if( element.hasAttribute( "Month" ) ) {
            bool monthOk = false;
            month = element.attribute( "Month" ).toInt( &monthOk );
            ok = ok & monthOk;
        }
        if( element.hasAttribute( "Day" ) ) {
            bool dayOk = false;
            day = element.attribute( "Day" ).toInt( &dayOk );
            ok = ok & dayOk;
        }

        if( ok )
            value.setYMD( year, month, day );

        return ok;
    }



    bool readTimeNode( const TQDomElement& element, TQTime& value )
    {
        bool ok = true;
        int hour=0, minute=0, second=0, msec=0;
        if( element.hasAttribute( "Hour" ) ) {
            bool hourOk = false;
            hour = element.attribute( "Hour" ).toInt( &hourOk );
            ok = ok & hourOk;
        }
        if( element.hasAttribute( "Minute" ) ) {
            bool minuteOk = false;
            minute = element.attribute( "Minute" ).toInt( &minuteOk );
            ok = ok & minuteOk;
        }
        if( element.hasAttribute( "Second" ) ) {
            bool secondOk = false;
            second = element.attribute( "Second" ).toInt( &secondOk );
            ok = ok & secondOk;
        }
        if( element.hasAttribute( "Millisecond" ) ) {
            bool msecOk = false;
            msec = element.attribute( "Millisecond" ).toInt( &msecOk );
            ok = ok & msecOk;
        }

        if( ok )
            value.setHMS( hour, minute, second, msec );

        return ok;
    }



    TQt::PenStyle stringToPenStyle( const TQString& style )
    {
        if( style == "NoPen" )
            return TQt::NoPen;
        else if( style == "SolidLine" )
            return TQt::SolidLine;
        else if( style == "DashLine" )
            return TQt::DashLine;
        else if( style == "DotLine" )
            return TQt::DotLine;
        else if( style == "DashDotLine" )
            return TQt::DashDotLine;
        else if( style == "DashDotDotLine" )
            return TQt::DashDotDotLine;
        else // should not happen
            return TQt::SolidLine;
    }


    TQt::BrushStyle stringToBrushStyle( const TQString& style )
    {
        // PENDING(kalle) Support custom patterns
        if( style == "NoBrush" )
            return TQt::NoBrush;
        else if( style == "SolidPattern" )
            return TQt::SolidPattern;
        else if( style == "Dense1Pattern" )
            return TQt::Dense1Pattern;
        else if( style == "Dense2Pattern" )
            return TQt::Dense2Pattern;
        else if( style == "Dense3Pattern" )
            return TQt::Dense3Pattern;
        else if( style == "Dense4Pattern" )
            return TQt::Dense4Pattern;
        else if( style == "Dense5Pattern" )
            return TQt::Dense5Pattern;
        else if( style == "Dense6Pattern" )
            return TQt::Dense6Pattern;
        else if( style == "Dense7Pattern" )
            return TQt::Dense7Pattern;
        else if( style == "HorPattern" )
            return TQt::HorPattern;
        else if( style == "VerPattern" )
            return TQt::VerPattern;
        else if( style == "CrossPattern" )
            return TQt::CrossPattern;
        else if( style == "BDiagPattern" )
            return TQt::BDiagPattern;
        else if( style == "FDiagPattern" )
            return TQt::FDiagPattern;
        else if( style == "DiagCrossPattern" )
            return TQt::DiagCrossPattern;
        else // should not happen (but can with custom patterns)
            return TQt::SolidPattern;
    }

}
