/* pbmtoppa.c
 * program to print a 600 dpi PBM file on the HP DJ820Cse or the HP720 series.
 * Copyright (c) 1998 Tim Norman.  See LICENSE for details
 * 2-24-98
 */

#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
#define _BSD_SOURCE
    /* This makes sure strcasecmp() is in string.h */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "pbm.h"
#include "nstring.h"

#include "ppa.h"
#include "ppapbm.h"
#include "cutswath.h"

#include "defaults.h"

/* Paper sizes in 600ths of an inch. */

/* US is 8.5 in by 11 in */

#define USWIDTH  (5100)
#define USHEIGHT (6600)

/* A4 is 210 mm by 297 mm == 8.27 in by 11.69 in */

#define A4WIDTH  (4960)
#define A4HEIGHT (7016)

int Width;      /* width and height in 600ths of an inch */
int Height;
int Pwidth;     /* width in bytes */

#define MAX_LINES 300

ppa_stat printer;

static int
printPbm(FILE * const ifP) {

    char line[1024];
    pbm_stat pbm;
    int done_page, numpages = 0;
    ppa_sweep_data sweeps[2];
    int current_sweep, previous_sweep;

    ppa_init_job(&printer);

    while (make_pbm_stat(&pbm, ifP)) {
        if (pbm.width != Width || pbm.height != Height)
            pm_error("print_pbm(): Input image is not the size "
                    "of a page for Page %d.  "
                    "The input is %dW x %dH, "
                     "while a page is %dW x %dH pixels.  "
                    "Page size is controlled by options and the configuration "
                    "file.",
                     numpages+1, pbm.width, pbm.height, Width, Height);

        ppa_init_page(&printer);
        ppa_load_page(&printer);

        sweeps[0].direction   = right_to_left;
        sweeps[0].image_data  = NULL;
        sweeps[0].nozzle_data = NULL;
        sweeps[0].next=&sweeps[1];

        sweeps[1].direction   = left_to_right;
        sweeps[1].image_data  = NULL;
        sweeps[1].nozzle_data = NULL;
        sweeps[1].next=&sweeps[0];

        current_sweep=0;
        previous_sweep=-1;

        done_page=0;
        while(!done_page) {
            int rc;
            rc = cut_pbm_swath(&pbm, &printer, MAX_LINES,
                               &sweeps[current_sweep]);
            switch(rc) {
            case 0:
                pm_error("print_pbm(): cut_pbm_swath() failed.");
                break;
            case 1:
                done_page=1;
                break;
            case 2:
                if (previous_sweep >= 0) {
                    ppa_print_sweep(&printer, &sweeps[previous_sweep]);
                    free(sweeps[previous_sweep].image_data);
                    free(sweeps[previous_sweep].nozzle_data);
                    sweeps[previous_sweep].image_data = NULL;
                    sweeps[previous_sweep].nozzle_data = NULL;
                }
                previous_sweep=current_sweep;
                current_sweep= current_sweep==0 ? 1 : 0;
                break;
            default:
                pm_error("print_pbm(): unknown return code from "
                         "cut_pbm_swath()");
            }
        }
        if (previous_sweep >= 0) {
            sweeps[previous_sweep].next=NULL;
            ppa_print_sweep(&printer,&sweeps[previous_sweep]);
        }

        free(sweeps[0].image_data);
        free(sweeps[0].nozzle_data);
        free(sweeps[1].image_data);
        free(sweeps[1].nozzle_data);
        sweeps[0].image_data = NULL;
        sweeps[0].nozzle_data = NULL;
        sweeps[1].image_data = NULL;
        sweeps[1].nozzle_data = NULL;

        ppa_eject_page(&printer);

        /* eat any remaining whitespace */
        if(pbm.version==P1)
            fgets (line, 1024, ifP);

        ++numpages;
    }

    if (numpages == 0)
        pm_error("No pages printed!");

    ppa_end_print(&printer);

    fclose (pbm.fptr);
    fclose (printer.fptr);

    return 0;
}



static void
setPrinterSpecificDefaults() {

    switch(printer.version) {
    case HP710:
        pm_error("Don't know how to drive HP 710");
        break;
    case HP720:
        printer.marg_diff     = HP720_MARG_DIFF;
        printer.bufsize       = HP720_BUFSIZE;

        printer.x_offset      = HP720_X_OFFSET;
        printer.y_offset      = HP720_Y_OFFSET;
        printer.top_margin    = HP720_TOP_MARGIN;
        printer.left_margin   = HP720_LEFT_MARGIN;
        printer.right_margin  = HP720_RIGHT_MARGIN;
        printer.bottom_margin = HP720_BOTTOM_MARGIN;
        break;
    case HP820:
        printer.marg_diff     = HP820_MARG_DIFF;
        printer.bufsize       = HP820_BUFSIZE;

        printer.x_offset      = HP820_X_OFFSET;
        printer.y_offset      = HP820_Y_OFFSET;
        printer.top_margin    = HP820_TOP_MARGIN;
        printer.left_margin   = HP820_LEFT_MARGIN;
        printer.right_margin  = HP820_RIGHT_MARGIN;
        printer.bottom_margin = HP820_BOTTOM_MARGIN;
        break;
    case HP1000:
        printer.marg_diff     = HP1000_MARG_DIFF;
        printer.bufsize       = HP1000_BUFSIZE;

        printer.x_offset      = HP1000_X_OFFSET;
        printer.y_offset      = HP1000_Y_OFFSET;
        printer.top_margin    = HP1000_TOP_MARGIN;
        printer.left_margin   = HP1000_LEFT_MARGIN;
        printer.right_margin  = HP1000_RIGHT_MARGIN;
        printer.bottom_margin = HP1000_BOTTOM_MARGIN;
        break;
    }
}



static void
show_usage(const char* const prog)
{
    printf("usage: %s [ options ] [ <infile> [ <outfile> ] ]\n\n",prog);
    printf("  Prints a pbm- or pbmraw-format <infile> to HP720/820/1000-format <outfile>.\n\n");
    printf("    -v <version>    printer version (720, 820, or 1000)\n");
    printf("    -x <xoff>       vertical offset adjustment in 1\"/600\n");
    printf("    -y <yoff>       horizontal offset adjustment in 1\"/600\n");
    printf("    -t <topmarg>    top margin in 1\"/600    (default: 150 = 0.25\")\n");
    printf("    -l <leftmarg>   left margin in 1\"/600   (default: 150 = 0.25\")\n");
    printf("    -r <rightmarg>  right margin in 1\"/600  (default: 150 = 0.25\")\n");
    printf("    -b <botmarg>    bottom margin in 1\"/600 (default: 150 = 0.25\")\n");
    printf("    -s <paper>      paper size (us, a4, default: us)\n");
    printf("    -f <cfgfile>    read <cfgfile> as parameters\n\n");
    printf("  The -x and -y options accumulate.  The -v option resets the horizontal and\n");
    printf("  vertical adjustments to an internal default.  <infile> and <outfile> default\n");
    printf("  to stdin and stdout.  '-' is a synonym for stdin and stdout.\n\n");
    printf("  Configuration files specified with the '-f' parameter have the following\n  format:\n\n");
    printf("    # Comment\n");
    printf("    <key1> <value1>\n");
    printf("    <key2> <value2>\n");
    printf("    [etc.]\n\n");
    printf("  Valid keys are 'version', 'xoffset', 'yoffset', 'topmargin', 'leftmargin',\n");
    printf("  'rightmargin', 'bottommargin', 'papersize', or any non-null truncated\n");
    printf("  version of these words.  Valid values are the same as with the corresponding\n");
    printf("  command-line parameters.  Parameters in the configuration file act as though\n");
    printf("  the corresponding parameters were substituted, in order, for the '-f'\n");
    printf("  parameter which specified the file.\n\n");
    printf("  The file /etc/pbmtoppa.conf, if it exists, is processed as a configuration\n");
    printf("  file before any command-line parameters are processed.\n\n");
}



static void
parmVersion(char * const arg) {

    if(!strcasecmp(arg,"hp720") || !strcmp(arg,"720"))
        printer.version=HP720;
    else if(!strcasecmp(arg,"hp820") || !strcmp(arg,"820"))
        printer.version=HP820;
    else if(!strcasecmp(arg,"hp1000") || !strcmp(arg,"1000"))
        printer.version=HP1000;
    else
        pm_error("parm_version(): unknown printer version '%s'",arg);
    setPrinterSpecificDefaults();
}



static void
parmIversion(int const arg) {

    switch(arg)
    {
    case 720:
        printer.version = HP720;
        break;
    case 820:
        printer.version = HP820;
        break;
    case 1000:
        printer.version = HP1000;
        break;
    default:
        pm_error("parmIversion(): unknown printer version '%d'", arg);
    }
    setPrinterSpecificDefaults();
}



static void
dumpConfig() {

    printf("version:  ");

    switch(printer.version) {
    case HP710:  printf("HP710\n");  break;
    case HP720:  printf("HP720\n");  break;
    case HP820:  printf("HP820\n");  break;
    case HP1000: printf("HP1000\n"); break;
    }
    printf("x-offset: %d\ny-offset: %d\nmargins:\n top:    %d\n"
           " left:   %d\n right:  %d\n bottom: %d\n",printer.x_offset,
           printer.y_offset,printer.top_margin,printer.left_margin,
           printer.right_margin,printer.bottom_margin);
    exit(0);
}



static void
readConfigFile(const char * const fname) {

    FILE * cfgFileP;
    char line[1024];
    char key[14];
    char buf[10];
    int len;
    int value;
    int lineno;

    cfgFileP = fopen(fname, "r");

    lineno = 1;  /* initial value */

    if (!cfgFileP)
        pm_error("read_config_file(): couldn't open file '%s'", fname);

    while (fgets(line, 1024, cfgFileP)) {
        if (strchr(line, '#'))
            *strchr(line, '#')=0;
        switch(sscanf(line, "%13s%9s", key, buf)) {
        case 2:
            value = atoi(buf);
            len   = strlen(key);
            if (strneq(key, "version",           len))
                parmIversion(value);
            else if (strneq(key, "xoffset",      len))
                printer.x_offset = value;
            else if (strneq(key, "yoffset",      len))
                printer.y_offset = value;
            else if(strneq(key,  "topmargin",    len))
                printer.top_margin = value;
            else if(strneq(key,  "leftmargin",   len))
                printer.left_margin = value;
            else if(strneq(key,  "rightmargin",  len))
                printer.right_margin = value;
            else if(strneq(key,  "bottommargin", len))
                printer.bottom_margin = value;
            else if(strneq(key,  "papersize"   , len)) {
                if(streq(buf, "us")) {
                    Width  = USWIDTH;
                    Height = USHEIGHT;
                } else if (streq(buf, "a4")) {
                    Width  = A4WIDTH;
                    Height = A4HEIGHT;
                } else
                    pm_error("read_config_file(): unknown paper size %s", buf);
            } else if(!strcmp(key,"dump"))
                dumpConfig();
            else
                pm_error("read_config_file(): unrecognized parameter '%s' "
                         "(line %d)", key, lineno);
        case EOF:
        case 0:
            break;
        default:
            pm_error("read_config_file(): error parsing config file "
                     "(line %d)", lineno);
        }
        ++lineno;
    }

    if (feof(cfgFileP)) {
        fclose(cfgFileP);
    } else
        pm_error("read_config_file(): error parsing config file");
}



const char * const defaultCfgFileNm = "/etc/pbmtoppa.conf";



int
main(int argc, char *argv[]) {

    int argn;
    int got_in=0, got_out=0, do_continue=1;
    FILE * ifP;
    FILE * ofP;
    struct stat tmpstat;

    pbm_init(&argc, argv);

    printer.version       = DEFAULT_PRINTER;
    printer.x_offset      = DEFAULT_X_OFFSET;
    printer.y_offset      = DEFAULT_Y_OFFSET;
    printer.top_margin    = DEFAULT_TOP_MARGIN;
    printer.left_margin   = DEFAULT_LEFT_MARGIN;
    printer.right_margin  = DEFAULT_RIGHT_MARGIN;
    printer.bottom_margin = DEFAULT_BOTTOM_MARGIN;
    printer.DPI           = DEFAULT_DPI;
    Width  = USWIDTH;
    Height = USHEIGHT;
    setPrinterSpecificDefaults();

    if (!stat(defaultCfgFileNm, &tmpstat))
        readConfigFile(defaultCfgFileNm);

    ifP = stdin;  /* initial value */
    ofP = stdout;  /* initial value */

    for(argn=1; argn<argc; argn++)
    {
        if(!strcmp(argv[argn],"-h"))
        {
            show_usage(*argv);
            return 0;
        }
        else if(!strcmp(argv[argn],"-d"))
            dumpConfig();
        else if(argn+1<argc)
        {
            do_continue=1;
            if(!strcmp(argv[argn],"-v"))
                parmVersion(argv[++argn]);
            else if(!strcmp(argv[argn],"-x"))
                printer.x_offset+=atoi(argv[++argn]);
            else if(!strcmp(argv[argn],"-y"))
                printer.y_offset+=atoi(argv[++argn]);
            else if(!strcmp(argv[argn],"-t"))
                printer.top_margin=atoi(argv[++argn]);
            else if(!strcmp(argv[argn],"-l"))
                printer.left_margin=atoi(argv[++argn]);
            else if(!strcmp(argv[argn],"-r"))
                printer.right_margin=atoi(argv[++argn]);
            else if(!strcmp(argv[argn],"-b"))
                printer.bottom_margin=atoi(argv[++argn]);
            else if(!strcmp(argv[argn],"-d"))
                printer.DPI=atoi(argv[++argn]);
            else if(!strcmp(argv[argn],"-s"))
            {
                argn++;
                if(!strcmp(argv[argn],"us"))
                {
                    Width = USWIDTH;
                    Height = USHEIGHT;
                }
                else if(!strcmp(argv[argn],"a4"))
                {
                    Width = A4WIDTH;
                    Height = A4HEIGHT;
                }
                else
                    pm_error("unknown paper size %s",argv[argn]);
            }
            else if(!strcmp(argv[argn],"-f"))
                readConfigFile(argv[++argn]);
            else do_continue=0;
            if(do_continue) continue;
        }

        if(!got_in)
        {
            if (strcmp (argv[argn], "-") == 0)
                ifP = stdin;
            else if ((ifP = fopen (argv[argn], "rb")) == NULL)
                pm_error("main(): couldn't open file '%s'",
                         argv[argn]);
            got_in=1;
        }
        else if(!got_out)
        {
            if (strcmp (argv[argn], "-") == 0)
                ofP = stdout;
            else if ((ofP = fopen (argv[argn], "wb")) == NULL)
                pm_error("main(): couldn't open file '%s'",
                         argv[argn]);
            got_out=1;
        }
        else
            pm_error("main(): unrecognized parameter '%s'", argv[argn]);
    }

    Pwidth = (Width+7)/8;
    printer.fptr = ofP;

    return printPbm(ifP);
}



