/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF.  The full HDF copyright notice, including       *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF/releases/.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <math.h>
#include <stdio.h>

#include "mfhdf.h"
#include "hdp.h"

#define IMAGE 1

static void
dumprig_usage(int argc, char *argv[])
{
    (void)argc;

    printf("Usage:\n");
    printf("%s dumprig [-a|-i <indices>|-m <n>|-r <refs>] [-dhv] [-o <filename> [-bx]] <filelist>\n",
           argv[0]);
    printf("\t-a\tDump all RIGs in the file (default)\n");
    printf("\t-i <indices>\tDump the RIGs at positions listed in <indices>\n");
    printf("\t-m <n>\tDump the 8- or 24-bit RIGs only, <n> may be 8 or 24 \n");
    printf("\t-r <refs>\tDump the RIGs with reference number listed in <refs>\n");
    printf("\t-d\tDump data only, no tag/ref, formatted to input to hp2hdf\n");
    printf("\t-h\tDump header only, no annotation for elements nor data\n");
    printf("\t-v\tDump everything including all annotations (default)\n");
    printf("\t-c\tDo not add a carriage return to a long data line\n");
    printf("\t-o <filename>\tOutput to file <filename>\n");
    printf("\t-b\tBinary format of output\n");
    printf("\t-x\tAscii text format of output (default)\n");
    printf("\t<filelist>\tList of hdf file names, separated by spaces\n");
} /* end list_usage() */

static void
init_dumprig_opts(dump_info_t *dumprig_opts)
{
    dumprig_opts->filter       = DALL;     /* default dump all RIGs */
    dumprig_opts->filter_num   = NULL;     /* not by reference nor by index */
    dumprig_opts->filter_str   = NULL;     /* no strings */
    dumprig_opts->num_chosen   = (-1);     /* default dump all items */
    dumprig_opts->contents     = DVERBOSE; /* default dump all information */
    dumprig_opts->dump_to_file = FALSE;    /* don't dump to output file */
    dumprig_opts->file_format  = DASCII;   /* default output is ASCII file */
    dumprig_opts->as_stream    = FALSE;    /* print output aligned, using carriage returns */
    dumprig_opts->print_pal    = FALSE;    /* GR only, don't print palette */

    /* print output aligned, using carriage returns */
    dumprig_opts->as_stream = FALSE;

    /* print space characters (LF, FF, CR, space, tabs...) in \digit format */
    dumprig_opts->clean_output = FALSE;

    /* print data starting at column 5 unless reset otherwise */
    dumprig_opts->firstln_indent = 5; /* Note: only for dump_rig, so
                                       testfiles won't need to be changed */

    /* print data on a continuous line starting at column 5 unless
       reset otherwise */
    dumprig_opts->contln_indent = 5; /* Note: only for dump_rig, so
                                       testfiles won't need to be changed */

    /* GR only, print data using interlace at creation */
    dumprig_opts->interlace = NO_SPECIFIC;

    /* GR & SD only, print data of global attributes unless -g is given */
    dumprig_opts->no_gattr_data = FALSE;

    /* GR & SD only, print data of local attributes unless -l is given */
    dumprig_opts->no_lattr_data = FALSE;

    strcpy(dumprig_opts->file_name, "\0");
} /* end init_dumprig_opts() */

static int
parse_dumprig_opts(dump_info_t *dumprig_opts, int *curr_arg, int argc, char *argv[], int *model)
{
    int32 i;
    int32 numItems;
    char *tempPtr = NULL;
    char *ptr     = NULL;

    if (*curr_arg >= argc)
        return (FAIL);

    while ((*curr_arg < argc) && (argv[*curr_arg][0] == '-')) {
        switch (argv[*curr_arg][1]) {
            case 'a': /* dump all, default */
                dumprig_opts->filter = DALL;
                (*curr_arg)++;
                break;

            case 'm': /* dump the rigs with model specified as 8-bit or 24-bit */
                (*curr_arg)++;
                *model = atoi(argv[*curr_arg]);
                if (((*model) != 8) && ((*model) != 24)) {
                    printf("%s-bit raster model: not available.\n", argv[*curr_arg]);
                    exit(1);
                }
                (*curr_arg)++;
                break;

            case 'i': /* dump by index */
            case 'r': /* dump by reference */
                if ((argv[*curr_arg][1]) == 'i')
                    dumprig_opts->filter = DINDEX;
                else
                    dumprig_opts->filter = DREFNUM;
                (*curr_arg)++;

                ptr = argv[*curr_arg];

                /* check if it's the end of the command */
                if (ptr == NULL) {
                    printf("Missing values for option\n");
                    exit(1);
                }
                numItems = 0;
                while ((tempPtr = strchr(ptr, ',')) != NULL) {
                    numItems++;
                    ptr = tempPtr + 1;
                }                 /* end while */
                if (*ptr != '\0') /* count the last item */
                    numItems++;

                dumprig_opts->filter_num = (int *)malloc(sizeof(int) * (size_t)numItems);
                if (dumprig_opts->filter_num == NULL) {
                    printf("Not enough memory!\n");
                    exit(-1);
                }

                ptr = argv[*curr_arg];
                i   = 0;
                while ((tempPtr = strchr(ptr, ',')) != NULL) {
                    *tempPtr                    = '\0';
                    dumprig_opts->filter_num[i] = atoi(ptr);
                    ptr                         = tempPtr + 1;
                    i++;
                }
                dumprig_opts->filter_num[i] = atoi(ptr); /* get the last item */
                dumprig_opts->num_chosen    = numItems;  /* save the number of items */

                (*curr_arg)++;
                break;

            case 'd': /* dump data only */
                dumprig_opts->contents = DDATA;
                (*curr_arg)++;
                break;

            case 'h': /* no annotations nor data */
                dumprig_opts->contents = DHEADER;
                (*curr_arg)++;
                break;

            case 'v': /* dump all info */
                dumprig_opts->contents = DVERBOSE;
                (*curr_arg)++;
                break;

            case 'c': /* do not add carriage returns to output data lines */
                dumprig_opts->as_stream = TRUE;
                (*curr_arg)++;
                break;

            case 'o': /* specify output file */
                dumprig_opts->dump_to_file = TRUE;

                /* Get file name */
                strcpy(dumprig_opts->file_name, argv[++(*curr_arg)]);

                (*curr_arg)++;
                break;

            case 'b': /* dump data in binary */
                dumprig_opts->file_format = DBINARY;
                (*curr_arg)++;
                break;

            case 'x': /* dump data in ascii, also default */
                dumprig_opts->file_format = DASCII;
                (*curr_arg)++;
                break;

            default: /* invalid dumprig option */
                printf("Warning: Invalid dumprig option %s\n", argv[*curr_arg]);
                return (FAIL);
        } /* end switch */
    }     /* end while */
    return (SUCCEED);
} /* end parse_dumprig_opts */

static int
drig(dump_info_t *dumprig_opts, int curr_arg, int argc, char *argv[], int model)
{
    int32        *rig_chosen = NULL;
    int32         num_rig_chosen;
    int32         width, height;
    int32         ndsets;
    int32         temp;
    int           i, k, x;
    char          file_name[MAXFNLEN];
    FILE         *fp      = NULL;
    void         *image   = NULL;
    int           dumpall = 0;
    int           ncomps;
    int           il;
    file_format_t ff;
    int           ret_value = SUCCEED;

    /* Examine all files */
    while (curr_arg < argc) {
        strcpy(file_name, argv[curr_arg]);
        curr_arg++;

        num_rig_chosen = dumprig_opts->num_chosen;
        if (num_rig_chosen > 0) {
            if ((rig_chosen = (int32 *)malloc(sizeof(int32) * (size_t)num_rig_chosen)) == NULL) {
                fprintf(stderr, "Memory allocation error\n");
                ret_value = FAIL;
                goto done;
            }

            k = -1;
            HDmemfill(rig_chosen, &k, sizeof(int32), (uint32)num_rig_chosen);
        }

        /* Determine which RIGs are to be displayed. */
        if (dumprig_opts->filter == DINDEX) {
            for (i = 0; i < dumprig_opts->num_chosen; i++)
                rig_chosen[i] = dumprig_opts->filter_num[i];
            sort(rig_chosen, num_rig_chosen); /* DREFNUM doesn't need this */
        }

        /* ASCII or Binary? */
        ff = dumprig_opts->file_format;
        switch (ff) {
            case DASCII: /*  ASCII  file   */

                /* Get the name of the output file. */
                if (dumprig_opts->dump_to_file)
                    fp = fopen(dumprig_opts->file_name, "w");
                else
                    fp = stdout;
                if (dumprig_opts->contents != DDATA)
                    fprintf(fp, "File name: %s \n\n", file_name);

                /* Determine the number of images in a file. */
                if (model == 8) { /* raster 8 */
                    if ((ndsets = DFR8nimages(file_name)) == FAIL) {
                        ret_value = FAIL;
                        goto done;
                    }
                }
                else if (model == 24) { /* raster 24 */
                    if ((ndsets = DF24nimages(file_name)) == FAIL) {
                        ret_value = FAIL;
                        goto done;
                    }
                }
                else /* try both 8 and 24 */
                {
                    if ((temp = DFR8nimages(file_name)) == FAIL) {
                        ret_value = FAIL;
                        goto done;
                    }
                    if ((ndsets = DF24nimages(file_name)) == FAIL) {
                        ret_value = FAIL;
                        goto done;
                    }
                    ndsets += temp;
                }

                if (num_rig_chosen == -1) /* If all RIGs will be dumped, set the flag. */
                    dumpall = 1;

                x = 0; /* Used as the index of the array of "rig_chosen[x]". */

                /* can only check index range here */
                for (i = 0; i < dumprig_opts->num_chosen && dumprig_opts->filter == DINDEX; i++) {
                    if ((dumprig_opts->filter_num[i] > ndsets) || (dumprig_opts->filter_num[i] < 0)) {
                        fprintf(stderr, "\nThe index number %d is out of range\n",
                                dumprig_opts->filter_num[i]);
                        ret_value = FAIL;
                        goto done;
                    }
                }

                for (i = 0; i < ndsets && (dumpall != 0 || x < dumprig_opts->num_chosen);
                     i++) { /* Examine all RIGs. */
                    int    compressed;
                    int    has_pal;
                    int32  eltsz;
                    int32  read_nelts;
                    uint16 rig_ref;
                    uint16 compr_type;

                    /* get dimensions of ri */
                    if (FAIL == DFGRgetimdims(file_name, &width, &height, &ncomps, &il)) {
                        ret_value = FAIL;
                        goto done;
                    }

                    /* Determine the size of each element; "ncomps" is 1 for an
                       8-bit image or 3 for a 24-bit image. */
                    eltsz      = DFKNTsize(DFNT_UINT8 | DFNT_NATIVE) * ncomps;
                    read_nelts = width * height; /* Number of elements to be read in. */
                    if ((image = (void *)malloc((size_t)(read_nelts * eltsz))) == NULL) {
                        fprintf(stderr, "Not enough memory!\n");
                        ret_value = FAIL;
                        goto done;
                    }

                    /*DFGRreqimil( 1 );*/
                    if (DFGRIgetimlut((const char *)file_name, image, width, height, IMAGE, 0, &compressed,
                                      &compr_type, &has_pal) == -1) {
                        fprintf(stderr, "DFGRIgetimlut: Read error for file %s\n", file_name);
                        ret_value = FAIL;
                        goto done;
                    }

                    rig_ref = DFGRIlastref(); /* Determine the reference of the image just read. */

                    /* If the user has specified the reference option, then
                       something has to be done.
                       Note: the reason why the following part was not done
                       inside the above "switch" statement is that the reference
                       number of a raster image cannot be appropriately retrieved
                       before actually reading in a raster image. */
                    if (dumprig_opts->filter == DREFNUM) {
                        int ref_found = 0, m;

                        /* Determine if the image just read has the reference specified by the user. */
                        for (m = 0; m < dumprig_opts->num_chosen; m++) {
                            if (dumprig_opts->filter_num[m] == rig_ref)
                                ref_found = 1; /* found image */
                        }

                        if (!ref_found) { /* If no match, then the current image is
                                             not what the user wants and so skip it. */
                            free(image);
                            image = NULL; /* reset */
                            continue;
                        }
                    }

                    /* If not all images are to be dumped out and the current image
                       is not what the user wants or if the user has specified a
                       model and the model of the current image is not that one, then
                       skip the current image. */
                    if (((dumprig_opts->filter == DINDEX) && (i != rig_chosen[x])) ||
                        (((ncomps * 8) != model) && (model != 0))) {
                        free(image);
                        image = NULL; /* reset */
                        continue;
                    }

                    /* Determine what to be dumped out. */
                    switch (dumprig_opts->contents) {
                        case DVERBOSE:
                        case DHEADER:
                            fprintf(fp, "Data model: %d-bit raster image ", ncomps * 8);
                            /* 24-bit images do not have palette */
                            if ((ncomps != 3) && has_pal)
                                fprintf(fp, "with palette.\n");
                            else
                                fprintf(fp, "without palette.\n");
                            fprintf(fp, "\twidth=%d;  height=%d\n", (int)width, (int)height);
                            fprintf(fp, "\treference=%d\n", (int)rig_ref);
                            /* check compression if any */
                            if (compressed) {
                                fprintf(fp, "\t*data is compressed with ");
                                switch (compr_type) {
                                    case DFTAG_RLE:
                                        fprintf(fp, "RLE compression scheme.\n");
                                        break;
                                    case DFTAG_IMCOMP:
                                        fprintf(fp, "IMCOMP compression scheme.\n");
                                        break;
                                    case DFTAG_JPEG:
                                    case DFTAG_JPEG5:
                                        fprintf(fp, "JPEG compression scheme (24-bit data.)\n");
                                        break;
                                    case DFTAG_GREYJPEG:
                                    case DFTAG_GREYJPEG5:
                                        fprintf(fp, "JPEG compression scheme (8-bit data.)\n");
                                        break;
                                    default:
                                        fprintf(fp, "unknown scheme.");
                                        break;
                                } /* switch */
                                fprintf(fp, "\n");
                            } /* if (compressed) */
                            else
                                fprintf(fp, "\t*data is not compressed.\n");

                            if (dumprig_opts->contents == DHEADER)
                                break;

                            /* FALLTHROUGH */

                        case DDATA:
                            if (dumprig_opts->contents != DDATA)
                                fprintf(fp, "\tData : \n");
                            if (FAIL ==
                                dumpfull(DFNT_UINT8, dumprig_opts, read_nelts * eltsz, image, fp, 5, 5)) {
                                fprintf(stderr, "dumpfull: failed to dump %d'th image data for file %s", i,
                                        file_name);
                                ret_value = FAIL;
                                goto done;
                            }
                            free(image);
                            image = NULL;
                            break;
                        default:
                            printf("dumping RIG, unknown option \n");
                            ret_value = FAIL;
                            goto done;
                    } /* switch  */

                    if (dumpall != 1 && i == rig_chosen[x])
                        x++;

                } /* for every image in file  */

                break; /* ASCII */

            case DBINARY: /* binary  file  */
                /* Get the name of the output file. */
                if (dumprig_opts->dump_to_file)
                    fp = fopen(dumprig_opts->file_name, "wb");
                else
                    fp = stdout;

                /* Determine the number of images in a file. */
                if (model == 8) { /* raster 8 */
                    if ((ndsets = DFR8nimages(file_name)) == FAIL) {
                        ret_value = FAIL;
                        goto done;
                    }
                }
                else if (model == 24) { /* raster 24 */
                    if ((ndsets = DF24nimages(file_name)) == FAIL) {
                        ret_value = FAIL;
                        goto done;
                    }
                }
                else /* try both 8 and 24 */
                {
                    if ((temp = DFR8nimages(file_name)) == FAIL) {
                        ret_value = FAIL;
                        goto done;
                    }
                    if ((ndsets = DF24nimages(file_name)) == FAIL) {
                        ret_value = FAIL;
                        goto done;
                    }
                    ndsets += temp;
                }

                /* If all RIGs will be dumped, set the flag. */
                if (num_rig_chosen == -1)
                    dumpall = 1;

                x = 0; /* Used as the index of the array of "rig_chosen[x]". */

                /* can only check index range here */
                for (i = 0; i < dumprig_opts->num_chosen && dumprig_opts->filter == DINDEX; i++) {
                    if ((rig_chosen[i] > ndsets) || (rig_chosen[i] < 0)) {
                        fprintf(stderr, "\nThe index %d is out of range\n", (int)rig_chosen[i]);
                        ret_value = FAIL;
                        goto done;
                    }
                }

                for (i = 0; i < ndsets && (dumpall != 0 || x < dumprig_opts->num_chosen);
                     i++) { /* Examine all RIGs. */
                    int    compressed;
                    int    has_pal;
                    int32  eltsz;
                    int32  read_nelts;
                    uint16 rig_ref;
                    uint16 compr_type;

                    if (FAIL == DFGRgetimdims(file_name, &width, &height, &ncomps, &il)) {
                        ret_value = FAIL;
                        goto done;
                    }

                    /* Determine the size of each element; "ncomps" is 1 for an
                       8-bit image or 3 for a 24-bit image. */
                    eltsz      = DFKNTsize(DFNT_UINT8 | DFNT_NATIVE) * ncomps;
                    read_nelts = width * height; /* Number of elements to be read in. */
                    if ((image = (void *)malloc((size_t)(read_nelts * eltsz))) == NULL) {
                        fprintf(stderr, "Not enough memory!\n");
                        ret_value = FAIL;
                        goto done;
                    }
                    if (DFGRIgetimlut((const char *)file_name, image, width, height, IMAGE, 0, &compressed,
                                      &compr_type, &has_pal) == -1) {
                        fprintf(stderr, "DFGRIgetimlut: Read error for file %s\n", file_name);
                        ret_value = FAIL;
                        goto done;
                    }

                    rig_ref = DFGRIlastref(); /* Determine the reference of the image just read. */

                    /* If the user has specified the reference option, then
                       something has to be done.
                       Note: the reason why the following part was not done
                       inside the above "switch" statement is that the reference
                       number of a raster image cannot be appropriately retrieved
                       before actually reading in a raster image. */
                    if (dumprig_opts->filter == DREFNUM) {
                        int ref_found = 0, m;

                        /* Determine if the image just read has the reference specified by the user. */
                        for (m = 0; m < dumprig_opts->num_chosen; m++) {
                            if (dumprig_opts->filter_num[m] == rig_ref)
                                ref_found = 1; /* found it */
                        }

                        if (!ref_found) { /* If no match, then the current image is
                                             not what the user wants and so skip it. */
                            free(image);
                            image = NULL;
                            continue;
                        }
                    }

                    /* If not all images are to be dumped out and the current image
                       is not what the user wants or if the user has specified a
                       model and the model of the current image is not that one, then
                       skip the current image. */
                    if (((dumprig_opts->filter == DINDEX) && (i != rig_chosen[x])) ||
                        (((ncomps * 8) != model) && (model != 0))) {
                        free(image);
                        image = NULL;
                        continue;
                    }

                    if (FAIL == dumpfull(DFNT_UINT8, dumprig_opts, read_nelts * ncomps, image, fp, 5, 5)) {
                        fprintf(stderr, "dumpfull: failed to dump %d'th image data for file %s", i,
                                file_name);
                        ret_value = FAIL;
                        goto done;
                    }
                    free(image);
                    image = NULL; /* reset */

                    if (dumpall != 1 && i == rig_chosen[x])
                        x++;
                } /* for every image in file  */

                break; /* BINARY */
            default:
                printf("dumping RIG, unknown output file option \n");
                ret_value = FAIL;
                goto done;
        } /* switch for output file   */

        free(rig_chosen);
        rig_chosen = NULL;

        if (dumprig_opts->dump_to_file)
            fclose(fp);
    } /* while processing files  */

done:
    if (ret_value == FAIL) { /* Failure cleanup */
        free(image);
        free(rig_chosen);
    }

    return ret_value;
} /* drig */

int
do_dumprig(int curr_arg, int argc, char *argv[], int help)
{
    dump_info_t dumprig_opts; /* dumprig options */
    int         model     = 0;
    int         ret_value = SUCCEED;

    if (help == TRUE) {
        dumprig_usage(argc, argv);
        goto done;
    } /* end if */

    /* initialize the structure that holds user's options and inputs */
    init_dumprig_opts(&dumprig_opts);

    if (parse_dumprig_opts(&dumprig_opts, &curr_arg, argc, argv, &model) == FAIL) {
        dumprig_usage(argc, argv);
        ret_value = FAIL;
        goto done;
    } /* end if */

    if (drig(&dumprig_opts, curr_arg, argc, argv, model) == FAIL) {
        fprintf(stderr, "Failure in drig.\n");
        ret_value = FAIL;
        goto done;
    }

done:
    free(dumprig_opts.filter_num);

    return ret_value;
} /* end do_dumprig() */
