/*
 * asc2fax.c
 *
 * Copyright (C) 1993 by Olaf 'Rhialto' Seibert. All rights reserved.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define INTUI_V36_NAMES_ONLY
#include <utility/tagitem.h>
#include <intuition/intuition.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/diskfont_protos.h>

#include "faxfile.h"

#define RASTERWIDTH LINE_BITS
#define ESC	    "\33"
#define CSI	    "\233"

int		verbose;
int		xoffset = 50;
int		yoffset = 50;
int		invert;
void	       *IntuitionBase;
void	       *GfxBase;
void	       *DiskFontBase;
struct BitMap	BitMap;
struct RastPort EmergencyRastPort;
struct RastPort *RastPort;
struct Window  *Window;
struct TextFont *Font;
struct TextFont *OldFont;
struct NewWindow NewWindow = {
    0, 20,		    /* LeftEdge, TopEdge */
    640, 0,		    /* Width, Height (calculated and set) */
    1, 1,		    /* DetailPen, BlockPen */
    0,			    /* IDCMPFlags */
    WFLG_SUPER_BITMAP | WFLG_GIMMEZEROZERO | WFLG_NOCAREREFRESH |
	WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SIZEGADGET | WFLG_SIZEBRIGHT, /* Flags */
    NULL,		    /* FirstGadget */
    NULL,		    /* CheckMark */
    NULL,		    /* Title */
    NULL,		    /* Screen */
    &BitMap,		    /* BitMap */
    20,20, -1,0,	    /* Min/Max Width/Height */
    WBENCHSCREEN	    /* Type */
};
struct TagItem TextTags[] = {
    TA_DeviceDPI, X_DPI | Y_DPI << 16,
    TAG_END
};
struct TTextAttr TextAttr = {
    "courier.font", 30, FSF_TAGGED, 0, TextTags
};

void
meminvert(unsigned char *d, int size)
{
    if (((long) d & 0x01) == 0) {
	while (size >= 4) {
	    *(long *)d ^= 0xFFFFFFFF;
	    d += 4;
	    size -= 4;
	}
    }
    while (size > 0) {
	*d++ ^= 0xFF;
	size--;
    }
}

struct TextInfo {
    struct RastPort *rp;
    int 	    xoffset;
} ti;

char	       *
DoText(char *text, int len)
{
    if (len > 0)
	Text(ti.rp, text, len);
    return text + len;
}

char	       *
DoCSI(unsigned char *text)
{
    int 	    arg[8];
    int 	    narg = 0;
    int 	    i;

    memset(arg, 0, sizeof(arg));
    /* CSI 0 x and CSI x are indistinguishable */
    for(;;text++) {
	while (isdigit(text[0])) {
	    if (narg < 8) {
		arg[narg] *= 10;
		arg[narg] += text[0] - '0';
	    }
	    text++;
	}
	narg++;
	if (text[0] != ';')
	    break;
    }
    if (narg > 8)
	narg = 8;

    switch (*text++) {
    case 'm':   /* Select graphic rendition */
	for (i = 0; i < narg; i++) {
	    switch (arg[i]) {
	    case 0:	/* plain */
		SetSoftStyle(ti.rp, FS_NORMAL, FSF_BOLD|FSF_ITALIC|FSF_UNDERLINED);
		SetDrMd(ti.rp, JAM1);
		break;
	    case 1:	/* bold */
		SetSoftStyle(ti.rp, FSF_BOLD, FSF_BOLD);
		break;
	    case 3:	/* italic */
		SetSoftStyle(ti.rp, FSF_ITALIC, FSF_ITALIC);
		break;
	    case 4:	/* underline */
		SetSoftStyle(ti.rp, FSF_UNDERLINED, FSF_UNDERLINED);
		break;
	    case 7:	/* inverse video */
		SetDrMd(ti.rp, JAM1 | INVERSVID);
		break;
	    case 22:	 /* not bold */
		SetSoftStyle(ti.rp, 0, FSF_BOLD);
		break;
	    case 23:	 /* not italic */
		SetSoftStyle(ti.rp, 0, FSF_ITALIC);
		break;
	    case 24:	 /* not underline */
		SetSoftStyle(ti.rp, 0, FSF_UNDERLINED);
		break;
	    case 27:	 /* not inverse video */
		SetDrMd(ti.rp, JAM1);
		break;
	    }
	}
	break;
    case 'x':           /* set left offset */
	ti.xoffset = arg[0];
	break;
    }

    return text;
}

char	       *
DoCtrl(unsigned char *text)
{
    switch (*text++) {
    case '\t':          /* tab */
	{
	    int 	    charpos;

	    charpos = (ti.rp->cp_x - ti.xoffset) / ti.rp->TxWidth;
	    charpos = (charpos + 8) & ~7;
	    Move(ti.rp, ti.xoffset + charpos * ti.rp->TxWidth, ti.rp->cp_y);
	}
	break;
    case '\n':          /* newline */
	Move(ti.rp, ti.xoffset, ti.rp->cp_y + ti.rp->TxHeight);
	break;
    case '\f':          /* formfeed */
	SetRast(ti.rp, 0);
	Move(ti.rp, ti.xoffset, ti.rp->TxBaseline);
	break;
    case '\233':        /* control sequence introducer */
	goto csi;
    case '\033':        /* escape */
	switch (*text++) {
	case 'c':       /* reset */
	    ti.xoffset = 0;
	    ti.rp->Mask = 0x0001;
	    SetAPen(ti.rp, 1);
	    SetBPen(ti.rp, 0);
	    DoCtrl("\f");
	    DoCSI("0m");
	    break;
	case '[':       /* CSI */
	csi:
	    text = DoCSI(text);
	    break;
	}
	break;
    }

    return text;
}

void
WinWrite(char *text)
{
    while (*text) {
	char	       *p;
	int		len;

	/* First, determine how much real text we have */
	for (len = 0, p = text; isprint((unsigned char)*p); p++)
	    len++;
	text = DoText(text, len);
	if (!isprint(*text))
	    text = DoCtrl(text);
    }
}

void
WinWriteInit(struct RastPort *rp)
{
    ti.rp = rp;
    DoCtrl(ESC"c");
}


long
dofile(char *ascname, void *faxp)
{
    char	    line[256];
    FILE	   *file;
    struct RastPort *rp = RastPort;

    file = fopen(ascname, "r");
    if (file == NULL) {
	printf("Can't open file %s.\n", ascname);
	return 1;
    }

    faxout_begin_page(faxp, 1);

    WinWriteInit(RastPort);
    /* reset, set x offset */
    sprintf(line, ESC"c" CSI"%dx", xoffset);
    WinWrite(line);

    if (yoffset) {
	int		i;

	for (i = 0; i < yoffset; i++)
	    tofax(faxp, BitMap.Planes[0], RASTERWIDTH);
    }

    for (;;) {
	int		i;
	char	       *plane;

	if (feof(file))
	    break;
	if (fgets(line, sizeof(line), file) == NULL)
	    break;
	if (plane = strchr(line, '\n'))
	    *plane = '\0';

	if (verbose)
	    printf("%s\n", line);
	WinWrite("\f");
	WinWrite(line);

	SyncSBitMap(rp->Layer);
	plane = BitMap.Planes[0];
	for (i = 0; i < Font->tf_YSize; i++) {
	    if (invert)
		meminvert(plane, BitMap.BytesPerRow);
	    tofax(faxp, plane, RASTERWIDTH);
	    plane += BitMap.BytesPerRow;
	}
    }

    faxout_end_page(faxp);
    fclose(file);

    return 0;
}

void
openall(void)
{
    /* Libraries */
    IntuitionBase = OpenLibrary("intuition.library", 33);
    if (IntuitionBase == NULL) {
	printf("Needs intuition V33+.\n");
	exit(10);
    }
    GfxBase = OpenLibrary("graphics.library", 33);
    if (GfxBase == NULL) {
	printf("Needs gfx V33+.\n");
	exit(10);
    }
    DiskFontBase = OpenLibrary("diskfont.library", 34);
    if (DiskFontBase == NULL) {
	printf("Needs diskfont V34+.\n");
	exit(10);
    }
    /* Font for window; sorry for the strange order */
    printf("Opening DiskFont... (This may take a while)\n");
    Font = OpenDiskFont((struct TextAttr *)&TextAttr);
    if (Font == NULL) {
	printf("No %s %d!\n", TextAttr.tta_Name, TextAttr.tta_YSize);
	exit(10);
    }
    NewWindow.Height = Font->tf_YSize + 16;  /* slight safety fudge */
    /* Raster for text */
    InitBitMap(&BitMap, 1, RASTERWIDTH, NewWindow.Height);
    if ((BitMap.Planes[0] = AllocRaster(RASTERWIDTH, NewWindow.Height)) == NULL) {
	printf("No plane\n");
	exit(10);
    }
    /* Window for raster. For showing-off purposes only. */
    if ((Window = OpenWindow(&NewWindow)) == NULL) {
	printf("No window (probably too large). Will do without.\n");
    }
    if (Window) {
	RastPort = Window->RPort;
    } else {
	RastPort = &EmergencyRastPort;
	InitRastPort(RastPort);
	RastPort->BitMap = &BitMap;
    }
    RastPort->Mask = 0x0001;
    OldFont = RastPort->Font;
    SetFont(RastPort, Font);
}


/*
 * Clean up system stuff in case of exit
 */
void
cleanup(void)
{
    if (Font) {
	SetFont(RastPort, OldFont);
	CloseFont(Font);
    }
    if (Window) {
	CloseWindow(Window);
    }
    if (GfxBase) {
	if (BitMap.Planes[0]) {
	    FreeRaster(BitMap.Planes[0], RASTERWIDTH, NewWindow.Height);
	    BitMap.Planes[0] = NULL;
	}
	CloseLibrary(GfxBase);
    }
    if (IntuitionBase) {
	CloseLibrary(IntuitionBase);
    }
    if (DiskFontBase) {
	CloseLibrary(DiskFontBase);
    }
}

int
main(int argc, char **argv)
{
    char	   *outfile = "ascii.g3";
    struct faxout  *faxp;
    int 	    rawfax = 1;
    int 	    append = 0;
    extern char    *optarg;
    extern int	    optind;
    extern int	    getopt(int, char **, char *);
    int 	    errflg = 0;
    int 	    c;

    while ((c = getopt(argc, argv, "af:io:rs:vx:y:")) != -1) {
	switch (c) {
	case 'a':
	    append = 1;
	    break;
	case 'f':
	    TextAttr.tta_Name = optarg;
	    break;
	case 's':
	    TextAttr.tta_YSize = atoi(optarg);
	    break;
	case 'i':
	    invert = 1;
	    break;
	case 'o':
	    outfile = optarg;
	    break;
	case 'r':
	    rawfax++;
	    break;
	case 'v':
	    verbose = TRUE;
	    break;
	case 'x':
	    xoffset = atoi(optarg);
	    break;
	case 'y':
	    yoffset = atoi(optarg);
	    break;
	case '?':
	    errflg++;
	    break;
	}
    }

    if (errflg || optind >= argc) {
	printf(
"Usage: asc2fax [-o fax-file (ascii.g3)] [-r raw faxfile] [-a (append)]\n"
"               [-x/y x/y-offset (50)] [-v] [-i (invert)]\n"
"               [-f name.font] [-s fontsize] ascii-files\n");
	exit(EXIT_FAILURE);
    }

    atexit(cleanup);
    openall();

    faxp = faxout_open_fp(fopen(outfile, append? "ab": "wb"), rawfax);
    if (faxp == NULL) {
	fprintf(stderr, "can't open output file %s.\n", outfile);
	goto fail;
    }

    while (optind < argc) {
	dofile(argv[optind], faxp);
	optind++;
    }

    faxout_close(faxp);
fail:
    /* atexit function cleans up here */
}
