/*
 * error.c: buttress error handling
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "buttress.h"

/*
 * Error flags
 */
#define PREFIX 0x0001		       /* give `buttress:' prefix */
#define FILEPOS 0x0002		       /* give file position prefix */

static void do_error(int code, va_list ap) {
    char error[1024];
    char auxbuf[256];
    char *sp;
    wchar_t *wsp;
    filepos fpos;
    int flags;

    switch(code) {
      case err_nomemory:	       /* no arguments */
	sprintf(error, "out of memory");
	flags = PREFIX;
	break;
      case err_optnoarg:
	sp = va_arg(ap, char *);
	sprintf(error, "option `-%.200s' requires an argument", sp);
	flags = PREFIX;
	break;
      case err_nosuchopt:
	sp = va_arg(ap, char *);
	sprintf(error, "unrecognised option `-%.200s'", sp);
	flags = PREFIX;
	break;
      case err_noinput:		       /* no arguments */
	sprintf(error, "no input files");
	flags = PREFIX;
	break;
      case err_cantopen:
	sp = va_arg(ap, char *);
	sprintf(error, "unable to open input file `%.200s'", sp);
	flags = PREFIX;
	break;
      case err_nodata:		       /* no arguments */
	sprintf(error, "no data in input files");
	flags = PREFIX;
	break;
      case err_brokencodepara:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "every line of a code paragraph should begin `\\c'");
	flags = FILEPOS;
	break;
      case err_kwunclosed:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected `}' after paragraph keyword");
	flags = FILEPOS;
	break;
      case err_kwexpected:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected a paragraph keyword");
	flags = FILEPOS;
	break;
      case err_kwillegal:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected no paragraph keyword");
	flags = FILEPOS;
	break;
      case err_kwtoomany:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected only one paragraph keyword");
	flags = FILEPOS;
	break;
      case err_bodyillegal:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected no text after paragraph keyword");
	flags = FILEPOS;
	break;
      case err_badparatype:
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "command `%.200s' unrecognised at start of"
		" paragraph", sp);
	flags = FILEPOS;
	break;
      case err_badmidcmd:
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "command `%.200s' unexpected in mid-paragraph", sp);
	flags = FILEPOS;
	break;
      case err_unexbrace:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "brace character unexpected in mid-paragraph");
	flags = FILEPOS;
	break;
      case err_explbr:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected `{' after command");
	flags = FILEPOS;
	break;
      case err_commenteof:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "end of file unexpected inside `\\#{...}' comment");
	flags = FILEPOS;
	break;
      case err_kwexprbr:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected `}' after cross-reference");
	flags = FILEPOS;
	break;
      case err_missingrbrace:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "unclosed braces at end of paragraph");
	flags = FILEPOS;
	break;
      case err_nestedstyles:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "unable to nest text styles");
	flags = FILEPOS;
	break;
      case err_nestedindex:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "unable to nest index markings");
	flags = FILEPOS;
	break;
      case err_nosuchkw:
	fpos = *va_arg(ap, filepos *);
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	sprintf(error, "unable to resolve cross-reference to `%.200s'", sp);
	flags = FILEPOS;
	break;
      case err_multiBR:
	fpos = *va_arg(ap, filepos *);
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	sprintf(error, "multiple `\\BR' entries given for `%.200s'", sp);
	flags = FILEPOS;
	break;
      case err_nosuchidxtag:
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	sprintf(error, "`\\IM' on unknown index tag `%.200s'", sp);
	flags = 0;
	/* FIXME: need to get a filepos to here somehow */
	break;
      case err_cantopenw:
	sp = va_arg(ap, char *);
	sprintf(error, "unable to open output file `%.200s'", sp);
	flags = PREFIX;
	break;
      case err_macroexists:
	fpos = *va_arg(ap, filepos *);
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	sprintf(error, "macro `%.200s' already defined", sp);
	flags = FILEPOS;
	break;
      case err_sectjump:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected higher heading levels before this one");
	flags = FILEPOS;
	break;
      case err_whatever:
	sp = va_arg(ap, char *);
        vsprintf(error, sp, ap);
        flags = PREFIX;
        break;
    }

    if (flags & PREFIX)
	fputs("buttress: ", stderr);
    if (flags & FILEPOS) {
	fprintf(stderr, "%s:%d:", fpos.filename, fpos.line);
	if (fpos.col > 0)
	    fprintf(stderr, "%d:", fpos.col);
	fputc(' ', stderr);
    }
    fputs(error, stderr);
    fputc('\n', stderr);
}

void fatal(int code, ...) {
    va_list ap;
    va_start(ap, code);
    do_error(code, ap);
    va_end(ap);
    exit(EXIT_FAILURE);
}

void error(int code, ...) {
    va_list ap;
    va_start(ap, code);
    do_error(code, ap);
    va_end(ap);
}
