/* main.c
 *
 * Copyright (C) 2005 by Andy Goth <unununium@openverse.com>.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA. */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "qbism.h"

/* Allocates memory or else. */
void* xmalloc(size_t bytes)
{
    void* result = malloc(bytes);

    if (result == NULL) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    return result;
}

/* Reallocates memory or else. */
void* xrealloc(void* buf, size_t bytes)
{
    void* result = realloc(buf, bytes);

    if (result == NULL) {
        perror("realloc");
        exit(EXIT_FAILURE);
    }

    return result;
}

typedef enum {
    OPT_INQBE,
    OPT_INQBM,
    OPT_OUTQBE,
    OPT_OUTQBM,
    OPT_OUTIMG,
    OPT_SIZE,
    OPT_REGS,
    OPT_STEPS,
    OPT_COUNT
} opt_t;

static char* opt_names[] = {
    "-inqbe",
    "-inqbm",
    "-outqbe",
    "-outqbm",
    "-outimg",
    "-size",
    "-regs",
    "-steps"
};

static FILE* my_open(char* name, char* mode)
{
    FILE* fd;

    if (strcmp(name, "-") == 0) {
        if (strcmp(mode, "r") == 0) {
            fd = stdin;
        } else {
            fd = stdout;
        }
    } else {
        fd = fopen(name, mode);
        if (fd == NULL) {
            perror(name);
            exit(EXIT_FAILURE);
        }
    }

    return fd;
}

static void my_close(FILE* fd)
{
    if (fd != stdin && fd != stdout) {
        if (fclose(fd) == EOF) {
            perror("fclose");
            exit(EXIT_FAILURE);
        }
    }
}

/* Repeals gravity. */
int main(int argc, char** argv)
{
    FILE* fd;
    int width, height;
    int i, j;
    int split;
    algo_t algo = {NULL, 36, 12};

    char* opts[OPT_COUNT] = {NULL};

    /* Defaults. */
    opts[OPT_SIZE]   = "640x480";
    opts[OPT_OUTIMG] = "-";
    opts[OPT_REGS]   = "6";
    opts[OPT_STEPS]  = "36";

    /* Check if help is requested. */
    if (argc > 1 && ((strcmp(argv[1], "-h") == 0) ||
            (strcmp(argv[1], "-help") == 0) ||
            (strcmp(argv[1], "--help") == 0))) {
        printf("Usage: qbism [OPTION]...\n"
        "Use learning for evil.\n"
        "\n"
        "  -inqbe  FILE           run the algorithm in the QBE FILE\n"
        "  -inqbm  FILE           run the algorithm in the QBM FILE\n"
        "  -outqbe FILE           save the algorithm in QBE format to FILE\n"
        "  -outqbm FILE           save the algorithm in QBM format to FILE\n"
        "  -outimg FILE           render the image in MIFF format to FILE\n"
        "  -size   WIDTHxHEIGHT   render at WIDTH by HEIGHT pixels\n"
        "  -regs   NUM            for random algorithms, use NUM registers\n"
        "  -steps  NUM            for random algorithms, use NUM steps\n"
        "  -h, -help              display this message\n"
        "  -v, -version           print the version information\n"
        "\n"
        "If neither -inqbe nor -inqbm are specified, a random algorithm is\n"
        "generated and used to render the image.  Otherwise the algorithm\n"
        "stored in the QBE or QBM file is used.\n"
        "\n"
        "Specify - as FILE to use stdin or stdout.  By default, the output\n"
        "image is written to stdout; pipe through display or convert (from\n"
        "ImageMagick) for the desired effect.\n"
        "\n"
        "Report features to Andy Goth <unununium@openverse.com>.\n");

        return EXIT_SUCCESS;
    }

    /* Check if the version number is requested. */
    if (argc > 1 && ((strcmp(argv[1], "-v") == 0) ||
            (strcmp(argv[1], "-version") == 0) ||
            (strcmp(argv[1], "--version") == 0))) {
        printf("qbism 0.2\n");
        return EXIT_SUCCESS;
    }

    /* Check that every option has a parameter. */
    if ((argc - 1) % 2 != 0) {
        fprintf(stderr, "Option \"%s\" has no parameter\n", argv[argc - 1]);
        return EXIT_FAILURE;
    }

    /* Get all parameters. */
    for (i = 1; i < argc; i += 2) {
        for (j = 0; j < OPT_COUNT; ++j) {
            if (strcmp(argv[i], opt_names[j]) == 0) {
                opts[j] = argv[i + 1];
                break;
            }
        }
        if (j == OPT_COUNT) {
            fprintf(stderr, "Unknown option \"%s\"\n", argv[i]);
            return EXIT_FAILURE;
        }
    }

    /* Check for disallowed combinations. */
    if (opts[OPT_INQBE] != NULL && opts[OPT_INQBM] != NULL) {
        fprintf(stderr, "Cannot specify both -inqbe and -inqbm\n");
        return EXIT_FAILURE;
    }

    /* Interpret numeric options. */
    if (sscanf(opts[OPT_SIZE], "%dx%d", &width, &height) != 2) {
        fprintf(stderr, "Malformed size \"%s\"\n", opts[OPT_SIZE]);
    }
    if (sscanf(opts[OPT_REGS], "%d", &algo.num_regs) != 1 ||
            algo.num_regs < 1) {
        fprintf(stderr, "Malformed register count \"%s\"\n", opts[OPT_REGS]);
    }
    if (sscanf(opts[OPT_STEPS], "%d", &algo.num_steps) != 1 ||
            algo.num_steps < 0) {
        fprintf(stderr, "Malformed step count \"%s\"\n", opts[OPT_STEPS]);
    }

    /* Read/generate an algorithm. */
    if (opts[OPT_INQBE] != NULL) {
        fd = my_open(opts[OPT_INQBE], "r");
        algo_read_qbe(&algo, fd);
        my_close(fd);
    } else if (opts[OPT_INQBM] != NULL) {
        fprintf(stderr, "Unimplemented!\n");
    } else {
        /* Generate a random transformation sequence. */
        srandom(time(NULL));
        algo.seq = xmalloc(sizeof(*algo.seq) * algo.num_steps);
        for (i = 0; i < algo.num_steps; ++i) {
            algo.seq[i].opcode  = random() % XOP_COUNT;
            algo.seq[i].source  = random() % algo.num_regs;
            algo.seq[i].control = random() % algo.num_regs;
            algo.seq[i].dest    = random() % algo.num_regs;
        }
    }

    /* If so requested, write out the algorithm. */
    if (opts[OPT_OUTQBE] != NULL) {
        fd = my_open(opts[OPT_OUTQBE], "w");
        algo_write_qbe(&algo, fd);
        my_close(fd);
    } else if (opts[OPT_OUTQBM] != NULL) {
        fprintf(stderr, "Unimplemented!\n");
    }

    /* Render! */
    fd = my_open(opts[OPT_OUTIMG], "w");
    qbism_render(&algo, width, height, fd);
    my_close(fd);
    
    return EXIT_SUCCESS;
}

/* vim: set ts=4 sts=4 sw=4 tw=80 et: */

