/***************************************************************************
            mreportengine.cpp  -  Kugar report engine
            -------------------
  begin     : Sun Aug 15 1999
  copyright : (C) 1999 by Mutiny Bay Software
  email     : info@mutinybaysoftware.com
  copyright : (C) 2002 Alexander Dymo
  email     : cloudtemple@mksat.net
***************************************************************************/

#include <tqfile.h>
#include <tqprinter.h>

#include "mreportengine.h"
#include "mutil.h"

#include <kdebug.h>

#include <math.h>

namespace Kugar
{

/** Constructor */
MReportEngine::MReportEngine() : TQObject()
{
    // Set page params
    m_pageCollection = 0;
    m_needRegeneration = true;
    m_refCount = 1;
    pageSize = MReportEngine::Letter;
    pageOrientation = MReportEngine::Portrait;
    topMargin = 0;
    bottomMargin = 0;
    leftMargin = 0;
    rightMargin = 0;
    heightOfDetails = 0;
    widthDelta = 1;
    heightDelta = 1;

    // Set cancel flag
    cancelRender = false;

    // Set the grand total list to AutoDelete
    grandTotal.setAutoDelete( true );
    // Set the details lists to AutoDelete
    dHeaders.setAutoDelete( true );
    details.setAutoDelete( true );
    dFooters.setAutoDelete( true );

    rHeader.setPrintFrequency( MReportSection::FirstPage );
    pHeader.setPrintFrequency( MReportSection::EveryPage );
    pFooter.setPrintFrequency( MReportSection::EveryPage );
    rFooter.setPrintFrequency( MReportSection::LastPage );

    // Set the default page metrics
    TQSize ps = getPageMetrics( pageSize, pageOrientation );
    pageWidth = ps.width();
    pageHeight = ps.height();
}

/** Copy constructor */
MReportEngine::MReportEngine( const MReportEngine& mReportEngine )   /*: TQObject((TQObject &) mReportEngine)*/
{
    copy( &mReportEngine );
}

/** Assignment operator */
MReportEngine MReportEngine::operator=( const MReportEngine& mReportEngine )
{
    if ( &mReportEngine == this )
        return * this;

    // Copy the derived class's data
    copy( &mReportEngine );

    // Copy the base class's data
    //((TQObject &) *this) = mReportEngine;

    return *this;
}

/** Destructor */
MReportEngine::~MReportEngine()
{
    // Clear the grand total data
    grandTotal.clear();
    kdDebug( 31000 ) << k_funcinfo << endl;
}

void MReportEngine::addRef()
{
    m_refCount++;
}

void MReportEngine::removeRef()
{
    m_refCount--;
    if ( !m_refCount )
        deleteLater();
}

/** Clears report formatting */
void MReportEngine::clearFormatting()
{
    rHeader.clear();
    pHeader.clear();
    dHeaders.clear();
    details.clear();
    dFooters.clear();
    pFooter.clear();
    rFooter.clear();
}


//Set the report data from an existing TQDomDocument
bool MReportEngine::setReportData( const TQDomDocument &data )
{
    rd = data.cloneNode( true ).toDocument();
    initData();

    return true;
}


// Set the report's data from an inline string.  Return true if it was valid
// data.

bool MReportEngine::setReportData( const TQString &data )
{

    if ( !rd.setContent( data ) )
    {
        kdDebug() << "Unable to parse report data" << endl;
        return false;
    }
    initData();

    return true;
}


// Set the report's data from an i/o device.  Return true if it was valid data.

bool MReportEngine::setReportData( TQIODevice *dev )
{
    if ( !rd.setContent( dev ) )
    {
        kdDebug() << "Unable to parse report data" << endl;
        return false;
    }
    initData();

    return true;
}


// Initialise after reading a report's data.

void MReportEngine::initData()
{
    m_needRegeneration = true;
    // Get the record set (we assume there is only one).

    for ( TQDomNode n = rd.firstChild(); !n.isNull(); n = n.nextSibling() )
        if ( n.nodeName() == "KugarData" )
        {
            // Get the records.

            records = n.childNodes();

            // See if there is a prefered template given.

            TQDomNamedNodeMap attr = n.attributes();
            TQDomNode tempattr = attr.namedItem( "Template" );
            TQString tempname = tempattr.nodeValue();

            if ( !tempname.isNull() )
                emit preferedTemplate( tempname );

            break;
        }
}


// Set the report's template from an inline string.  Return true if it was a
// valid template.

bool MReportEngine::setReportTemplate( const TQString &tpl )
{
    clearFormatting();

    if ( !rt.setContent( tpl ) )
    {
        kdDebug() << "Unable to parse report template" << endl;
        return false;
    }

    initTemplate();

    return true;
}


// Set the report's template from an i/o device.  Return true if it was a valid
// template.

bool MReportEngine::setReportTemplate( TQIODevice *dev )
{
    clearFormatting();

    if ( !rt.setContent( dev ) )
    {
        kdDebug() << "Unable to parse report template" << endl;
        return false;
    }

    initTemplate();

    return true;
}


/** Cancels rendering of a report */
void MReportEngine::slotCancelRendering()
{
    cancelRender = true;
}

/** Finds the detail header object, which is apropriate for the given level */
MReportSection *MReportEngine::findDetailHeader( int level )
{
    MReportSection * sec;
    for ( sec = dHeaders.first(); sec; sec = dHeaders.next() )
    {
        if ( sec->getLevel() == level )
            return sec;
    }
    return NULL;
}

/** Finds the detail object, which is apropriate for the given level */
MReportDetail *MReportEngine::findDetail( int level )
{
    MReportDetail * sec;
    for ( sec = details.first(); sec; sec = details.next() )
    {
        if ( sec->getLevel() == level )
            return sec;
    }
    return NULL;
}

/** Finds the detail footer object, which is apropriate for the given level */
MReportSection *MReportEngine::findDetailFooter( int level )
{
    MReportSection * sec;
    for ( sec = dFooters.first(); sec; sec = dFooters.next() )
    {
        if ( sec->getLevel() == level )
            return sec;
    }
    return NULL;
}

/** Renders the report as a page collection - the caller
  * is responsible for de-allocating the returned
  * collection
  */
MPageCollection* MReportEngine::renderReport()
{
    if ( !m_needRegeneration )
        return m_pageCollection;
    if ( m_pageCollection )
    {
        m_pageCollection->deleteLater();
        m_pageCollection = 0;
    }

    unsigned int j;
    unsigned int i;

    // Set cancel flag
    cancelRender = false;

    // Create the page collection
    MPageCollection* pages = new MPageCollection( this );

    // Initialize the basic page data
    currHeight = pageHeight - ( topMargin + bottomMargin + pFooter.getHeight() );
    currPage = 0;
    currDate = TQDate::currentDate();

    // Initialise global report variables
    unsigned int rowCount = records.length();
    for ( j = 0; j < rowCount; j++ )
    {
        TQDomNode record = records.item( j );
        if ( ( record.nodeType() == TQDomNode::ElementNode )
                && ( record.nodeName() == "Var" ) )
        {
            // Process the variable ...
            TQDomNamedNodeMap fields = record.attributes();
            for ( int k = 0; k < fields.count(); ++k )
            {
                rHeader.setFieldData( fields.item( k ).nodeName(), fields.item( k ).nodeValue() );
                rFooter.setFieldData( fields.item( k ).nodeName(), fields.item( k ).nodeValue() );
                pHeader.setFieldData( fields.item( k ).nodeName(), fields.item( k ).nodeValue() );
                pFooter.setFieldData( fields.item( k ).nodeName(), fields.item( k ).nodeValue() );
            }
        }
    }

    // Initialize the grand total array
    grandTotal.clear();
    for ( int i = 0; i < rFooter.getCalcFieldCount(); i++ )
        grandTotal.append( new TQMemArray<double> );

    // Create the first page
    startPage( pages );

    // Draw the detail section by looping through all the records
    int chkRow = 0;
    int curDetailLevel = 0;
    int prevDetailLevel = -1;
    int curDetailHeader = -1;
    int curDetailFooter = -1;

    for ( j = 0; j < rowCount; j++ )
    {
        TQString detailValue;
        TQDomNode record = records.item( j );

        if ( ( record.nodeType() == TQDomNode::ElementNode )
                && ( record.nodeName() == "Row" ) )
        {
            // Update status event
            if ( ( chkRow = ( j / 2 ) % 20 ) == 0 )
                emit signalRenderStatus( j / 2 );

            // Check for cancel action
            if ( cancelRender )
            {
                p.end();
                delete pages;
                return 0;
            }

            // Process the record ...
            TQDomNamedNodeMap fields = record.attributes();

            // Find the detail object to process with rendering
            detailValue = fields.namedItem( "level" ).nodeValue();
            curDetailLevel = detailValue.toInt();

            // Reset the header/footer detail levels?
            curDetailHeader = curDetailLevel > curDetailHeader ? -1 : curDetailHeader;
            curDetailFooter = curDetailLevel > curDetailFooter ? -1 : curDetailFooter;

            // Draw detail footer if appropriate
            if ( curDetailLevel <= prevDetailLevel )
            {
                // Draw footer for previous detail levels
                //   from prevDetailLevel up to curDetailLevel
                // Draw footer for current detail level
                for ( int i = prevDetailLevel; i >= curDetailLevel; i-- )
                {
                    MReportSection *footer = findDetailFooter( i );
                    if ( footer )
                    {
                        footer->setPageNumber( currPage );
                        footer->setReportDate( currDate );
                        footer->setCalcFieldData( &grandTotal );
                        if ( ( currY + footer->getHeight() ) > currHeight )
                        {
                            newPage( pages );
                        }
                        footer->draw( &p, leftMargin, currY );
                        currY += footer->getHeight();
                    }
                }
            }

            MReportDetail *detail = findDetail( detailValue.toInt() );
            detail->setPageNumber( currPage );
            detail->setReportDate( currDate );

            int headerHeight = 0;
            MReportSection * header = findDetailHeader( curDetailLevel );
            if ( header && curDetailHeader != curDetailLevel )
                headerHeight = header->getHeight();

            bool repeating = ( currY + detail->getHeight() + headerHeight ) > currHeight ||
                             ( detail->getRepeat() && currY + heightOfDetails > currHeight );
            if ( repeating )
            {
                newPage( pages );

                MReportDetail *sec;
                for ( sec = details.first(); sec; sec = details.next() )
                {
                    if ( sec->getLevel() != curDetailLevel && sec->getRepeat() )
                    {
                        sec->draw( &p, leftMargin, currY );
                        currY += sec->getHeight();
                    }
                }
            }

            // Draw detail header for level curDetailLevel
            // if it hasn't already been drawn
            if ( curDetailHeader != curDetailLevel || repeating )
            {
                MReportSection * header = findDetailHeader( curDetailLevel );
                if ( header )
                {
                    header->setPageNumber( currPage );
                    header->setReportDate( currDate );
                    if ( ( currY + header->getHeight() ) > currHeight )
                    {
                        newPage( pages );
                    }
                    header->draw( &p, leftMargin, currY );
                    currY += header->getHeight();
                    curDetailHeader = curDetailLevel;
                }
            }

            unsigned int fieldCount = detail->getFieldCount();
            for ( i = 0; i < fieldCount; i++ )
            {
                // Get the field value
                detailValue = fields.namedItem( detail->getFieldName( i ) ).nodeValue();
                // Set the field
                detail->setFieldData( i, detailValue );
                // Set the grand total data
                int calcIdx = rFooter.getCalcFieldIndex( detail->getFieldName( i ) );
                if ( calcIdx != -1 )
                {
                    int vsize = grandTotal.at( calcIdx ) ->size();
                    grandTotal.at( calcIdx ) ->resize( vsize + 1 );
                    grandTotal.at( calcIdx ) ->at( vsize ) = detailValue.toDouble();
                }
            }

            detail->draw( &p, leftMargin, currY );
            currY += detail->getHeight();
            prevDetailLevel = curDetailLevel;
        }
    }

    // Draw detail footers that were not drawn before
    //   for details from curDetailLevel up to prevDetailLevel
    for ( int i = prevDetailLevel; i >= 0; i-- )
    {
        MReportSection *footer = findDetailFooter( i );
        if ( footer )
        {
            footer->setPageNumber( currPage );
            footer->setReportDate( currDate );
            footer->setCalcFieldData( &grandTotal );
            if ( ( currY + footer->getHeight() ) > currHeight )
            {
                newPage( pages );
            }

            footer->draw( &p, leftMargin, currY );
            currY += footer->getHeight();
        }
    }

    // Finish the last page of the report
    endPage( pages );

    // Destroy the page painter
    p.end();

    // Set the page collection attributes
    pages->setPageDimensions( TQSize( pageWidth, pageHeight ) );
    pages->setPageSize( pageSize );
    pages->setPageOrientation( pageOrientation );

    // Send final status
    emit signalRenderStatus( rowCount / 2 );
    m_needRegeneration = false;
    m_pageCollection = pages;
    return pages;
}

/** Starts a new page of the report */
void MReportEngine::startPage( MPageCollection* pages )
{
    // Set the current y pos
    currY = topMargin;

    // Create a new page
    pages->appendPage();
    // Increment the page count
    currPage++;

    // Set the page painter to the current page
    p.begin( pages->getCurrentPage() );

    // Draw the report header
    drawReportHeader( pages );
    // Draw the page header
    drawPageHeader( pages );
}

/** Finishes the current page of the report */
void MReportEngine::endPage( MPageCollection* pages )
{
    // Draw the report footer
    drawReportFooter( pages );
    // Draw the page footer
    drawPageFooter( pages );
}

/** Finishes the current page and adds a new page */
void MReportEngine::newPage( MPageCollection* pages )
{
    // Draw the page footer
    drawPageFooter( pages );

    // Stop the painter
    p.end();

    // Start a new page
    startPage( pages );
}

/** Draws the report header object to the current page */
void MReportEngine::drawReportHeader( MPageCollection* pages )
{
    if ( rHeader.getHeight() == 0 )
        return ;

    if ( ( rHeader.printFrequency() == MReportSection::FirstPage && currPage == 1 )
            || ( rHeader.printFrequency() == MReportSection::EveryPage ) )
    {

        rHeader.setPageNumber( currPage );
        rHeader.setReportDate( currDate );
        rHeader.draw( &p, leftMargin, currY );
        currY += rHeader.getHeight();
    }
}

/** Draws the page header to the current page */
void MReportEngine::drawPageHeader( MPageCollection* pages )
{
    if ( pHeader.getHeight() == 0 )
        return ;

    if ( ( currY + pHeader.getHeight() ) > currHeight )
        newPage( pages );

    if ( ( pHeader.printFrequency() == MReportSection::FirstPage && currPage == 1 )
            || ( pHeader.printFrequency() == MReportSection::EveryPage ) )
    {

        pHeader.setPageNumber( currPage );
        pHeader.setReportDate( currDate );
        pHeader.draw( &p, leftMargin, currY );
        currY += pHeader.getHeight();
    }
}

/** Draws the page footer to the current page */
void MReportEngine::drawPageFooter( MPageCollection* pages )
{
    if ( pFooter.getHeight() == 0 )
        return ;

    if ( ( pFooter.printFrequency() == MReportSection::FirstPage && currPage == 1 )
            || ( pFooter.printFrequency() == MReportSection::EveryPage ) )
    {

        pFooter.setPageNumber( currPage );
        pFooter.setReportDate( currDate );
        pFooter.draw( &p, leftMargin, ( pageHeight - bottomMargin ) - pFooter.getHeight() );
        currY += pFooter.getHeight();
    }
}

/** Draws the report footer to the current page */
void MReportEngine::drawReportFooter( MPageCollection* pages )
{
    if ( rFooter.getHeight() == 0 )
        return ;

    if ( ( currY + rFooter.getHeight() ) > currHeight )
        newPage( pages );

    if ( ( rFooter.printFrequency() == MReportSection::EveryPage )
            || ( rFooter.printFrequency() == MReportSection::LastPage ) )
    {

        rFooter.setCalcFieldData( &grandTotal );

        rFooter.setPageNumber( currPage );
        rFooter.setReportDate( currDate );
        rFooter.draw( &p, leftMargin, currY );
        currY += rFooter.getHeight();
    }
}

/** Gets the metrics for the selected page size & orientation */
TQSize MReportEngine::getPageMetrics( int size, int orientation )
{

    TQPrinter * printer;
    TQSize ps;

    // Set the page size
    printer = new TQPrinter();
    printer->setFullPage( true );
    printer->setPageSize( ( TQPrinter::PageSize ) size );
    printer->setOrientation( ( TQPrinter::Orientation ) orientation );

    // Get the page metrics
    TQPaintDeviceMetrics pdm( printer );

    // Display the first page of the report
    ps.setWidth( pdm.width() );
    ps.setHeight( pdm.height() );

    delete printer;

    return ps;
}


/**
This recalculates report dimensions to 96 dpi, standard on Windows
platform.

FIXME: TODO: get the proper screen resolution and not hardcode 96dpi
FIXME: TODO: make this function work on X11 too
*/
void MReportEngine::recalcDimensions()
{
    TQDomNode report;
    TQDomNode child;
    for ( report = rt.firstChild(); !report.isNull(); report = report.nextSibling() )
        if ( report.nodeName() == "KugarTemplate" )
            break;

    TQDomNamedNodeMap rattributes = report.attributes();
    recalcAttribute( "BottomMargin", rattributes );
    recalcAttribute( "TopMargin", rattributes );
    recalcAttribute( "LeftMargin", rattributes );
    recalcAttribute( "RightMargin", rattributes );

    TQDomNodeList children = report.childNodes();
    int childCount = children.length();

    for ( int j = 0; j < childCount; j++ )
    {
        child = children.item( j );
        TQDomNamedNodeMap attributes = child.attributes();

        TQDomNodeList children2 = child.childNodes();
        int childCount2 = children2.length();
        recalcAttribute( "Height", attributes );

        for ( int k = 0; k < childCount2; k++ )
        {
            TQDomNode child2 = children2.item( k );
            TQDomNamedNodeMap attributes = child2.attributes();
            recalcAttribute( "X", attributes );
            recalcAttribute( "Y", attributes );
            recalcAttribute( "Width", attributes );
            recalcAttribute( "Height", attributes );
            recalcAttribute( "X1", attributes );
            recalcAttribute( "X2", attributes );
            recalcAttribute( "Y1", attributes );
            recalcAttribute( "Y2", attributes );
        }
    }

}

void MReportEngine::recalcAttribute( const TQString& name, TQDomNamedNodeMap attributes )
{
    if ( !attributes.namedItem( name ).isNull() )
    {
        attributes.namedItem( name ).setNodeValue( TQString( "%1" ).arg( attributes.namedItem( name ).nodeValue().toInt() * 93 / 81 ) );
    }
}

/** Walks the document tree, setting the report layout */
void MReportEngine::initTemplate()
{
    heightOfDetails = 0;

#ifdef TQ_WS_WIN

    recalcDimensions();
#endif

    TQDomNode report;
    TQDomNode child;

    m_needRegeneration = true;
    // Get the report - assume there is only one.

    for ( report = rt.firstChild(); !report.isNull(); report = report.nextSibling() )
        if ( report.nodeName() == "KugarTemplate" )
            break;

    setReportAttributes( &report );

    // Get all the child report elements
    TQDomNodeList children = report.childNodes();
    int childCount = children.length();

    for ( int j = 0; j < childCount; j++ )
    {
        child = children.item( j );

        if ( child.nodeType() == TQDomNode::ElementNode )
        {
            // Report Header
            if ( child.nodeName() == "ReportHeader" )
                setSectionAttributes( &rHeader, &child );
            else if ( child.nodeName() == "PageHeader" )
                setSectionAttributes( &pHeader, &child );
            else if ( child.nodeName() == "DetailHeader" )
            {
                MReportSection * dHeader = new MReportSection;
                dHeaders.append( dHeader );
                setDetMiscAttributes( dHeader, &child );
            }
            else if ( child.nodeName() == "Detail" )
                setDetailAttributes( &child );
            else if ( child.nodeName() == "DetailFooter" )
            {
                MReportSection * dFooter = new MReportSection;
                setDetMiscAttributes( dFooter, &child );
                dFooters.append( dFooter );
            }
            else if ( child.nodeName() == "PageFooter" )
                setSectionAttributes( &pFooter, &child );
            else if ( child.nodeName() == "ReportFooter" )
                setSectionAttributes( &rFooter, &child );
        }
    }
}

/** Sets the main layout attributes for the report */
void MReportEngine::setReportAttributes( TQDomNode* report )
{
    // Get the attributes for the report
    TQDomNamedNodeMap attributes = report->attributes();

    pageSize = attributes.namedItem( "PageSize" ).nodeValue().toInt();
    pageOrientation = attributes.namedItem( "PageOrientation" ).nodeValue().toInt();
    int templateWidth = attributes.namedItem( "PageWidth" ).nodeValue().toInt();
    int templateheight = attributes.namedItem( "PageHeight" ).nodeValue().toInt();

    TQSize ps = getPageMetrics( pageSize, pageOrientation );
    pageWidth = ps.width();
    pageHeight = ps.height();

    widthDelta = ( float ) pageWidth / templateWidth;
    heightDelta = ( float ) pageHeight / templateheight;

    kdDebug( 30001 ) << "pagewidth: " << pageWidth
    << " pageheight: " << pageHeight << "\n"
    << "templateWidth: " << templateWidth
    << " templateheight: " << templateheight << "\n"
    << "widthDelta: " << widthDelta
    << " heightDelta: " << heightDelta
    << endl;

    topMargin = scaleDeltaHeight( attributes.namedItem( "TopMargin" ).nodeValue().toInt() );
    bottomMargin = scaleDeltaHeight( attributes.namedItem( "BottomMargin" ).nodeValue().toInt() );
    leftMargin = scaleDeltaWidth( attributes.namedItem( "LeftMargin" ).nodeValue().toInt() );
    rightMargin = scaleDeltaWidth( attributes.namedItem( "RightMargin" ).nodeValue().toInt() );
}

int MReportEngine::scaleDeltaWidth( int width ) const
{
    float f = width * widthDelta;
    return f > 1 ? int( f + 0.5) : ceil( f );
}

int MReportEngine::scaleDeltaHeight( int height ) const
{
    float f = height * heightDelta;
    return f > 1 ? int( f + 0.5 ) : ceil( f );
}

/** Sets the layout attributes for the given report section */
void MReportEngine::setSectionAttributes( MReportSection* section, TQDomNode* report )
{
    // Get the attributes for the section
    TQDomNamedNodeMap attributes = report->attributes();

    // Get the section attributes
    section->setHeight( scaleDeltaHeight( attributes.namedItem( "Height" ).nodeValue().toInt() ) );
    section->setPrintFrequency( attributes.namedItem( "PrintFrequency" ).nodeValue().toInt() );

    // Process the sections labels
    TQDomNodeList children = report->childNodes();
    int childCount = children.length();

    // For each label, extract the attr list and add the new label
    // to the sections's label collection
    for ( int j = 0; j < childCount; j++ )
    {
        TQDomNode child = children.item( j );
        if ( child.nodeType() == TQDomNode::ElementNode )
        {
            if ( child.nodeName() == "Line" )
            {
                TQDomNamedNodeMap attributes = child.attributes();
                MLineObject* line = new MLineObject();
                setLineAttributes( line, &attributes );
                section->addLine( line );
            }
            else if ( child.nodeName() == "Label" )
            {
                TQDomNamedNodeMap attributes = child.attributes();
                MLabelObject* label = new MLabelObject();
                setLabelAttributes( label, &attributes );
                section->addLabel( label );
            }
            else if ( child.nodeName() == "Special" )
            {
                TQDomNamedNodeMap attributes = child.attributes();
                MSpecialObject* field = new MSpecialObject();
                setSpecialAttributes( field, &attributes );
                section->addSpecialField( field );
            }
            else if ( child.nodeName() == "CalculatedField" )
            {
                TQDomNamedNodeMap attributes = child.attributes();
                MCalcObject* field = new MCalcObject();
                setCalculatedFieldAttributes( field, &attributes );
                section->addCalculatedField( field );
            }
            else if ( child.nodeName() == "Field" )
            {
                TQDomNamedNodeMap attributes = child.attributes();
                MFieldObject* field = new MFieldObject();
                setFieldAttributes( field, &attributes );
                section->addField( field );
            }
        }
    }
}

/** Sets the layout attributes for the detail headers and footers */
void MReportEngine::setDetMiscAttributes( MReportSection* section, TQDomNode* report )
{
    // Get the attributes for the section
    TQDomNamedNodeMap attributes = report->attributes();

    // Get the section attributes
    section->setLevel( attributes.namedItem( "Level" ).nodeValue().toInt() );

    // Set other section attributes
    setSectionAttributes( section, report );
}


/** Sets the layout attributes for the detail section */
void MReportEngine::setDetailAttributes( TQDomNode* report )
{
    // Get the attributes for the detail section
    TQDomNamedNodeMap attributes = report->attributes();

    // Get the report detail attributes
    MReportDetail *detail = new MReportDetail;
    int height = scaleDeltaHeight( attributes.namedItem( "Height" ).nodeValue().toInt() );
    heightOfDetails += height;
    detail->setHeight( height );
    detail->setLevel( attributes.namedItem( "Level" ).nodeValue().toInt() );
    detail->setRepeat( attributes.namedItem( "Repeat" ).nodeValue() == "true" );

    // Process the report detail labels
    TQDomNodeList children = report->childNodes();
    int childCount = children.length();

    for ( int j = 0; j < childCount; j++ )
    {
        TQDomNode child = children.item( j );
        if ( child.nodeType() == TQDomNode::ElementNode )
        {
            if ( child.nodeName() == "Line" )
            {
                TQDomNamedNodeMap attributes = child.attributes();
                MLineObject* line = new MLineObject();
                setLineAttributes( line, &attributes );
                detail->addLine( line );
            }
            else if ( child.nodeName() == "Label" )
            {
                TQDomNamedNodeMap attributes = child.attributes();
                MLabelObject* label = new MLabelObject();
                setLabelAttributes( label, &attributes );
                detail->addLabel( label );
            }
            else if ( child.nodeName() == "Special" )
            {
                TQDomNamedNodeMap attributes = child.attributes();
                MSpecialObject* field = new MSpecialObject();
                setSpecialAttributes( field, &attributes );
                detail->addSpecialField( field );
            }
            else if ( child.nodeName() == "Field" )
            {
                TQDomNamedNodeMap attributes = child.attributes();
                MFieldObject* field = new MFieldObject();
                setFieldAttributes( field, &attributes );
                detail->addField( field );
            }
        }
    }
    // Append a newly created detail to the list
    details.append( detail );
}

/** Sets a line's layout attributes */
void MReportEngine::setLineAttributes( MLineObject* line, TQDomNamedNodeMap* attr )
{
    line->setLine( scaleDeltaWidth( attr->namedItem( "X1" ).nodeValue().toInt() ),
                   scaleDeltaHeight( attr->namedItem( "Y1" ).nodeValue().toInt() ),
                   scaleDeltaWidth( attr->namedItem( "X2" ).nodeValue().toInt() ),
                   scaleDeltaHeight( attr->namedItem( "Y2" ).nodeValue().toInt() ) );

    TQString tmp = attr->namedItem( "Color" ).nodeValue();

    line->setColor( tmp.left( tmp.find( "," ) ).toInt(),
                    tmp.mid( tmp.find( "," ) + 1, ( tmp.findRev( "," ) - tmp.find( "," ) ) - 1 ).toInt(),
                    tmp.right( tmp.length() - tmp.findRev( "," ) - 1 ).toInt() );

    line->setWidth( attr->namedItem( "Width" ).nodeValue().toInt() );
    line->setStyle( attr->namedItem( "Style" ).nodeValue().toInt() );
}

/** Sets a label's layout attributes */
void MReportEngine::setLabelAttributes( MLabelObject* label, TQDomNamedNodeMap* attr )
{
    TQString tmp;

    label->setText( attr->namedItem( "Text" ).nodeValue() );
    label->setGeometry( scaleDeltaWidth( attr->namedItem( "X" ).nodeValue().toInt() ),
                        scaleDeltaHeight( attr->namedItem( "Y" ).nodeValue().toInt() ),
                        scaleDeltaWidth( attr->namedItem( "Width" ).nodeValue().toInt() ),
                        scaleDeltaHeight( attr->namedItem( "Height" ).nodeValue().toInt() ) );

    tmp = attr->namedItem( "BackgroundColor" ).nodeValue();
    label->setBackgroundColor( tmp.left( tmp.find( "," ) ).toInt(),
                               tmp.mid( tmp.find( "," ) + 1, ( tmp.findRev( "," ) - tmp.find( "," ) ) - 1 ).toInt(),
                               tmp.right( tmp.length() - tmp.findRev( "," ) - 1 ).toInt() );

    tmp = attr->namedItem( "ForegroundColor" ).nodeValue();
    label->setForegroundColor( tmp.left( tmp.find( "," ) ).toInt(),
                               tmp.mid( tmp.find( "," ) + 1, ( tmp.findRev( "," ) - tmp.find( "," ) ) - 1 ).toInt(),
                               tmp.right( tmp.length() - tmp.findRev( "," ) - 1 ).toInt() );

    tmp = attr->namedItem( "BorderColor" ).nodeValue();
    label->setBorderColor( tmp.left( tmp.find( "," ) ).toInt(),
                           tmp.mid( tmp.find( "," ) + 1, ( tmp.findRev( "," ) - tmp.find( "," ) ) - 1 ).toInt(),
                           tmp.right( tmp.length() - tmp.findRev( "," ) - 1 ).toInt() );

    label->setBorderWidth( attr->namedItem( "BorderWidth" ).nodeValue().toInt() );
    label->setBorderStyle( attr->namedItem( "BorderStyle" ).nodeValue().toInt() );
    if ( !attr->namedItem( "DrawBottom" ).isNull() )
        label->setDrawBottom( attr->namedItem( "DrawBottom" ).nodeValue() == "true" );
    if ( !attr->namedItem( "DrawTop" ).isNull() )
        label->setDrawTop( attr->namedItem( "DrawTop" ).nodeValue() == "true" );
    if ( !attr->namedItem( "DrawLeft" ).isNull() )
        label->setDrawLeft( attr->namedItem( "DrawLeft" ).nodeValue() == "true" );
    if ( !attr->namedItem( "DrawRight" ).isNull() )
        label->setDrawRight( attr->namedItem( "DrawRight" ).nodeValue() == "true" );
    label->setFont( attr->namedItem( "FontFamily" ).nodeValue(),
                    attr->namedItem( "FontSize" ).nodeValue().toInt(),
                    attr->namedItem( "FontWeight" ).nodeValue().toInt(),
                    ( attr->namedItem( "FontItalic" ).nodeValue().toInt() == 0 ? false : true ) );
    label->setHorizontalAlignment( attr->namedItem( "HAlignment" ).nodeValue().toInt() );
    label->setVerticalAlignment( attr->namedItem( "VAlignment" ).nodeValue().toInt() );
    label->setWordWrap( attr->namedItem( "WordWrap" ).nodeValue().toInt() == 0 ? false : true );
}

/** Sets a special field's layout attributes */
void MReportEngine::setSpecialAttributes( MSpecialObject* field, TQDomNamedNodeMap* attr )
{
    field->setType( attr->namedItem( "Type" ).nodeValue().toInt() );
    field->setDateFormat( attr->namedItem( "DateFormat" ).nodeValue().toInt() );

    setLabelAttributes( ( MLabelObject * ) field, attr );
}

/** Sets a field's layout attributes */
void MReportEngine::setFieldAttributes( MFieldObject* field, TQDomNamedNodeMap* attr )
{
    field->setFieldName( attr->namedItem( "Field" ).nodeValue() );
    field->setDataType( attr->namedItem( "DataType" ).nodeValue().toInt() );
    field->setDateFormat( attr->namedItem( "DateFormat" ).nodeValue().toInt() );
    field->setPrecision( attr->namedItem( "Precision" ).nodeValue().toInt() );
    field->setCurrency( attr->namedItem( "Currency" ).nodeValue().toInt() );
    field->setCommaSeparator( attr->namedItem( "CommaSeparator" ).nodeValue().toInt() );
    field->setInputMask( attr->namedItem( "InputMask" ).nodeValue() );

    TQString tmp = attr->namedItem( "NegValueColor" ).nodeValue();

    field->setNegValueColor( tmp.left( tmp.find( "," ) ).toInt(),
                             tmp.mid( tmp.find( "," ) + 1, ( tmp.findRev( "," ) - tmp.find( "," ) ) - 1 ).toInt(),
                             tmp.right( tmp.length() - tmp.findRev( "," ) - 1 ).toInt() );

    setLabelAttributes( ( MLabelObject * ) field, attr );
}

/** Sets a calculated field's layout attributes */
void MReportEngine::setCalculatedFieldAttributes( MCalcObject* field, TQDomNamedNodeMap* attr )
{
    field->setCalculationType( attr->namedItem( "CalculationType" ).nodeValue().toInt() );
    setFieldAttributes( ( MFieldObject * ) field, attr );
}

/** Copies member data from one object to another.
  Used by the copy constructor and assignment operator */
void MReportEngine::copy( const MReportEngine* mReportEngine )
{
    // Copy document data
    m_refCount = 1;
    rd = mReportEngine->rd;
    rt = mReportEngine->rt;

    // Copy page metrics
    pageSize = mReportEngine->pageSize;
    pageOrientation = mReportEngine->pageOrientation;
    topMargin = mReportEngine->topMargin;
    bottomMargin = mReportEngine->bottomMargin;
    leftMargin = mReportEngine->leftMargin;
    rightMargin = mReportEngine->rightMargin;
    pageWidth = mReportEngine->pageWidth;
    pageHeight = mReportEngine->pageHeight;
    heightOfDetails = mReportEngine->heightOfDetails;

    // Copy the report header
    rHeader = mReportEngine->rHeader;
    // Copy the page header
    pHeader = mReportEngine->pHeader;
    // Copy the detail sections
    MReportDetail *detail;
    TQPtrList<MReportDetail> temp = mReportEngine->details;
    temp.setAutoDelete( false );
    for ( detail = temp.first(); detail; detail = temp.next() )
    {
        MReportDetail * new_detail = new MReportDetail;
        *new_detail = *detail;
        details.append( new_detail );
    }
    // Copy the page footer
    pFooter = mReportEngine->pFooter;
    // Copy the report footer
    rFooter = mReportEngine->rFooter;

    // Copy the rendering state
    currY = mReportEngine->currY;
    currHeight = mReportEngine->currHeight;
    currPage = mReportEngine->currPage;
    currDate = mReportEngine->currDate;
    cancelRender = mReportEngine->cancelRender;

    // Copy grand totals list
    grandTotal = mReportEngine->grandTotal;
    m_pageCollection = mReportEngine->m_pageCollection;
    m_needRegeneration = mReportEngine->m_needRegeneration;
    if ( m_pageCollection )
        m_pageCollection->addRef();
}

}

#include "mreportengine.moc"
