#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <archive.h>
#include <archive_entry.h>
#include "ppport.h"

int DEBUG = 0;

typedef struct archive* Archive__Peek__Libarchive;

struct archive* _open_file(const char * filename) {
    struct archive *a;
    int r;

    a = archive_read_new();
    archive_read_support_compression_all(a);
    archive_read_support_format_all(a);

    if ((r = archive_read_open_file(a, filename, 10240))) {
        croak(archive_error_string(a));
    }
    return a;
}

void _close_file(struct archive* a) {
    archive_read_close(a);
    archive_read_finish(a);
}

static int
_copy_data(struct archive *ar, struct archive *aw)
{
	int r;
	const void *buff;
	size_t size;
	off_t offset;

	for (;;) {
		r = archive_read_data_block(ar, &buff, &size, &offset);
		if (r == ARCHIVE_EOF)
			return (ARCHIVE_OK);
		if (r != ARCHIVE_OK)
			return (r);
		r = archive_write_data_block(aw, buff, size, offset);
		if (r != ARCHIVE_OK) {
			warn("archive_write_data_block()",
			    archive_error_string(aw));
			return (r);
		}
	}
}
    
MODULE = Archive::Extract::Libarchive          PACKAGE = Archive::Extract::Libarchive

void _extract(const char * filename, const char * path)
PPCODE:
    struct archive *a;
    struct archive *ext;
    struct archive_entry *entry;
    SV *path_sv;
    int r;
    int flags;
    
    a = _open_file(filename);

    flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS;
    ext = archive_write_disk_new();
    archive_write_disk_set_options(ext, flags);
    archive_write_disk_set_standard_lookup(ext);

    for (;;) {
        r = archive_read_next_header(a, &entry);
        if (r == ARCHIVE_EOF)
            break;
	if (r != ARCHIVE_OK)
            croak(archive_error_string(a));
        if (archive_entry_filetype(entry) == AE_IFREG) {
            mXPUSHs(newSVpv(archive_entry_pathname(entry), 0));
        }

	path_sv = newSVpv(path, 0);
	sv_catpvs(path_sv, "/");
	sv_catpv(path_sv, archive_entry_pathname(entry));
	archive_entry_set_pathname(entry, SvPV_nolen(path_sv));
	sv_free(path_sv);
	
	r = archive_write_header(ext, entry);
	if (r != ARCHIVE_OK)
	    croak(archive_error_string(ext));
	_copy_data(a, ext);
	r = archive_write_finish_entry(ext);
	if (r != ARCHIVE_OK)
	    croak(archive_error_string(ext));
    }
    _close_file(a);
    archive_write_close(ext);
    archive_write_finish(ext);