/*
 * fax2iff.c
 *
 * $Id: fax2iff.c,v 1.5 1993/10/25 02:14:27 Rhialto Exp $
 * $Log: fax2iff.c,v $
 * Revision 1.5  1993/10/25  02:14:27  Rhialto
 * Make +FBOR flexible; fix RTC detection bug.
 *
 * Revision 1.4  1993/08/20  02:48:24  Rhialto
 * Add hack for auto-detecting fax bit order, and an option
 * to specify it explicitly.
 *
 * Revision 1.3  1993/07/01  00:44:20  Rhialto
 * Add CMAP and CAMG chunk. Create CAT only for multiple input files.
 *
 * Revision 1.2  1993/06/11  16:33:37  Rhialto
 * First real RCS checkin
 *
 */

#include <stdlib.h>

#include "iffp/iff.h"
#include "iffp/ilbm.h"
#include "iffp/packer.h"
#include "faxfile.h"

#ifdef DEBUG
#define debug(x)    printf x
#else
#define debug(x)
#endif

typedef struct faxin {
	int	 raw;
	FAXHDR	 faxhdr;
	FAXHDR	 pagehdr;
	int	 row;
	int	 endoffile;
	int	 eols;
	int	 rawzeros;
	int	 shdata;
	int	 shbit;
	int	 kludge;
	int	 reversebits;
	int	 stretch;
	unsigned char  *bitp;
	unsigned char	bitval;

} FAXIN;

extern FAXIN faxin;

struct ParseInfo ParseInfo;
int		verbose;
int		invert;
int		bitorder = -1;
int		nocat;

/* Assumes malloc()ed pointer */
void
meminvert(unsigned char *d, int size)
{
    while (size >= 4) {
	*(long *)d ^= 0xFFFFFFFF;
	d += 4;
	size -= 4;
    }
    while (size > 0) {
	*d++ ^= 0xFF;
	size--;
    }
}

long
dobody(FILE *faxfile, struct ParseInfo *pi)
{
    unsigned char  *planedata;
    unsigned char  *bodydata;
    int 	    planedatasize;
    int 	    bodydatasize;
    int 	    lines;

    planedatasize = BytesPerRow(LINE_BITS);
    bodydatasize  = MaxPackedSize(BytesPerRow(LINE_BITS));
    planedata = malloc(4 + planedatasize);
    bodydata  = malloc(4 + bodydatasize);

    for (lines=0;;lines++) {
	unsigned char  *p, *b;
	long		size;

	memset(planedata, 0, planedatasize);
	size = fromfax(faxfile, planedata);
	if (size == -1) {
	    debug(("size = -1 = fromfax()\n"));
	    break;
	}
	if (invert)
	    meminvert(planedata, planedatasize);
	p = planedata;
	b = bodydata;
	size = PackRow(&p, &b, LINE_BITS / 8);
	WriteChunkBytes(pi->iff, bodydata, size);
    }

    free(bodydata);
    free(planedata);

    return lines;
}

long
dopage(FILE *faxfile, struct ParseInfo *pi)
{
    long	    bmhdpos;
    long	    currpos;
    int 	    lines;
    FILE	   *ifffile;
    UBYTE	    cm[] = {
			255, 255, 255,	    /* color 0: white */
			0,   0,   0,	    /* color 1: black */
		    };
    CamgChunk	    camg = { HIRES | LACE };
    BitMapHeader    bmhd = {
			LINE_BITS, 9999,    /* w, h (unknown yet) */
			0, 0,		    /* x, y */
			1,		    /* nPlanes */
			mskNone,	    /* masking */
			cmpByteRun1,	    /* compression */
			0,		    /* reserved1 */
			0,		    /* transparentcolor */
			X_DPI, Y_DPI,    /* xAspect, yAspect */
			LINE_BITS, 9999,    /* pageWidth, pageHeight */
		    };

    if (faxin.faxhdr.info.hires) camg.ViewModes = HIRES | LACE;
    else camg.ViewModes = HIRES;

    if (faxin.faxhdr.info.hires) bmhd.yAspect = Y_DPI;
    else bmhd.yAspect = Y_DPI / 2;

    PushChunk(pi->iff, ID_ILBM, ID_FORM, IFFSIZE_UNKNOWN);
    PutCk(pi->iff, ID_CMAP, sizeof(cm), (BYTE *)cm);
    PutCk(pi->iff, ID_CAMG, sizeof(camg), (BYTE *)&camg);

    /*
     * This is DIRTY DIRTY DIRTY!!
     * We need to update the BitMapHeader later on because we
     * don't know the y size yet.
     */
    ifffile = (FILE *)pi->iff->iff_Stream;
    bmhdpos = ftell(ifffile) + 8;
    putbmhd(pi->iff, &bmhd);

    PushChunk(pi->iff, ID_ILBM, ID_BODY, IFFSIZE_UNKNOWN);

    lines = dobody(faxfile, pi);
    debug(("lines = %d = dobody()\n", lines));

    PopChunk(pi->iff);  /* BODY */
    PopChunk(pi->iff);  /* FORM ILBM */

    /* Update BMHD */
    bmhd.h = lines;
    bmhd.pageHeight = lines;
    currpos = ftell(ifffile);
    if (fseek(ifffile, bmhdpos, SEEK_SET) == 0) {
	fwrite(&bmhd, sizeof(bmhd), 1, ifffile);
	fseek(ifffile, currpos, SEEK_SET);
    } else {
	fprintf(stderr, "fseek on iff-file failed\n");
    }

    return 0;
}

long
dofile(char *faxname, struct ParseInfo *pi)
{
    FILE	   *faxfile;
    long	    error;

    faxfile = fopen(faxname, "rb");
    if (faxfile == NULL)
	return 1;

    error = faxin_open_fp(faxfile, 0, bitorder);
    debug(("%d = faxin_open_fp(%s)\n", error, faxname));

    while (error == 0 && faxin_begin_page(faxfile) == 0) {
	debug(("0 = faxin_begin_page\n"));
	error = dopage(faxfile, pi);
	debug(("%d = dopage()\n", error));
    }

error:
    fclose(faxfile);
    return error;
}


/*
 * Clean up system stuff in case of exit
 */
void
cleanup(void)
{
    if (IFFParseBase) {
	if (ParseInfo.iff) {
	    closeifile(&ParseInfo);
	    FreeIFF(ParseInfo.iff);
	}
	CloseLibrary(IFFParseBase);
    }
}

int
main(int argc, char **argv)
{
    struct IFFHandle *iff;
    char	   *outfile = "fax.iff";
    extern char    *optarg;
    extern int	    optind;
    extern int	    getopt(int, char **, char *);
    int 	    errflg = 0;
    int 	    c;
    int openError;

    while ((c = getopt(argc, argv, "1b:io:v")) != -1) {
	switch (c) {
	case '1':
	    nocat = 1;
	    break;
	case 'b':
	    bitorder = *optarg ? atoi(optarg) : 1;
	    break;
	case 'i':
	    invert = 1;
	    break;
	case 'o':
	    outfile = optarg;
	    break;
	case 'v':
	    verbose = TRUE;
	    break;
	case '?':
	    errflg++;
	    break;
	}
    }

    if (errflg || optind >= argc) {
	printf(
"Usage: fax2iff [-o iff-file (fax.iff)] [-v] [-i (invert)] [-1]\n"
"       [-b0/1 (bitorder)] fax-files\n");
	exit(10);
    }

    atexit(cleanup);

    IFFParseBase = OpenLibrary("iffparse.library", 0);
    if (IFFParseBase == NULL) {
	printf("Needs iffparse.library.\n");
	exit(10);
    }

    if (optind == argc - 1)
	nocat = !nocat;
    iff = AllocIFF();
    ParseInfo.iff = iff;
    openError = openifile(&ParseInfo, outfile, IFFF_WRITE);

    if (!nocat)
	PushChunk(iff, ID_ILBM, ID_CAT, IFFSIZE_UNKNOWN);

    if (!openError)
      while (optind < argc) {
	debug(("calling dofile '%s'\n", argv[optind]));
	dofile(argv[optind], &ParseInfo);
	optind++;
	if (nocat)
	  break;
      }

    if (!nocat)
	PopChunk(iff);          /* CAT  */
    closeifile(&ParseInfo);
    FreeIFF(iff);
    ParseInfo.iff = NULL;

    /*CloseLibrary(IFFParseBase);*/
    return 0;
}
