/***************************************************************************
    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(DCConfig::Instance()->GetLocalEncoding(), DCConfig::Instance()->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(DCConfig::Instance()->GetRemoteEncoding(), DCConfig::Instance()->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;
}
