/* pnmcat.c - concatenate portable anymaps
**
** Copyright (C) 1989, 1991 by Jef Poskanzer.
**
** 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 "pnm.h"
#include "mallocvar.h"

int
main( argc, argv )
    int argc;
    char* argv[];
{
    FILE** ifp;
    register xel* newxelrow;
    xel** xelrow;
    xel* background;
    xelval* maxval;
    xelval newmaxval;
    int argn, backdefault, backblack, lrflag, tbflag, justify, nfiles;
    int* rows;
    int* cols;
    int* format;
    int newformat;
    int i, row;
    register int col;
    int newrows, newcols, new;
    const char* const usage = "[-white|-black] -leftright|-lr [-jtop|-jbottom] pnmfile ...\n               [-white|-black] -topbottom|-tb [-jleft|-jright] pnmfile ...";

    pnm_init( &argc, argv );

    argn = 1;
    backdefault = 1;
    lrflag = tbflag = 0;
    justify = 0;

    /* Check for flags. */
    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
	{
        if ( pm_keymatch( argv[argn], "-white", 2 ) )
	    {
            backdefault = 0;
            backblack = 0;
	    }
        else if ( pm_keymatch( argv[argn], "-black", 2 ) )
	    {
            backdefault = 0;
            backblack = 1;
	    }
        else if ( pm_keymatch( argv[argn], "-lr", 2 ) ||
                  pm_keymatch( argv[argn], "-leftright", 2 ) )
            lrflag = 1;
        else if ( pm_keymatch( argv[argn], "-tb", 2 ) ||
                  pm_keymatch( argv[argn], "-topbottom", 2 ) )
            tbflag = 1;
        else if ( pm_keymatch( argv[argn], "-jtop", 3 ) )
            justify = -1;
        else if ( pm_keymatch( argv[argn], "-jbottom", 3 ) )
            justify = 1;
        else if ( pm_keymatch( argv[argn], "-jleft", 3 ) )
            justify = -1;
        else if ( pm_keymatch( argv[argn], "-jright", 3 ) )
            justify = 1;
        else if ( pm_keymatch( argv[argn], "-jcenter", 3 ) )
            justify = 0;
        else
            pm_usage( usage );
        ++argn;
	}

    if ( lrflag && tbflag )
        pm_error( "only one of -lr and -tb may be specified" );
    if ( ! ( lrflag || tbflag ) )
        pm_error( "one of -lr or -tb must be specified" );

    if ( argn < argc )
        nfiles = argc - argn;
    else
        nfiles = 1;
    
    MALLOCARRAY(ifp, nfiles);
    MALLOCARRAY(xelrow, nfiles);
    MALLOCARRAY(background, nfiles);
    MALLOCARRAY(maxval, nfiles);
    MALLOCARRAY(rows, nfiles);
    MALLOCARRAY(cols, nfiles);
    MALLOCARRAY(format, nfiles);

    if ( ifp == NULL || xelrow == NULL || background == NULL ||
         maxval == NULL || rows == NULL || cols == NULL ||
         format == NULL )
        pm_error( "out of memory" );

    if ( argn < argc )
	{
        for ( i = 0; i < nfiles; ++i )
            ifp[i] = pm_openr( argv[argn+i] );
	}
    else
        ifp[0] = stdin;

    newcols = 0;
    newrows = 0;
    for ( i = 0; i < nfiles; ++i )
	{
        pnm_readpnminit( ifp[i], &cols[i], &rows[i], &maxval[i], &format[i] );
        if ( i == 0 )
	    {
            newmaxval = maxval[i];
            newformat = format[i];
	    }
        else
	    {
            if ( PNM_FORMAT_TYPE(format[i]) > PNM_FORMAT_TYPE(newformat) )
                newformat = format[i];
            if ( maxval[i] > newmaxval )
                newmaxval = maxval[i];
	    }
        xelrow[i] = pnm_allocrow( cols[i] );
        if ( lrflag )
	    {
            newcols += cols[i];
            if ( rows[i] > newrows )
                newrows = rows[i];
	    }
        else
	    {
            newrows += rows[i];
            if ( cols[i] > newcols )
                newcols = cols[i];
	    }
	}
    for ( i = 0; i < nfiles; ++i )
	{
        /* Read first row just to get a good guess at the background. */
        pnm_readpnmrow( ifp[i], xelrow[i], cols[i], maxval[i], format[i] );
        pnm_promoteformatrow(
            xelrow[i], cols[i], maxval[i], format[i], newmaxval, newformat );
        if (  backdefault )
            background[i] =
                pnm_backgroundxelrow(
                    xelrow[i], cols[i], newmaxval, newformat );
        else
            if ( backblack )
                background[i] = pnm_blackxel( newmaxval, newformat );
            else
                background[i] = pnm_whitexel( newmaxval, newformat );
	}

    newxelrow = pnm_allocrow( newcols );

    pnm_writepnminit( stdout, newcols, newrows, newmaxval, newformat, 0 );

    if ( lrflag )
	{
        for ( row = 0; row < newrows; ++row )
	    {
            new = 0;
            for ( i = 0; i < nfiles; ++i )
            {
                int padtop;

                if ( justify == -1 )
                    padtop = 0;
                else if ( justify == 1 )
                    padtop = newrows - rows[i];
                else
                    padtop = ( newrows - rows[i] ) / 2;
                if ( row < padtop || row >= padtop + rows[i] )
                {
                    for ( col = 0; col < cols[i]; ++col )
                        newxelrow[new+col] = background[i];
                }
                else
                {
                    if ( row != padtop )	/* first row already read */
                    {
                        pnm_readpnmrow(
                            ifp[i], xelrow[i], cols[i], maxval[i], format[i] );
                        pnm_promoteformatrow(
                            xelrow[i], cols[i], maxval[i], format[i],
                            newmaxval, newformat );
                    }
                    for ( col = 0; col < cols[i]; ++col )
                        newxelrow[new+col] = xelrow[i][col];
                }
                new += cols[i];
            }
            pnm_writepnmrow( stdout, 
                             newxelrow, newcols, newmaxval, newformat, 0 );
	    }
	}
    else
	{
        int padleft;

        new = 0;
        i = 0;
        if ( justify == -1 )
            padleft = 0;
        else if ( justify == 1 )
            padleft = newcols - cols[i];
        else
            padleft = ( newcols - cols[i] ) / 2;

        for ( row = 0; row < newrows; ++row )
	    {
            if ( row - new >= rows[i] )
            {
                new += rows[i];
                ++i;
                if ( i >= nfiles )
                    pm_error( "shouldn't happen" );
                if ( justify == -1 )
                    padleft = 0;
                else if ( justify == 1 )
                    padleft = newcols - cols[i];
                else
                    padleft = ( newcols - cols[i] ) / 2;
            }
            if ( row - new > 0 )
            {
                pnm_readpnmrow(
                    ifp[i], xelrow[i], cols[i], maxval[i], format[i] );
                pnm_promoteformatrow(
                    xelrow[i], cols[i], maxval[i], format[i],
                    newmaxval, newformat );
            }
            for ( col = 0; col < padleft; ++col )
                newxelrow[col] = background[i];
            for ( col = 0; col < cols[i]; ++col )
                newxelrow[padleft+col] = xelrow[i][col];
            for ( col = padleft + cols[i]; col < newcols; ++col )
                newxelrow[col] = background[i];
            pnm_writepnmrow( stdout, 
                             newxelrow, newcols, newmaxval, newformat, 0 );
	    }
	}

    for ( i = 0; i < nfiles; ++i )
        pm_close( ifp[i] );
    pm_close( stdout );

    exit( 0 );
}
