/* pbmtog3.c - read a portable bitmap and produce a Group 3 FAX file
**
** Copyright (C) 1989 by Paul Haeberli <paul@manray.sgi.com>.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/

#include "pbm.h"
#include "shhopt.h"
#include "g3.h"

static int reversebits;

struct cmdlineInfo {
    /* All the information the user supplied in the command line,
       in a form easy for the program to use.
    */
    const char *inputFilespec;  /* Filespec of input file */
    unsigned int reversebits;
    unsigned int nofixedwidth;
    unsigned int verbose;
};


static int shdata;
static int shbit;



static void
parseCommandLine(int argc, char ** const argv,
                 struct cmdlineInfo * const cmdlineP) {
/*----------------------------------------------------------------------------
   Note that the file spec array we return is stored in the storage that
   was passed to us as the argv array.
-----------------------------------------------------------------------------*/
    optEntry *option_def = malloc(100*sizeof(optEntry));
        /* Instructions to OptParseOptions2 on how to parse our options.
         */
    optStruct3 opt;

    unsigned int option_def_index;

    option_def_index = 0;   /* incremented by OPTENTRY */
    OPTENT3(0,   "reversebits",      OPT_FLAG,  NULL, &cmdlineP->reversebits,
            0);
    OPTENT3(0,   "nofixedwidth",     OPT_FLAG,  NULL, &cmdlineP->nofixedwidth,
            0);
    OPTENT3(0,   "verbose",          OPT_FLAG,  NULL, &cmdlineP->verbose, 
            0);

    opt.opt_table = option_def;
    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
    opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */

    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
        /* Uses and sets argc, argv, and some of *cmdlineP and others. */

    if (argc-1 == 0) 
        cmdlineP->inputFilespec = "-";
    else if (argc-1 != 1)
        pm_error("Program takes zero or one argument (filename).  You "
                 "specified %d", argc-1);
    else
        cmdlineP->inputFilespec = argv[1];

}



static void
putbit(d)
    int d;
{
    if(d) 
        shdata = shdata|shbit;
    if ( reversebits )
        shbit = shbit<<1;
    else
        shbit = shbit>>1;
    if((shbit&0xff) == 0) {
        putchar(shdata);
        shdata = 0;
        shbit = reversebits ? 0x01 : 0x80;
    }
}

static void
flushbits( )
{
    if ( ( reversebits && shbit != 0x01 ) ||
         ( ! reversebits && shbit != 0x80 ) ) {
        putchar(shdata);
        shdata = 0;
        shbit = reversebits ? 0x01 : 0x80;
    }
}



static void
putcode(te)
    tableentry* te;
{
    unsigned int mask;
    int code;

    mask = 1<<(te->length-1);
    code = te->code;
    while(mask) {
        if(code&mask)
            putbit(1);
        else
            putbit(0);
        mask >>= 1;
    }

}

static void
puteol()
{
    int i;

    for(i=0; i<11; ++i)
        putbit(0);
    putbit(1);
}

static void putblackspan(int c);

static void
putwhitespan(c)
    int c;
{
    int tpos;
    tableentry* te;

    do {
        if(c>=64) {
            if (c < 1792) {
                tpos = (c - 64) / 64;
                te = mwtable+tpos;
            }
            else {
                tpos = (c - 1792) / 64;
                if (tpos > ((2560 - 1792) / 64))
                    tpos = (2560 - 1792) / 64;
                te = extable+tpos;
            }
            c -= te->count;
            putcode(te);
        }
        tpos = c;
        if (tpos >= 64)
            tpos = 63;
        te = twtable+tpos;
        c -= te->count;
        putcode(te);
        if (c)
            putblackspan(0);
    } while (c);
}

static void
putblackspan(c)
    int c;
{
    int tpos;
    tableentry* te;

    do {
        if(c>=64) {
            if (c < 1792) {
                tpos = (c - 64) / 64;
                te = mbtable+tpos;
            }
            else {
                tpos = (c - 1792) / 64;
                if (tpos > ((2560 - 1792) / 64))
                    tpos = (2560 - 1792) / 64;
                te = extable+tpos;
            }
            c -= te->count;
            putcode(te);
        }
        tpos = c;
        if (tpos >= 64)
            tpos = 63;
        te = tbtable+tpos;
        c -= te->count;
        putcode(te);
        if (c)
            putwhitespan(0);
    } while (c);
}

static void
tofax(bitrow,n)
    bit* bitrow;
    int n;
{
    int c;

    while(n>0) {
        c = 0;
        while(*bitrow == PBM_WHITE && n>0) {
            ++bitrow;
            ++c;
            --n;
        }
        putwhitespan(c);
        c = 0;
        if(n==0)
            break;
        while(*bitrow == PBM_BLACK && n>0) {
            ++bitrow;
            ++c;
            --n;
        }
        putblackspan(c);
    }
    puteol();
}

static void
putinit()
{
    shdata = 0;
    shbit = reversebits ? 0x01 : 0x80;
}

int
main( argc, argv )
    int argc;
    char* argv[];
{
    struct cmdlineInfo cmdline;
    FILE* ifP;
    bit* bitrow;
    int rows, cols, bigcols, format, row, i;

    pbm_init( &argc, argv );

    parseCommandLine(argc, argv, &cmdline);

    ifP = pm_openr(cmdline.inputFilespec);

    reversebits = cmdline.reversebits;

    pbm_readpbminit( ifP, &cols, &rows, &format );
    if (cmdline.nofixedwidth)
        bigcols = cols;
    else
        bigcols = 1728;
    bitrow = pbm_allocrow( bigcols );

    /* Write out four extra rows to get things stabilized. */
    putinit();
    puteol();

    /* Write out bitmap. */
    for ( row = 0; row < rows; ++row )
    {
        for ( i = cols; i < bigcols; i ++ )
            bitrow[i] = PBM_WHITE;
        pbm_readpbmrow( ifP, bitrow, cols, format );
        tofax( bitrow, bigcols );
    }

    /* And finish off. */
    for( i = 0; i < 6; ++i )
        puteol( );
    flushbits( );

    pm_close( ifP );

    exit( 0 );
}


