/*
 * zip.cpp - zip/unzip files
 * Copyright (C) 2003  Justin Karneges
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */

#include <QString>
#include <QStringList>
#include <QFile>

#ifdef PSIMINIZIP
#include "minizip/unzip.h"
#else
#include <unzip.h>
#endif
#include "zip.h"

class UnZipPrivate
{
public:
    UnZipPrivate() : caseSensitive(UnZip::CS_Default) {}

    QString name;
    unzFile uf;
    QStringList listing;
    UnZip::CaseSensitivity caseSensitive;
};

UnZip::UnZip(const QString &name)
{
    d = new UnZipPrivate;
    d->uf = 0;
    d->name = name;
}

UnZip::UnZip(UnZip &&o) :
    d(o.d)
{
    o.d = nullptr;
}

UnZip::~UnZip()
{
    if (d) {
        close();
        delete d;
    }
}

void UnZip::setCaseSensitivity(CaseSensitivity state)
{
    d->caseSensitive = state;
}

void UnZip::setName(const QString &name)
{
    d->name = name;
}

const QString & UnZip::name() const
{
    return d->name;
}

bool UnZip::open()
{
    close();

    d->uf = unzOpen(QFile::encodeName(d->name).data());
    if(!d->uf)
        return false;

    return getList();
}

void UnZip::close()
{
    if(d->uf) {
        unzClose(d->uf);
        d->uf = 0;
    }

    d->listing.clear();
}

const QStringList & UnZip::list() const
{
    return d->listing;
}

bool UnZip::getList()
{
    unz_global_info gi;
    int err = unzGetGlobalInfo(d->uf, &gi);
    if(err != UNZ_OK)
        return false;

    QStringList l;
    for(int n = 0; n < (int)gi.number_entry; ++n) {
        char filename_inzip[256];
        unz_file_info file_info;
        int err = unzGetCurrentFileInfo(d->uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
        if(err != UNZ_OK)
            return false;

        l += filename_inzip;

        if((n+1) < (int)gi.number_entry) {
            err = unzGoToNextFile(d->uf);
            if(err != UNZ_OK)
                return false;
        }
    }

    d->listing = l;

    return true;
}

bool UnZip::readFile(const QString &fname, QByteArray *buf, int max)
{
    int err = unzLocateFile(d->uf, QFile::encodeName(fname).data(), d->caseSensitive);
    if(err != UNZ_OK)
        return false;

    char filename_inzip[256];
    unz_file_info file_info;
    err = unzGetCurrentFileInfo(d->uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
    if(err != UNZ_OK)
        return false;

    err = unzOpenCurrentFile(d->uf);
    if(err != UNZ_OK)
        return false;

    QByteArray a;
    QByteArray chunk;
    chunk.resize(16384);
    while(1) {
        err = unzReadCurrentFile(d->uf, chunk.data(), chunk.size());
        if(err < 0) {
            unzCloseCurrentFile(d->uf);
            return false;
        }
        if(err == 0)
            break;

        int oldsize = a.size();
        if(max > 0 && oldsize + err > max)
            err = max - oldsize;
        a.resize(oldsize + err);
        memcpy(a.data() + oldsize, chunk.data(), err);

        if(max > 0 && (int)a.size() >= max)
            break;
    }

    err = unzCloseCurrentFile(d->uf);
    if(err != UNZ_OK)
        return false;

    *buf = a;
    return true;
}

bool UnZip::fileExists(const QString &fname)
{
    int err = unzLocateFile(d->uf, QFile::encodeName(fname).data(), d->caseSensitive);
    return err == UNZ_OK;
}
