#include "streaksremove.h"

static pmConfig * parseArguments(int, char **);

int main(int argc, char *argv[])
{
    long i;
    bool status;

    psLibInit(NULL);

    pmConfig *config = parseArguments(argc, argv);
    if (!config) {
        streaksExit("failed to parse arguments", PS_EXIT_CONFIG_ERROR);
    }

    ippStage stage = psMetadataLookupS32(&status, config->arguments, "STAGE");

    sFile *file1 = sFileOpen(config, stage, "INPUT1", NULL, true);
    sFile *file2 = sFileOpen(config, stage, "INPUT2", NULL, true);

    int ncomponents;
    if (stage == IPP_STAGE_RAW) {
        if (file1->nHDU != file2->nHDU) {
            streaksExit("mages do not have same number of components\n", PS_EXIT_DATA_ERROR);
        }
        if (!(psFitsMoveExtNum(file1->fits, 1, true) && psFitsMoveExtNum(file2->fits, 1, true))) {
            streaksExit("failed to skip primary header of raw file\n", PS_EXIT_DATA_ERROR);
        }
        ncomponents = file1->nHDU - 1;
    } else {
        ncomponents = 1;
    }

    int numErrors = 0;
    for (int component = 0; component < ncomponents; component++) {
        if (component && !(psFitsMoveExtNum(file1->fits, 1, true) 
                           && psFitsMoveExtNum(file2->fits, 1, true))) {
            streaksExit("failed to advance to next extesion\n", PS_EXIT_DATA_ERROR);
        }
        readImage(file1, 0, stage, false);
        readImage(file2, 0, stage, false);

        psImage *image1 = file1->image;
        psImage *image2 = file2->image;

        // TODO: do more sanity checking. For example check that extname's  (if any) match 
        // check for matching image cubes
        if (image1 && image2) {
            if ((image1->numRows != image2->numRows) || (image1->numCols != image2->numCols)) {
                streaksExit("image sizes do not match\n", PS_EXIT_DATA_ERROR);
            }
            int width = image1->numCols;
            int height = image1->numRows;
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    bool error = false;

                    double value1 = psImageGet(image1, x, y);
                    double value2 = psImageGet(image1, x, y);

                    if (!isnan(value1) && !isnan(value2)) {
                        if (value1 != value2) {
                            error = true;
                        }
                    } else {
                        // if one's a nan they had both better be
                        error = ! isnan(value1) && isnan(value2);
                    }
                    
                    if (error) {
                        numErrors++;
                        fprintf(stderr, "pixel at %5d %5d do not match %f %f\n", x, y, value1, value2);
                    }
                }
            }
        } else if (! ((image1 == NULL) && (image2 == NULL))) {
            // if one image is null (video extension) both should be
            streaksExit("image1 null but not image2!", PS_EXIT_DATA_ERROR);
        }
    }

    fprintf(stderr, "%d errors\n", numErrors);

    closeImage(file1);
    closeImage(file2);

    psFree(config);
    psLibFinalize();

    fprintf(stderr, "Found %d leaks at %s\n", psMemCheckLeaks (0, NULL, NULL, false), "streakscompare");

    //  PAU
    return 0;
}
static void usage(void)
{
    fprintf(stderr, "USAGE: streakscompare file1 file2\n");

    exit(2);
}

static pmConfig *parseArguments(int argc, char **argv)
{
    int argnum;

    pmConfig *config = pmConfigRead(&argc, argv, NULL);
    if (!config) {
        return NULL;
    }

    ippStage stage = IPP_STAGE_NONE;
    if ((argnum = psArgumentGet(argc, argv, "-stage"))) {
        psArgumentRemove(argnum, &argc, argv);
        stage = parseStage(argv[argnum]);
        psMetadataAddS32(config->arguments, PS_LIST_TAIL, "STAGE", 0,
                "pipeline stage for streak removal", stage);
        psArgumentRemove(argnum, &argc, argv);
    } else {
        psError(PS_ERR_UNKNOWN, true, "-stage is required\n");
        usage();
    }

    // If we are asked to replace inputs with the outputs and input image is in nebulous
    // we require mask, weight, and tmproot to all be
    bool nebulousImage = false;
    if ((argnum = psArgumentGet(argc, argv, "-image1"))) {
        // for raw and chip level images we use psFits for I/O and ...
        nebulousImage = IN_NEBULOUS(argv[argnum+1]);
        if (CHIP_LEVEL_INPUT(stage)) {
            psArgumentRemove(argnum, &argc, argv);
            psMetadataAddStr(config->arguments, PS_LIST_TAIL, "INPUT1", 0,
                    "name of input image", argv[argnum]);
            psArgumentRemove(argnum, &argc, argv);
        } else  {
            // ... for warped images we use pmFPAfiles
            if (!pmConfigFileSetsMD(config->arguments, &argc, argv, "INPUT1", "-image1", NULL)) { ;
                psError(PS_ERR_UNKNOWN, false, "failed to process -image");
                usage();
            }
        }
    } else {
        psError(PS_ERR_UNKNOWN, true, "-image1 is required\n");
        usage();
    }
    if ((argnum = psArgumentGet(argc, argv, "-image2"))) {
        // for raw and chip level images we use psFits for I/O and ...
        nebulousImage = IN_NEBULOUS(argv[argnum+1]);
        if (CHIP_LEVEL_INPUT(stage)) {
            psArgumentRemove(argnum, &argc, argv);
            psMetadataAddStr(config->arguments, PS_LIST_TAIL, "INPUT2", 0,
                    "name of input image", argv[argnum]);
            psArgumentRemove(argnum, &argc, argv);
        } else  {
            // ... for warped images we use pmFPAfiles
            if (!pmConfigFileSetsMD(config->arguments, &argc, argv, "INPUT2", "-image2", NULL)) { ;
                psError(PS_ERR_UNKNOWN, false, "failed to process -image");
                usage();
            }
        }
    } else {
        psError(PS_ERR_UNKNOWN, true, "-image2 is required\n");
        usage();
    }

    if (argc != 1) {
        psString unexpectedArguments = NULL;
        for (int i=1; i<argc; i++) {
            psStringAppend(&unexpectedArguments, "%s ", argv[i]);
        }
        psError(PS_ERR_UNKNOWN, true, "unexpected arguments: %s", unexpectedArguments);
        psFree(unexpectedArguments);
        usage();
    }

    return config;
}

