/* chmd_order: test that extracting a CHM file in different ways works
 * and all give the same results:
 * - extracting files in the order they're listed (generally alphabetical)
 * - extracting files ordered by their content section offset
 * - extracting files using fast_find() to find them
 * - extracting files from two chms at the same time with one decompressor
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mspack.h>

#include <md5_fh.h>
#include <error.h>

struct my_file {
    struct mschmd_file *file;
    struct mschmd_file result;
    char ordered[32], sorted[32], fast_find[32], mixed[32];
};

static int sortfunc(const void *a, const void *b) {
  off_t diff =
    ((struct my_file *) a)->file->offset -
    ((struct my_file *) b)->file->offset;
  return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
}

int main(int argc, char *argv[]) {
    struct mschm_decompressor *chmd;
    struct mschmd_header *chm, *chm2;
    struct mschmd_file *file;
    struct my_file *f;
    unsigned int N, i;

    MSPACK_SYS_SELFTEST(i);
    if (i) return 0;

    if ((chmd = mspack_create_chm_decompressor(&read_files_write_md5))) {
        for (argv++; *argv; argv++) {
            printf("%s\n", *argv);

            if ((chm = chmd->open(chmd, *argv))) {
                if ((chm2 = chmd->fast_open(chmd, *argv))) {

                    /* count the number of files, allocate a results structure */
                    for (N=0, file = chm->files; file; file = file->next) N++;
                    if ((f = (struct my_file *) calloc(N, sizeof(struct my_file)))) {
                        
                        /* fill out results structure while doing ordered extraction */
                        for (i = 0, file = chm->files; file; file = file->next, i++) {
                            printf("OX %s\n", file->filename);
                            f[i].file = file;
                            if (chmd->extract(chmd, file, NULL)) {
                                fprintf(stderr, "%s: O extract error on \"%s\": %s\n",
                                        *argv, file->filename, ERROR(chmd));
                                continue;
                            }
                            memcpy(&f[i].ordered[0], md5_string, 32);
                        }

                        /* sort the list into offset order */
                        qsort(f, N, sizeof(struct my_file), &sortfunc);

                        /* extract in offset order */
                        for (i = 0; i < N; i++) {
                            printf("SX %s\n", f[i].file->filename);
                            if (chmd->extract(chmd, f[i].file, NULL)) {
                                fprintf(stderr, "%s: S extract error on \"%s\": %s\n",
                                        *argv, f[i].file->filename, ERROR(chmd));
                                continue;
                            }
                            memcpy(&f[i].sorted[0], md5_string, 32);
                        }

                        /* extract using fast_find */
                        for (i = 0; i < N; i++) {
                            printf("FX %s\n", f[i].file->filename);
                            
                            if (chmd->fast_find(chmd, chm2,
                                f[i].file->filename,
                                &f[i].result, sizeof(struct mschmd_file)))
                            {
                                fprintf(stderr, "%s: find error on \"%s\": %s\n",
                                        *argv, f[i].file->filename, ERROR(chmd));
                                continue;
                            }
                            if (!f[i].result.section) {
                                fprintf(stderr, "%s: can't find file \"%s\"\n",
                                        *argv, f[i].file->filename);
                                continue;
                            }
                            if (chmd->extract(chmd, &f[i].result, NULL)) {
                                fprintf(stderr, "%s: F extract error on \"%s\": %s\n",
                                        *argv, f[i].file->filename, ERROR(chmd));
                                continue;
                            }
                            memcpy(&f[i].fast_find[0], md5_string, 32);
                        }

                        /* extract two chms at once */
                        for (i = 0; i < N; i++) {
                            printf("MX %s\n", f[i].file->filename);
                            chmd->extract(chmd, f[i].file, NULL);
                            if (chmd->extract(chmd, &f[i].result, NULL)) {
                                fprintf(stderr, "%s: M extract error on \"%s\": %s\n",
                                        *argv, f[i].file->filename, ERROR(chmd));
                                continue;
                            }
                            memcpy(&f[i].mixed[0], md5_string, 32);
                        }

                        /* check all the MD5 sums match */
                        for (i = 0; i < N; i++) {
                            if (memcmp(&f[i].ordered, &f[i].sorted,    32) ||
                                memcmp(&f[i].ordered, &f[i].fast_find, 32) ||
                                memcmp(&f[i].ordered, &f[i].mixed,     32))
                             {
                                 fprintf(stderr, "%s: sums mismatch on %s "
                                         "(O=%32.32s,S=%32.32s,F=%32.32s,M=%32.32s)\n",
                                         *argv, f[i].file->filename,
                                         &f[i].ordered[0], &f[i].sorted[0],
                                         &f[i].fast_find[0], &f[i].mixed[0]);
                             }
                        }

                        free(f);
                    }
                    chmd->close(chmd, chm2);
                }
                chmd->close(chmd, chm);
            }
            else {
                printf("%s: can't open -- %s\n", *argv, ERROR(chmd));
            }
        }
        mspack_destroy_chm_decompressor(chmd);
    }
    return 0;
}