/* minigzip.c
   part of the minizip-ng project

   Copyright (C) Nathan Moinvaziri
     https://github.com/zlib-ng/minizip-ng

   This program is distributed under the terms of the same license as zlib.
   See the accompanying LICENSE file for the full text of the license.
*/

#include "mz.h"
#include "mz_os.h"
#include "mz_strm.h"
#include "mz_strm_os.h"
#include "mz_strm_zlib.h"

#include <stdio.h> /* printf */

/***************************************************************************/

#define MZ_GZIP_COMPRESS   (1)
#define MZ_GZIP_DECOMPRESS (2)

int32_t minigzip_banner(void);
int32_t minigzip_help(void);

/***************************************************************************/

int32_t minigzip_banner(void) {
    printf("Minigzip %s - https://github.com/zlib-ng/minizip-ng\n", MZ_VERSION);
    printf("---------------------------------------------------\n");
    return MZ_OK;
}

int32_t minigzip_help(void) {
    printf(
        "Usage: minigzip [-x] [-d] [-0 to -9] [files]\n\n"
        "  -x  Extract file\n"
        "  -d  Destination directory\n"
        "  -0  Store only\n"
        "  -1  Compress faster\n"
        "  -9  Compress better\n\n");
    return MZ_OK;
}

/***************************************************************************/

int32_t minigzip_copy(const char *path, const char *destination, int16_t operation, int16_t level) {
    void *target_stream = NULL;
    void *source_stream = NULL;
    void *zlib_stream = NULL;
    const char *filename = NULL;
    char target_path[1024];
    int32_t err = 0;

    memset(target_path, 0, sizeof(target_path));

    if (destination) {
        if (mz_os_file_exists(destination) != MZ_OK)
            mz_dir_make(destination);
    }

    if (operation == MZ_GZIP_COMPRESS) {
        mz_path_combine(target_path, path, sizeof(target_path));
        strncat(target_path, ".gz", sizeof(target_path) - strlen(target_path) - 1);
        printf("Compressing to %s\n", target_path);
    } else if (operation == MZ_GZIP_DECOMPRESS) {
        if (destination)
            mz_path_combine(target_path, destination, sizeof(target_path));

        if (mz_path_get_filename(path, &filename) != MZ_OK)
            filename = path;

        mz_path_combine(target_path, filename, sizeof(target_path));
        mz_path_remove_extension(target_path);
        printf("Decompressing to %s\n", target_path);
    }

    zlib_stream = mz_stream_zlib_create();
    mz_stream_zlib_set_prop_int64(zlib_stream, MZ_STREAM_PROP_COMPRESS_WINDOW, 15 + 16);

    source_stream = mz_stream_os_create();
    err = mz_stream_os_open(source_stream, path, MZ_OPEN_MODE_READ);

    if (err == MZ_OK) {
        target_stream = mz_stream_os_create();
        err = mz_stream_os_open(target_stream, target_path, MZ_OPEN_MODE_CREATE | MZ_OPEN_MODE_WRITE);

        if (err == MZ_OK) {
            if (operation == MZ_GZIP_COMPRESS) {
                mz_stream_zlib_set_prop_int64(zlib_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, level);
                mz_stream_zlib_open(zlib_stream, target_path, MZ_OPEN_MODE_WRITE);
                mz_stream_set_base(zlib_stream, target_stream);
                err = mz_stream_copy_to_end(zlib_stream, source_stream);
            } else if (operation == MZ_GZIP_DECOMPRESS) {
                mz_stream_zlib_open(zlib_stream, path, MZ_OPEN_MODE_READ);
                mz_stream_set_base(zlib_stream, source_stream);
                err = mz_stream_copy_to_end(target_stream, zlib_stream);
            }

            if (err != MZ_OK)
                printf("Error %d in zlib stream (%d)\n", err, mz_stream_zlib_error(zlib_stream));
            else
                printf("Operation completed successfully\n");

            mz_stream_zlib_close(zlib_stream);
        } else {
            printf("Error %d opening target path %s\n", err, target_path);
        }

        mz_stream_os_close(target_stream);
        mz_stream_os_delete(&target_stream);
    } else {
        printf("Error %d opening source path %s\n", err, path);
    }

    mz_stream_os_close(source_stream);
    mz_stream_os_delete(&source_stream);

    mz_stream_zlib_delete(&zlib_stream);
    return err;
}

/***************************************************************************/

#if !defined(MZ_ZIP_NO_MAIN)
int main(int argc, const char *argv[]) {
    int16_t operation_level = MZ_COMPRESS_LEVEL_DEFAULT;
    int32_t path_arg = 0;
    int32_t err = 0;
    int32_t i = 0;
    uint8_t operation = MZ_GZIP_COMPRESS;
    const char *path = NULL;
    const char *destination = NULL;

    minigzip_banner();
    if (argc == 1) {
        minigzip_help();
        return 0;
    }

    /* Parse command line options */
    for (i = 1; i < argc; i += 1) {
        printf("%s ", argv[i]);
        if (argv[i][0] == '-') {
            char c = argv[i][1];
            if ((c == 'x') || (c == 'X'))
                operation = MZ_GZIP_DECOMPRESS;
            else if ((c >= '0') && (c <= '9'))
                operation_level = (c - '0');
            else if (((c == 'd') || (c == 'D')) && (i + 1 < argc)) {
                destination = argv[i + 1];
                printf("%s ", argv[i + 1]);
                i += 1;
            } else {
                err = MZ_SUPPORT_ERROR;
            }
        } else if (path_arg == 0) {
            path_arg = i;
            break;
        }
    }
    printf("\n");

    if (err == MZ_SUPPORT_ERROR) {
        printf("Feature not supported\n");
        return err;
    }

    if (path_arg == 0) {
        minigzip_help();
        return 0;
    }

    path = argv[path_arg];
    err = minigzip_copy(path, destination, operation, operation_level);

    return err;
}
#endif
