#include"mailsubject.h"

#include <kmime_codecs.h>
#include <kcharsets.h>
#include <kdebug.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <tqdatetime.h>
#include <tqtextcodec.h>
#include <ctype.h>

KornMailSubject::KornMailSubject() : _id(0), _drop(0), _size(-1), _date(-1), _fullMessage(false)
{
}

KornMailSubject::KornMailSubject(KornMailId * id, KMailDrop *drop)
	: _id(id), _drop( drop ), _size(-1), _date(-1), _fullMessage(false)
{
}

KornMailSubject::KornMailSubject(const KornMailSubject & src)
	: _id(0), _drop(0), _size(-1), _date(-1)
{
	operator=(src);
}

KornMailSubject & KornMailSubject::operator= (const KornMailSubject & src)
{
	_size = src._size;
	_date = src._date;
	_subject = src._subject;
	_sender = src._sender;
	_header = src._header;
	_fullMessage = src._fullMessage;
	if (_id)
		delete _id;
	_id = 0;
	if (src._id)
		_id = src._id->clone();
	_drop = src._drop;
	return *this;
}

KornMailSubject::~KornMailSubject()
{
	if (_id)
		delete _id;
	_id = 0;
}

TQString KornMailSubject::toString() const
{
	TQDateTime date;
	date.setTime_t(_date);
	return TQString("KornMailSubject, Id: ") + (_id?_id->toString():TQString("NULL")) + ", " + i18n("Subject:") + " " + _subject
		+ ", " + i18n("Sender:") + " " + _sender + ", " + i18n("Size:") + " " + TQString::number(_size)
		+ ", " + i18n("Date:") + " " + date.toString(Qt::ISODate);
}

TQString KornMailSubject::decodeRFC2047String(const TQCString& aStr)
{
	if ( aStr.isEmpty() )
		return TQString();

	const TQCString str = unfold( aStr );

	if ( str.isEmpty() )
		return TQString();

	if ( str.find( "=?" ) < 0 ) {
		// No need to decode
		return TQString(str);
	}

	TQString result;
	TQCString LWSP_buffer;
	bool lastWasEncodedWord = false;

	for ( const char * pos = str.data() ; *pos ; ++pos ) {
		// collect LWSP after encoded-words,
		// because we might need to throw it out
		// (when the next word is an encoded-word)
		if ( lastWasEncodedWord && isBlank( pos[0] ) ) {
			LWSP_buffer += pos[0];
			continue;
		}
		// verbatimly copy normal text
		if (pos[0]!='=' || pos[1]!='?') {
			result += LWSP_buffer + pos[0];
			LWSP_buffer = 0;
			lastWasEncodedWord = false;
			continue;
		}
		// found possible encoded-word
		const char * const beg = pos;
		{
			// parse charset name
			TQCString charset;
			int i = 2;
			pos += 2;
			for ( ; *pos != '?' && ( *pos==' ' || ispunct(*pos) || isalnum(*pos) ); ++i, ++pos ) {
				charset += *pos;
			}
			if ( *pos!='?' || i<4 )
				goto invalid_encoded_word;

			// get encoding and check delimiting question marks
			const char encoding[2] = { pos[1], '\0' };
			if (pos[2]!='?' || (encoding[0]!='Q' && encoding[0]!='q' &&
			    encoding[0]!='B' && encoding[0]!='b'))
				goto invalid_encoded_word;
			pos+=3; i+=3; // skip ?x?
			const char * enc_start = pos;
			// search for end of encoded part
			while ( *pos && !(*pos=='?' && *(pos+1)=='=') ) {
				i++;
				pos++;
			}
			if ( !*pos )
				goto invalid_encoded_word;

			// valid encoding: decode and throw away separating LWSP
			const KMime::Codec * c = KMime::Codec::codecForName( encoding );
			kdFatal( !c ) << "No \"" << encoding << "\" codec!?" << endl;

			TQByteArray in; in.setRawData( enc_start, pos - enc_start );
			const TQByteArray enc = c->decode( in );
			in.resetRawData( enc_start, pos - enc_start );

			const TQTextCodec * codec = codecForName(charset);
			if (!codec) return TQString();
			result += codec->toUnicode(enc);
			lastWasEncodedWord = true;

			++pos; // eat '?' (for loop eats '=')
			LWSP_buffer = 0;
		}
		continue;
invalid_encoded_word:
		// invalid encoding, keep separating LWSP.
		pos = beg;
		if ( !LWSP_buffer.isNull() )
		result += LWSP_buffer;
		result += "=?";
		lastWasEncodedWord = false;
		++pos; // eat '?' (for loop eats '=')
		LWSP_buffer = 0;
	}
	return result;
}

TQCString KornMailSubject::unfold( const TQCString & header )
{
	if ( header.isEmpty() )
		return TQCString();

	TQCString result( header.size() ); // size() >= length()+1 and size() is O(1)
	char * d = result.data();

	for ( const char * s = header.data() ; *s ; )
		if ( *s == '\r' ) { // ignore
			++s;
			continue;
		} else if ( *s == '\n' ) { // unfold
			while ( this->isBlank( *++s ) );
			*d++ = ' ';
		} else
			*d++ = *s++;

	*d++ = '\0';

	result.truncate( d - result.data() );
	return result;
}

//-----------------------------------------------------------------------------
const TQTextCodec* KornMailSubject::codecForName(const TQCString& _str)
{
	if (_str.isEmpty()) return 0;
		TQCString codec = _str;
	return TDEGlobal::charsets()->codecForName(codec);
}

void KornMailSubject::decodeHeaders()
{
	_subject = decodeRFC2047String( _subject.latin1() );
	_sender = decodeRFC2047String( _sender.latin1() );
}