/* ppmtouil.c - read a portable pixmap and produce a Motif UIL icon file
**
** Converted by Jef Poskanzer from ppmtoxpm.c, which is
** Copyright (C) 1990 by Mark W. Snitily.
**
** 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 <ctype.h>
#include <string.h>
#include "ppm.h"
#include "ppmcmap.h"

/* Max number of colors allowed in ppm input. */
#define MAXCOLORS 256

/* Lower bound and upper bound of character-pixels printed in UIL output. */
#define LOW_CHAR '`'
#define HIGH_CHAR '~'

typedef struct {	/* character-pixel mapping */
    char* cixel;	/* character string printed for pixel */
    char* rgbname;	/* ascii rgb color, either mnemonic or #rgb value */
    char* barname;	/* ascii rgb color with spaces replaced by underbars */
    } cixel_map;

static char* gen_numstr ARGS(( int i, int base, char low_char, int digits ));
static void gen_cmap ARGS(( colorhist_vector chv, int ncolors, pixval maxval, cixel_map cmap[MAXCOLORS], int* charsppP ));

int
main( argc, argv )
    int argc;
    char* argv[];
    {
    FILE* ifp;
    pixel** pixels;
    register pixel* pP;
    int argn, rows, cols, ncolors, row, col, i;
    pixval maxval;
    colorhash_table cht;
    colorhist_vector chv;
    cixel_map cmap[MAXCOLORS];
    int charspp;
    char out_name[100];
    char* cp;
    int printed_one;
    char* usage = "[-name <uilname>] [ppmfile]";


    ppm_init( &argc, argv );
    out_name[0] = '\0';

    argn = 1;

    /* Check for command line options. */
    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
	{
	if ( pm_keymatch( argv[argn], "-name", 2 ) )
	    {
	    ++argn;
	    if ( argn == argc )
		pm_usage( usage );
	    (void) strcpy( out_name, argv[argn] );
	    }
	else
	    pm_usage( usage );
	++argn;
	}

    if ( argn != argc )
	{
	/* Open the input file. */
	ifp = pm_openr( argv[argn] );

	/* If output filename not specified, use input filename as default. */
	if ( out_name[0] == '\0' )
	    {
	    (void) strcpy( out_name, argv[argn] );
	    cp = index( out_name, '.' );
	    if ( cp != 0 )
		*cp = '\0';	/* remove extension */
	    if ( strcmp( out_name, "-" ) == 0 )
		(void) strcpy( out_name, "noname" );
	    /* Remove trailing _icon in name. */
	    cp = rindex( out_name, '_' );
	    if ( cp != (char*) 0 &&
		 cp[1] == 'i' && cp[2] == 'c' && cp[3] == 'o' &&
		 cp[4] == 'n' && cp[5] == '\0' )
		*cp = '\0';
	    }
	++argn;
	}
    else
	{
	/* No input file specified. */
	ifp = stdin;
	if ( out_name[0] == '\0' )
	    (void) strcpy( out_name, "noname" );
	}

    if ( argn != argc )
	pm_usage( usage );

    /* Read in the ppm file. */
    pixels = ppm_readppm( ifp, &cols, &rows, &maxval );
    pm_close( ifp );

    /* Figure out the colormap. */
    pm_message( "computing colormap..." );
    chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &ncolors );
    if ( chv == (colorhist_vector) 0 )
	pm_error(
	    "too many colors - try doing a 'ppmquant %d'", MAXCOLORS );
    pm_message( "%d colors found", ncolors );

    /* Make a hash table for fast color lookup. */
    cht = ppm_colorhisttocolorhash( chv, ncolors );

    /* Now generate the character-pixel colormap table. */
    gen_cmap( chv, ncolors, maxval, cmap, &charspp );

    /* Write out the UIL header. */
    printf( "module %s\n", out_name );
    printf( "version = 'V1.0'\n" );
    printf( "names = case_sensitive\n" );
    printf( "include file 'XmAppl.uil';\n" );

    /* Write out the colors. */
    printf( "\n" );
    printf( "value\n" );
    for ( i = 0; i < ncolors; ++i )
	if ( cmap[i].barname != (char*) 0 )
	    printf(
		"    %s : color( '%s' );\n", cmap[i].barname, cmap[i].rgbname );

    /* Write out the ascii colormap. */
    printf( "\n" );
    printf( "value\n" );
    printf( "  %s_rgb : color_table (\n", out_name );
    printed_one = 0;
    for ( i = 0; i < ncolors; ++i )
	if ( cmap[i].barname != (char*) 0 )
	    {
	    if ( printed_one )
		printf( ",\n" );
	    printf( "    %s = '%s'", cmap[i].barname, cmap[i].cixel );
	    printed_one = 1;
	    }     
    printf( "\n    );\n" );

    /* Write out the ascii character-pixel image. */
    printf( "\n" );
    printf(
	"%s_icon : exported icon( color_table = %s_rgb,\n",
	out_name, out_name );
    for ( row = 0; row < rows; ++row )
	{
	printf( "    '" );
	for ( col = 0, pP = pixels[row]; col < cols; ++col, ++pP )
	    {
	    if ( ( col * charspp ) % 70 == 0 && col > 0 )
		printf( "\\\n" );		/* line continuation */
	    printf( "%s", cmap[ppm_lookupcolor(cht, pP)].cixel );
	    }
	if ( row != rows - 1 )
	    printf( "',\n" );
	else
	    printf( "'\n") ; 
	}
    printf( ");\n" );
    printf( "\n" );
    printf( "end module;\n" );

    exit( 0 );
    }

/* Given a number and a base, this routine prints the number into a
** malloc'ed string and returns it.  The length of the string is
** specified by "digits".  The ascii characters of the printed
** number range from low_char to low_char + base.  The string is
** low_char filled.
*/

#if __STDC__
static char*
gen_numstr( int i, int base, char low_char, int digits )
#else /*__STDC__*/
static char*
gen_numstr( i, base, low_char, digits )
    int i, base, digits;
    char low_char;
#endif /*__STDC__*/
    {
    char* str;
    char* p;
    int d;

    /* Allocate memory for printed number.  Abort if error. */
    str = (char*) malloc( digits + 1 );
    if ( str == 0 )
	pm_error( "out of memory allocating number string" );

    /* Generate characters starting with least significant digit. */
    p = str + digits;
    *p-- = '\0';	/* nul terminate string */
    while ( p >= str )
	{
	d = i % base;
	i /= base;
	*p-- = low_char + d;
	}

    return str;
    }

#if __STDC__
static void
gen_cmap( colorhist_vector chv, int ncolors, pixval maxval, cixel_map cmap[MAXCOLORS], int* charsppP )
#else /*__STDC__*/
static void
gen_cmap( chv, ncolors, maxval, cmap, charsppP )
    colorhist_vector chv;
    int ncolors;
    pixval maxval;
    cixel_map cmap[MAXCOLORS];
    int* charsppP;
#endif /*__STDC__*/
    {
    int i, j, base;
    char* colorname;
    char* cp1;
    char* cp2;

    /* Figure out how many characters per pixel we'll be using.  Don't want
    ** to be forced to link with libm.a, so using a division loop rather than
    ** a log function.
    */
    base = (int) HIGH_CHAR - (int) LOW_CHAR + 1;
    for ( *charsppP = 0, i = ncolors; i > 0; ++(*charsppP) )
	i /= base;

    /* Generate the character-pixel string and the rgb name for each colormap
    ** entry.
    */
    for ( i = 0; i < ncolors; ++i )
	{
	/* Generate color name string. */
	colorname = ppm_colorname( &(chv[i].color), maxval, 0 );

	/* Check whether we've already assigned a character to this name,
	** via the closest-match code.
	*/
	for ( j = 0; j < i; ++j )
	    {
	    if ( cmap[j].rgbname != (char*) 0 &&
		 strcmp( colorname, cmap[j].rgbname ) == 0 )
		{
		cmap[i].cixel = cmap[j].cixel;
		cmap[i].rgbname = (char*) 0;
		cmap[i].barname = (char*) 0;
		goto nexti;
		}
	    }

	/* Copy it. */
	cmap[i].rgbname = (char*) malloc( strlen( colorname ) + 1 );
	if ( cmap[i].rgbname == 0 )
	    pm_error( "out of memory allocating color name" );
	(void) strcpy( cmap[i].rgbname, colorname );

	/* Copy it again and replace the blanks with underbars. */
	cmap[i].barname = malloc( strlen( colorname ) + 1 );
	if ( cmap[i].barname == 0 )
	    pm_error( "out of memory allocating color name", 0,0,0,0,0 );
	cp1 = colorname;
	cp2 = cmap[i].barname;
	for ( ; ; )
	    {
	    if ( *cp1 == ' ' )
		*cp2 = '_';
	    else
		*cp2 = *cp1;
	    if ( *cp1 == '\0' )
		break;
	    ++cp1;
	    ++cp2;
	    }

	/* Generate color value characters. */
	cmap[i].cixel = gen_numstr( i, base, LOW_CHAR, *charsppP );

	nexti: ;
	}
    }
