/***************************************************************************
    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>
#include <dclib/core/cxml.h>
#include <dclib/cfilemanager.h>

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

#include "dcconfig.h"

/** */
DCFilelistDecompressor::DCFilelistDecompressor( QObject * parent, CString file ) : QThread( parent )
{
	filename = file;
	
	compressed = NULL;
	xmllist = NULL;
	textlist = NULL;
}

/** */
DCFilelistDecompressor::~DCFilelistDecompressor()
{
	if ( isRunning() )
	{
		wait();
	}
	
	delete xmllist;
	delete textlist;
	delete compressed;
}

/** */
CXml * DCFilelistDecompressor::takeXml()
{
	CXml * x = NULL;
	
	if ( !isRunning() )
	{
		x = xmllist;
		xmllist = NULL;
	}
	
	return x;
}

/** */
CString * DCFilelistDecompressor::takeText()
{
	CString * s = NULL;
	
	if ( !isRunning() )
	{
		s = textlist;
		textlist = NULL;
	}
	
	return s;
}

/** */
CByteArray * DCFilelistDecompressor::takeCompressed()
{
	CByteArray * b = NULL;
	
	if ( !isRunning() )
	{
		b = compressed;
		compressed = NULL;
	}
	
	return b;
}

/** */
CString DCFilelistDecompressor::getFileName()
{
	return filename;
}

/** */
void DCFilelistDecompressor::run()
{
	bool ok = false;
	bool oldvalknut = false;
	CByteArray out;
	CString test;
	
	compressed = new CByteArray();
	if ( filename.IsEmpty() )
	{
		if ( !CFileManager::Instance() )
		{
			delete compressed;
			compressed = NULL;
			return;
		}
		
		if ( CFileManager::Instance()->GetShareBuffer( esbtXMLBZ, compressed, false ) == 0 )
		{
			filename = "myfiles.xml.bz2";
		}
		else if ( CFileManager::Instance()->GetShareBuffer( esbtBZ, compressed, false ) == 0 )
		{
			filename = "myfiles.txt.bz2";
		}
		else if ( CFileManager::Instance()->GetShareBuffer( esbtHE3, compressed, false ) == 0 )
		{
			filename = "myfiles.DcLst";
		}
		else
		{
			delete compressed;
			compressed = NULL;
			return;
		}
	}
	else
	{
		if ( (compressed->LoadFromFile(filename) == false) || (compressed->Size() == 0) )
		{
			delete compressed;
			compressed = NULL;
			return;
		}
	}
	
	// check for uncompressed xml (allowing for byte order mark)
	if ( compressed->Size() > 10 )
	{
		test.Set( (const char*)compressed->Data(), 9 );
	}
	
	if ( test.Find("<?xml") != -1 )
	{
		xmllist = new CXml();
		if ( xmllist->ParseMemory((const char*)compressed->Data(), compressed->Size()) && xmllist->DocFirstChild() )
		{
			// create compressed data to save
			CByteArray * recompressed = new CByteArray();
			CBZ::Compress(compressed,recompressed);
			
			delete compressed;
			compressed = recompressed;
			
			if ( filename.Find(".xml") == -1 )
			{
				filename.Append(".xml");
			}
			filename.Append(".bz2");
		}
		else
		{
			delete xmllist;
			xmllist = NULL;
			delete compressed;
			compressed = NULL;
		}
		
		return;
	}
	
	if ( compressed->Size() > 3 )
	{
		test.Set( (const char*)compressed->Data(), 2 );
	}
	else
	{
		test.Empty();
	}
	
	if ( test == "BZ" )
	{
		ok = CBZ::Decompress( compressed, &out );
	}
	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 != NULL )
			{
				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;
					}
				}
			}
		}
	}
	
	if ( (ok == false) || (out.Size() == 0) )
	{
		delete compressed;
		compressed = NULL;
		
		return;
	}
	
	// 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 )
	{
		xmllist = new CXml();
		
		if ( xmllist->ParseMemory((const char*)out.Data(),out.Size()) && xmllist->DocFirstChild() )
		{
			if ( oldvalknut )
			{
				delete compressed;
				compressed = new CByteArray();
				CBZ::Compress(&out,compressed);
				
				if ( filename.Find(".xml") == -1 )
				{
					filename.Append(".xml");
				}
				filename.Append(".bz2");
			}
		}
		else
		{
			delete xmllist;
			xmllist = NULL;
		}
	}
	else
	{
		textlist = new CString();
		
		/* must NULL terminate the CByteArray */
		if ( out.Realloc(out.Size()+1) )
		{
			out.Data()[out.Size()-1] = 0;
			
			if ( oldvalknut )
			{
				*textlist = CXml().FromUtf8( (const char*)out.Data() );
				
				CIconv * to_remote = new CIconv( g_pConfig->GetLocalEncoding(), g_pConfig->GetRemoteEncoding() );
				
				test = to_remote->encode( *textlist );
				
				delete to_remote;
				
				delete compressed;
				compressed = new CByteArray();
				
				out.SetSize(0);
				out.Append(test.Data(),test.Length());
				
				CBZ::Compress(&out,compressed);
				
				/* hopefully name.filelist -> name.filelist.bz2 */
				filename.Append(".bz2");
			}
			else
			{
				CIconv * ciconv = new CIconv( g_pConfig->GetRemoteEncoding(), g_pConfig->GetLocalEncoding() );
				*textlist = ciconv->encode( (const char*)out.Data() );
				delete ciconv;
			}
		}
	}
}

/** */
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;
}
