/* qbm.c
 *
 * Copyright (C) 2005-2006 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. */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include "qbism.h"

/* List of transformation names.  Note: this list must be manually sync'ed with
 * the xform_op_t typedef in qbism.h. */
static char* xform_names[] = {
    "PROJECTION",
    "SHIFT",
    "SHIFTBACK",
    "ROTATE",
    "ROTATEBACK",
    "MULTIPLY",
    "SINE",
    "CONDITIONAL",
    "COMPLEMENT"
};

/* Read a QBM file into an algorithm structure. */
void qbm_read(algo_t* algo, FILE* in)
{
    char* line = NULL;
    size_t line_len = 0;
    char xform_name[16];
    int i, j;

    /* Read and check the first line. */
    if (getline(&line, &line_len, in) < 0) {
        if (ferror(in)) {
            perror("getline");
        } else {
            fprintf(stderr, "Malformed QBM input file: zero length.\n");
        }
        free(line);
        exit(EXIT_FAILURE);
    }
    if (strcmp(line, "QBM VERSION 1\n") != 0) {
        fprintf(stderr, "Malformed QBM or unsupported version.\n");
        free(line);
        exit(EXIT_FAILURE);
    }

    algo->seq = NULL;
    algo->seq_len = 0;
    algo->num_regs = 0;
    while (1) {
        /* Read the line. */
        if (getline(&line, &line_len, in) < 0) {
            if (ferror(in)) {
                perror("getline");
                free(algo->seq);
                exit(EXIT_FAILURE);
            } else {
                break;
            }
        }

        /* Got a line, so allocate space for new data. */
        i = algo->seq_len;
        algo->seq_len++;
        algo->seq = xrealloc(algo->seq, sizeof(*algo->seq) * algo->seq_len);

        /* Extract the fields from the line. */
        if (sscanf(line, " %16s %d %d %d",
                &xform_name,
                &algo->seq[i].source,
                &algo->seq[i].control,
                &algo->seq[i].dest) != 4) {
            fprintf(stderr, "Malformed QBM input file.\n");
            free(line);
            free(algo->seq);
            exit(EXIT_FAILURE);
        }

        /* Find opcode in name table. */
        for (j = 0; j < XOP_COUNT; j++) {
            if (strcmp(xform_name, xform_names[j]) == 0) {
                algo->seq[i].opcode = j;
                break;
            }
        }
        if (j == XOP_COUNT) {
            fprintf(stderr, "Malformed QBM input file: "
                    "unknown opcode \"%s\".\n", xform_name);
            free(line);
            free(algo->seq);
            exit(EXIT_FAILURE);
        }

        /* Identify the highest-numbered register. */
        if (algo->seq[i].source > algo->num_regs) {
            algo->num_regs = algo->seq[i].source;
        }
        if (algo->seq[i].control > algo->num_regs) {
            algo->num_regs = algo->seq[i].control;
        }
        if (algo->seq[i].dest > algo->num_regs) {
            algo->num_regs = algo->seq[i].dest;
        }
    }

    /* The number of registers is one plus the highest-numbered register. */
    ++algo->num_regs;
    free(line);
}

/* Write an algorithm structure to a QBM file. */
void qbm_write(algo_t* algo, FILE* out)
{
    int i;
    fprintf(out, "QBM VERSION 1\n");
    for (i = 0; i < algo->seq_len; ++i) {
        fprintf(out, "%-11s %2d %2d %2d\n",
                xform_names[algo->seq[i].opcode],
                algo->seq[i].source,
                algo->seq[i].control,
                algo->seq[i].dest);
    }
}

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

