/***************************************************************************
    dcfilelistdecompressor.cpp  -  Valknut Filelist Processing Thread
                             -------------------
    begin                : Thu Jul 3 2008
    copyright            : (C) 2008 by Edward Sheldrake
    email                : ejs1920@yahoo.co.uk
 ***************************************************************************/

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

#include "dcfilelistdecompressor.h"

// dclib classes not QT classes are used to decompress the filelist...
#include <dclib/core/cbytearray.h>
#include <dclib/core/che3.h>
#include <dclib/core/cbz.h>
#include <dclib/core/ciconv.h>

// ...except processing and old valknut filelist uses QT classes
#include <QByteArray>
#include <QString>
#include <QFile>

// for attempting to undo what dclib <= 0.3.13 did to downloaded text lists
#include <dclib/core/cxml.h>

#include "dcconfig.h"

/** */
DCFilelistDecompressor::DCFilelistDecompressor( QObject * parent, CString file ) : QThread( parent )
{
	filename = file;
	data = 0;
	compressed = 0;
	recompressed = 0;
}

/** */
DCFilelistDecompressor::DCFilelistDecompressor( QObject * parent, CByteArray * memory ) : QThread( parent )
{
	data = 0;
	compressed = memory;
	recompressed = 0;
}

/** */
DCFilelistDecompressor::~DCFilelistDecompressor()
{
	/* It is DCFileBrowsers's responsibility to delete things */
	if ( isRunning() )
	{
		printf("~DCFilelistDecompressor: waiting for thread to finish\n");
		wait();
	}
}

/** */
CString * DCFilelistDecompressor::getData()
{
	return data;
}

/** */
void DCFilelistDecompressor::run()
{
	bool ok = false;
	bool oldvalknut = false;
	CByteArray out;
	
	if ( filename.NotEmpty() )
	{
		compressed = new CByteArray();
		if ( (compressed->LoadFromFile(filename) == false) || (compressed->Size() == 0) )
		{
			delete compressed;
			compressed = 0;
			emit finished( false );
			return;
		}
		
		// enable saving of lists...
		recompressed = compressed;
	}
	
	CString test;
	
	// check for uncompressed xml
	if ( compressed->Size() > 10 )
	{
		test.Set( (const char*)compressed->Data(), 9 );
	}
	
	if ( test.Find("<?xml") != -1 )
	{
		data = new CString();
		data->Set( (const char*)compressed->Data(), compressed->Size() );
		
		// create compressed data to save
		recompressed = new CByteArray();
		CBZ::Compress(compressed,recompressed);
		
		if ( filename.NotEmpty() )
		{
			delete compressed;
			compressed = 0;
		}
		
		ok = true;
		
		emit finished( ok );
		
		return;
	}
	
	if ( compressed->Size() > 3 )
	{
		test.Set( (const char*)compressed->Data(), 2 );
	}
	else
	{
		test.Empty();
	}
	
	if ( test == "BZ" )
	{
		if ( CBZ::Decompress( compressed, &out ) )
		{
			ok = true;
		}
	}
	else
	{
		if ( compressed->Size() > 4 )
		{
			test.Set( (const char*)compressed->Data(), 3 );
		}
		else
		{
			test.Empty();
		}
		
		if ( test == "HE3" )
		{
			CHE3 * he3 = new CHE3();
			
			CString * s = he3->decode_he3_data( compressed );
			
			if ( s != 0 )
			{
				out.Append( s->Data(), s->Length() );
				delete s;
				ok = true;
			}
			
			delete he3;
		}
		else
		{
			if ( compressed->Size() > 20 )
			{
				test.Set( (const char*)compressed->Data(), 18 );
			}
			else
			{
				test.Empty();
			}
			
			if ( test == "---HEADER START---" )
			{
				if ( LoadOldValknutList( &out ) )
				{
					if ( out.Size() > 0 )
					{
						oldvalknut = true;
						ok = true;
					}
				}
				delete compressed;
				compressed = 0;
			}
		}
	}
	
	if ( (ok == false) || (out.Size() == 0) )
	{
		if ( (compressed != 0) && (compressed == recompressed) )
		{
			delete compressed;
			compressed = 0;
			recompressed = 0;
		}
		
		emit finished( false );
		return;
	}
	
	data = new CString();
	
	// there may be a byte order mark which is up to 4 bytes at the start of the file
	if ( out.Size() > 10 )
	{
		test.Set( (const char*)out.Data(), 9 );
	}
	else
	{
		test.Empty();
	}
	
	if ( test.Find("<?xml") != -1 )
	{
		data->Set( (const char*)out.Data(), out.Size() );
		
		if ( oldvalknut )
		{
			recompressed = new CByteArray();
			CBZ::Compress(&out,recompressed);
		}
	}
	else
	{
		/* must NULL terminate the CByteArray */
		if ( out.Realloc(out.Size()+1) )
		{
			out.Data()[out.Size()-1] = 0;
			
			if ( oldvalknut )
			{
				*data = CXml().FromUtf8( (const char*)out.Data() );
				
				CIconv * to_remote = new CIconv( g_pConfig->GetLocalEncoding(), g_pConfig->GetRemoteEncoding() );
				
				test = to_remote->encode( *data );
				
				delete to_remote;
				
				recompressed = new CByteArray();
				
				out.SetSize(0);
				out.Append(test.Data(),test.Length());
				
				CBZ::Compress(&out,recompressed);
			}
			else
			{
				CIconv * ciconv = new CIconv( g_pConfig->GetRemoteEncoding(), g_pConfig->GetLocalEncoding() );
				*data = ciconv->encode( (const char*)out.Data() );
				delete ciconv;
			}
		}
	}
	
	emit finished( ok );
}

/** */
bool DCFilelistDecompressor::LoadOldValknutList( CByteArray * dest )
{
	bool res = false;
	
	if ( !dest )
	{
		return res;
	}
	
	QFile file( filename.Data() );

	if ( file.open( IO_ReadOnly ) )
	{
		QString line = QString::fromUtf8(file.readLine(255));
		
		if ( line == "---HEADER START---\n" )
		{
			// read header
			while ( !(line.isEmpty()) && line!="---HEADER END---\n" )
			{
				line = QString::fromUtf8(file.readLine(255));

#if 0
				if ( line.startsWith("TYPE=") )
				{
					m_sType = line.mid(5).simplified();
					if ( m_sType == "XMLFILELIST" )
					{
						m_sType = "xml";
					}
					else
					{
						m_sType = "text";
					}
				}
				else if ( line.startsWith("NICK=") )
				{
					m_sNick = line.mid(5).simplified();
				}
				else if ( line.startsWith("HUBNAME=") )
				{
					m_sHubName = line.mid(8).simplified();
				}
				else if ( line.startsWith("HUBHOST=") )
				{
					m_sHubHost = line.mid(8).simplified();
				}
#endif
			}

			QByteArray ba = file.readAll();
			
			if ( ba.size() > 0 )
			{
				dest->Append( ba.constData(), ba.size() );
				res = true;
			}
		}

        	file.close();
	}
	
	return res;
}
