
#include "bugdetails.h"

#include "bugdetailsimpl.h"
#include <tqstringlist.h>
#include <kdebug.h>
#include <kmdcodec.h>
#include <tdemessagebox.h>
#include <tqregexp.h>

BugDetails::BugDetails()
{
}

BugDetails::BugDetails( BugDetailsImpl *impl ) :
    m_impl( impl )
{
}

BugDetails::BugDetails( const BugDetails &other )
{
    (*this) = other;
}

BugDetails &BugDetails::operator=( const BugDetails &rhs )
{
    m_impl = rhs.m_impl;
    return *this;
}

BugDetails::~BugDetails()
{
}

TQString BugDetails::version() const
{
    if ( !m_impl )
        return TQString();

    return m_impl->version;
}

TQString BugDetails::source() const
{
    if ( !m_impl )
        return TQString();

    return m_impl->source;
}

TQString BugDetails::compiler() const
{
    if ( !m_impl )
        return TQString();

    return m_impl->compiler;
}

TQString BugDetails::os() const
{
    if ( !m_impl )
        return TQString();

    return m_impl->os;
}

TQDateTime BugDetails::submissionDate() const
{
    if ( !m_impl ) return TQDateTime();

    if ( m_impl->parts.count() > 0 ) {
        return m_impl->parts.last().date;
    }
    return TQDateTime();
}

int BugDetails::age() const
{
   if ( !m_impl )
       return 0;

   return submissionDate().daysTo( TQDateTime::currentDateTime() );
}

BugDetailsPart::List BugDetails::parts() const
{
    if ( !m_impl )
        return BugDetailsPart::List();

    return m_impl->parts;
}

TQValueList<BugDetailsImpl::AttachmentDetails> BugDetails::attachmentDetails() const
{
    if ( m_impl )
        return m_impl->attachments;
    else
        return TQValueList<BugDetailsImpl::AttachmentDetails>();
}

void BugDetails::addAttachmentDetails( const TQValueList<BugDetailsImpl::AttachmentDetails>& attch )
{
    if ( m_impl )
        m_impl->attachments =  attch;
}

TQValueList<BugDetails::Attachment> BugDetails::extractAttachments() const
{
    TQValueList<BugDetails::Attachment> lst;
    if ( !m_impl )
        return lst;
    BugDetailsPart::List parts = m_impl->parts;
    for ( BugDetailsPart::List::ConstIterator it = parts.begin(); it != parts.end(); ++it ) {
        lst += extractAttachments( (*it).text );
    }
    return lst;
}

//#define DEBUG_EXTRACT

TQValueList<BugDetails::Attachment> BugDetails::extractAttachments( const TQString& text )
{
    TQValueList<BugDetails::Attachment> lst;
    TQStringList lines = TQStringList::split( '\n', text );
#ifdef DEBUG_EXTRACT
    kdDebug() << k_funcinfo << lines.count() << " lines." << endl;
#endif
    TQString boundary;
    for ( TQStringList::Iterator it = lines.begin() ; it != lines.end() ; ++it )
    {
#ifdef DEBUG_EXTRACT
        kdDebug() << "Line: " << *it << endl;
#endif
        if ( (*it).startsWith( " Content-Type" ) ) // ## leading space comes from tdehtml
        {
#ifdef DEBUG_EXTRACT
            //kdDebug() << "BugDetails::extractAttachments going back, looking for empty or boundary=" << boundary << endl;
#endif
            // Rewind until last empty line
            TQStringList::Iterator rit = it;
            for ( ; rit != lines.begin() ; --rit ) {
                TQString line = *rit;
                if ( line.endsWith( "<br />" ) )
                    line = line.left( line.length() - 6 );
                while ( !line.isEmpty() && line[0] == ' ' )
                    line.remove( 0, 1 );
#ifdef DEBUG_EXTRACT
                kdDebug() << "Back: '" << line << "'" << endl;
#endif
                if ( line.isEmpty() ) {
                    ++rit; // boundary is next line
                    boundary = *rit;
                    if ( boundary.endsWith( "<br />" ) )
                        boundary = boundary.left( boundary.length() - 6 );
                    if ( boundary[0] == ' ' )
                        boundary.remove( 0, 1 );
                    kdDebug() << "BugDetails::extractAttachments boundary=" << boundary << endl;
                    break;
                }
                if ( line == boundary )
                    break;
            }
            // Forward until next empty line (end of headers) - and parse filename
            TQString filename;
            TQString encoding;
            rit = it;
            for ( ; rit != lines.end() ; ++rit ) {
                TQString header = *rit;
                if ( header.endsWith( "<br />" ) )
                    header = header.left( header.length() - 6 );
                if ( header[0] == ' ' )
                    header.remove( 0, 1 );
#ifdef DEBUG_EXTRACT
                kdDebug() << "Header: '" << header << "'" << endl;
#endif
                if ( header.isEmpty() )
                    break;
                if ( header.startsWith( "Content-Disposition:" ) )
                {
#ifdef DEBUG_EXTRACT
                    kdDebug() << "BugDetails::extractAttachments found header " << *rit << endl;
#endif
                    // Taken from libtdenetwork/kmime_headers.cpp
                    int pos=header.find("filename=", 0, false);
                    TQString fn;
                    if(pos>-1) {
                        pos+=9;
                        fn=header.mid(pos, header.length()-pos);
                        if ( fn.startsWith( "\"" ) && fn.endsWith( "\"" ) )
                            fn = fn.mid( 1, fn.length() - 2 );
                        //filename=decodeRFC2047String(fn, &e_ncCS, defaultCS(), forceCS());
                        filename = fn; // hack
                        kdDebug() << "BugDetails::extractAttachments filename=" << filename << endl;
                    }

                }
                else if ( header.startsWith( "Content-Transfer-Encoding:" ) )
                {
                    encoding = header.mid( 26 ).lower();
                    while ( encoding[0] == ' ' )
                        encoding.remove( 0, 1 );
                    kdDebug() << "BugDetails::extractAttachments encoding=" << encoding << endl;
                }
            } //for
            if ( rit == lines.end() )
                break;

            it = rit;
            if ( rit != lines.end() && !filename.isEmpty() )
            {
                ++it;
                if ( it == lines.end() )
                    break;
                // Read encoded contents
                TQString contents;
                for ( ; it != lines.end() ; ++it )
                {
                    TQString line = *it;
                    if ( line.endsWith( "</tt>" ) )
                        line = line.left( line.length() - 5 );
                    if ( line.endsWith( "<br />" ) ) // necessary for the boundary check
                        line = line.left( line.length() - 6 );
                    while ( !line.isEmpty() && line[0] == ' ' )
                        line.remove( 0, 1 );
                    if ( line.isEmpty() || line == boundary ) // end of attachment
                        break;
                    if ( line == boundary+"--" ) // end of last attachment
                    {
                        boundary = TQString();
                        break;
                    }
                    contents += line; // no newline, because of linebreaking between <br and />
                }
                contents = contents.replace( TQRegExp("<br */>"), TQString() );
#ifdef DEBUG_EXTRACT
                kdDebug() << "BugDetails::extractAttachments contents=***\n" << contents << "\n***" << endl;
#endif
                kdDebug() << "BugDetails::extractAttachments creating attachment " << filename << endl;
                Attachment a;
                if ( encoding == "base64" )
                    KCodecs::base64Decode( contents.local8Bit(), a.contents /*out*/ );
                else
                    //KCodecs::uudecode( contents.local8Bit(), a.contents /*out*/ );
                    KMessageBox::information( 0, i18n("Attachment %1 could not be decoded.\nEncoding: %2").arg(filename).arg(encoding) );
#ifdef DEBUG_EXTRACT
                kdDebug() << "Result: ***\n" << TQCString( a.contents.data(), a.contents.size()+1 ) << "\n*+*" << endl;
#endif
                a.filename = filename;
                lst.append(a);
                if ( it == lines.end() )
                    break;
            }
        }
    }
#ifdef DEBUG_EXTRACT
    kdDebug() << "BugDetails::extractAttachments returning " << lst.count() << " attachments for this part." << endl;
#endif
    return lst;
}

bool BugDetails::operator==( const BugDetails &rhs )
{
    return m_impl == rhs.m_impl;
}
