/*                           C V T . C
 * BRL-CAD
 *
 * Copyright (c) 1989-2024 United States Government as represented by
 * the U.S. Army Research Laboratory.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this file; see the file named COPYING for more
 * information.
 *
 */
/** @file comgeom/cvt.c
 *
 * This is the mainline for converting COM-GEOM
 * cards to a GED style database.
 *
 */

#include "common.h"

#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include "bio.h"

#include "vmath.h"
#include "bu/app.h"
#include "bu/getopt.h"
#include "raytrace.h"
#include "wdb.h"


/* defined in region.c */
extern void group_init(void);
extern int getregion(void);
extern void region_register(int reg_num, int id, int air, int mat, int los);
extern void group_write(void);

/* defined in read.c */
extern int get_line(char *cp, int buflen, char *title);

/* defined in solid.c */
extern void trim_trail_spaces(char *cp);
extern int getsolid(void);

struct wmember *wmp;	/* array indexed by region number */

int version = 5;	/* v4 or v5 ? */
int verbose = 0;	/* verbose = print a message on every item read */

char name_it[16];	/* stores argv if it exists and appends it
			   to each name generated.*/

size_t cur_col = 0;

FILE *infp;
struct rt_wdb *outfp;		/* Output file descriptor */

int sol_total, sol_work;	/* total num solids, num solids processed */
int reg_total;

extern void getid(void);

void col_pr(char *str);

static char usage[] = "\
Usage: comgeom-g [options] input_file output_file\n\
Options:\n\
	-v input_vers#	(default is 5 (cg5); versions 1, 4 also available)\n\
	-d debug_lvl\n\
	-s name_suffix\n\
";

int
get_args(int argc, char **argv)
{
    int c;
    char *file_name;

    if (argv == NULL) {
	return 0;
    }

    while ((c = bu_getopt(argc, argv, "d:v:s:h?")) != -1) {
	switch (c) {
	    case 'd':
		verbose = atoi(bu_optarg);
		break;
	    case 's':
		bu_strlcpy(name_it, bu_optarg, sizeof(name_it));
		break;
	    case 'v':
		version = atoi(bu_optarg);
		break;

	    default:		/* '?' */
		return 0;
	}
    }

    if (bu_optind+2 > argc)
	return 0;		/* FAIL */

    /* Input File */
    if (bu_optind >= argc) {
	return 0;		/* FAIL */
    } else {
	file_name = argv[bu_optind++];

	if (!file_name || !bu_file_exists(file_name, NULL)) {
	    perror(file_name);
	    return 0;
	}
	infp = fopen(file_name, "rb");
	if (infp == NULL) {
	    perror(file_name);
	    return 0;
	}
    }

    /* Output File */
    if (bu_optind >= argc) {
	return 0;		/* FAIL */
    } else {
	file_name = argv[bu_optind++];

	if (!file_name) {
	    perror(file_name);
	    return 0;
	}
	if (bu_file_exists(file_name, NULL)) {
	    bu_log("Warning: appending to existing database\n");
	}
	outfp = wdb_fopen(file_name);
	if (outfp == NULL) {
	    perror(file_name);
	    return 0;
	}
    }

    if (argc > ++bu_optind)
	fprintf(stderr, "comgeom-g: excess argument(s) ignored\n");

    return 1;		/* OK */
}


int
main(int argc, char **argv)
{
    int i;
    char ctitle[132] = {'\0'};
    char *title = NULL;
    char units[16] = {'\0'};

    bu_setprogname(argv[0]);

    if (!get_args(argc, argv)) {
	(void)fputs(usage, stderr);
	return 1;
    }

    if (version != 1 && version != 4 && version != 5) {
	fprintf(stderr, "version %d not supported\n", version);
	(void)fputs(usage, stderr);
	return 1;
    }

    printf("Reading version %d COMGEOM file\n", version);

    if (verbose) {
	printf("COMGEOM input file (version %d) must have this format:\n", version);
	switch (version) {
	    case 1:
		printf("     1.  title card\n");
		printf("     2.  solid table\n");
		printf("     3.  END\n");
		printf("     4.  region table\n");
		printf("     5.  END\n");
		break;
	    case 4:
		printf("     1.  units & title card\n");
		printf("     2.  solid & region count card\n");
		printf("     3.  solid table\n");
		printf("     4.  region table\n");
		printf("     5.  -1\n");
		printf("     6.  blank\n");
		printf("     7.  region ident table\n\n");
		break;
	    case 5:
		printf("     1.  units & title card\n");
		printf("     2.  solid & region count card\n");
		printf("     3.  solid table\n");
		printf("     4.  region table\n");
		printf("     5.  -1\n");
		printf("     6.  region ident table\n\n");
		break;
	}
    }

    group_init();

    /*
     * Read title card
     */
    if (get_line(ctitle, sizeof(ctitle), "title card") == EOF) {
	printf("Empty input file:  no title record\n");
	return 10;
    }

    title = ctitle+3;
    switch (version) {
	case 1:
	    title = ctitle;
	    bu_strlcpy(units, "in", sizeof(units));
	    break;
	case 4:
	case 5:
	    /* First 2 chars are units */
	    units[0] = ctitle[0];
	    units[1] = ctitle[1];
	    units[2] = '\0';
	    break;
    }

    /* Drop leading blanks in title */
    while (isspace((int)*title))
	title++;
    trim_trail_spaces(title);
    trim_trail_spaces(units);

    /* Convert units to lower case */
    {
	char *cp = units;
	while (*cp) {
	    if (isupper((int)*cp))
		*cp = tolower((int)*cp);
	    cp++;
	}
    }

    printf("Title: %s\n", title);
    printf("Units: %s\n", units);

    /* Before converting any geometry, establish the units conversion
     * factor which all mk_* routines will apply.
     */
    if (mk_conversion(units) < 0) {
	printf("WARNING:  unknown units '%s', using inches\n", units);
	bu_strlcpy(units, "in", sizeof(units));
	(void)mk_conversion(units);
    }

    /* Output the MGED database header */
    if (mk_id_units(outfp, title, units) < 0) {
	printf("Unable to write database ID, units='%s'\n", units);
	return 1;
    }

    /*
     * Read control card, if present
     */
    sol_total = reg_total = 0;
    switch (version) {
	case 1:
	    sol_total = reg_total = 9999;	/* Reads until 'END' rec */
	    break;

	case 4:
	    if (get_line(ctitle, sizeof(ctitle), "control card") == EOF) {
		printf("No control card .... STOP\n");
		return 10;
	    }
	    if (sscanf(ctitle, "%20d%10d", &sol_total, &reg_total) != 2) {
		printf("ctitle sscanf failed .... STOP\n");
		return 10;
	    }
	    break;
	case 5:
	    if (get_line(ctitle, sizeof(ctitle), "control card") == EOF) {
		printf("No control card .... STOP\n");
		return 10;
	    }
	    if (sscanf(ctitle, "%5d%5d", &sol_total, &reg_total) != 2) {
		printf("ctitle sscanf failed .... STOP\n");
		return 10;
	    }
	    break;
    }

    if (verbose) printf("Expecting %d solids, %d regions\n", sol_total, reg_total);


    /*
     * SOLID TABLE
     */
    if (verbose) printf("Primitive table\n");
    sol_work = 0;
    while (sol_work < sol_total) {
	i = getsolid();
	if (i < 0) {
	    printf("error converting primitive %d\n", sol_work);
	    /* Should we abort here? */
	    continue;
	}
	if (i > 0) {
	    printf("\nprocessed %d of %d solids\n\n", sol_work, sol_total);
	    if (sol_work < sol_total && version > 1) {
		printf("some solids are missing, aborting\n");
		return 1;
	    }
	    break;		/* done */
	}
    }

    /* REGION TABLE */

    if (verbose)printf("\nRegion table\n");

    wmp = (struct wmember *)bu_calloc(reg_total+2, sizeof(struct wmember), "wmp");

    for (i = reg_total + 1; i >= 0; i--) {
	BU_LIST_INIT(&wmp[i].l);
    }

    cur_col = 0;
    if (getregion() < 0) {
	return 10;
    }

    if (version == 1) {
	for (i = 1; i < reg_total; i++) {
	    region_register(i, 0, 0, 0, 0);
	}
    } else {
	if (version == 4) {
	    char dummy[88];
	    /* read the blank card (line) */
	    (void)get_line(dummy, sizeof(dummy), "blank card");
	}

	if (verbose) printf("\nRegion ident table\n");
	getid();
    }

    if (verbose) printf("\nGroups\n");
    cur_col = 0;
    group_write();
    if (verbose) printf("\n");

    return 0;
}


void
col_pr(char *str)
{
    if (!str) return;
    printf("%s", str);
    cur_col += strlen(str);
    while (cur_col < 78 && ((cur_col%10) > 0)) {
	putchar(' ');
	cur_col++;
    }
    if (cur_col >= 78) {
	printf("\n");
	cur_col = 0;
    }
}


/*
 * Local Variables:
 * mode: C
 * tab-width: 8
 * indent-tabs-mode: t
 * c-file-style: "stroustrup"
 * End:
 * ex: shiftwidth=4 tabstop=8
 */
