/*
 * Tcl command to read in a .ppm binary format file
 * and put it in a photo widget.
 *
 * readppm ?-zoom x y? ?-decimate x y ? photo-path filename ?x y?
 *
 * Copyright 1993 The Australian National University.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation is hereby granted, provided that the above copyright
 * notice appears in all copies.  This software is provided without any
 * warranty, express or implied. The Australian National University
 * makes no representations about the suitability of this software for
 * any purpose.
 *
 * Author: Paul Mackerras (paulus@cs.anu.edu.au)
 *
 * $Header: /home/paulus/CaVis/othersrc/tk3.3/RCS/readppm.c,v 1.5 1993/10/20 05:41:08 paulus Exp $
 */
#include <tcl.h>
#include "tkPhoto.h"
#include <stdio.h>
#include <stdlib.h>

static int read_hdr();

#define MAX_MEMORY	1048576		/* don't allocate > 1MB */

int
ReadPPMCmd(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    PhotoHandle pp;
    int nb, x, y, width, height, lpb, h;
    int i, l, zw, zh, hpb;
    FILE *fp;
    PhotoImage blk;
    int dec_x, dec_y, zoom_x, zoom_y;

    dec_x = dec_y = zoom_x = zoom_y = 1;
    i = 1;
    while( argc >= i + 3 && argv[i][0] == '-' ){
	l = strlen(argv[i]);
	if( l >= 2 && strncmp(argv[i], "-zoom", l) == 0 ){
	    /*
	     * Parse the -zoom option.
	     */
	    if( Tcl_GetInt(interp, argv[i+1], &zoom_x) != TCL_OK
	       || Tcl_GetInt(interp, argv[i+2], &zoom_y) != TCL_OK )
		return TCL_ERROR;
	    if( zoom_x <= 0 || zoom_y <= 0 ){
		Tcl_AppendResult(interp, "zoom factors must be positive",
				 (char *) NULL);
		return TCL_ERROR;
	    }
	    i += 3;
	} else if( l >= 2 && strncmp(argv[i], "-decimate", l) == 0 ){
	    /*
	     * Parse the -decimate option.
	     */
	    if( Tcl_GetInt(interp, argv[i+1], &dec_x) != TCL_OK
	       || Tcl_GetInt(interp, argv[i+2], &dec_y) != TCL_OK )
		return TCL_ERROR;
	    if( dec_x <= 0 || dec_y <= 0 ){
		Tcl_AppendResult(interp, "decimate factors must be positive",
				 (char *) NULL);
		return TCL_ERROR;
	    }
	    i += 3;
	} else
	    break;
    }

    if( (argc != i + 2 && argc != i + 4) || argv[i][0] == '-' ){
	Tcl_AppendResult(interp, "usage: ", argv[0], " ?-zoom x y?",
			 " ?-decimate x y? photo ppm-file ?x y?",
			 (char *) NULL);
	return TCL_ERROR;
    }

    pp = FindPhoto(argv[i]);
    if( pp == NULL ){
	Tcl_AppendResult(interp, argv[0], ": photo window ", argv[i],
			 " not found", (char *) NULL);
	return TCL_ERROR;
    }

    if( argc < i + 4 )
	x = y = 0;
    else {
	if( Tcl_GetInt(interp, argv[i+2], &x) != TCL_OK
	   || Tcl_GetInt(interp, argv[i+2], &y) != TCL_OK )
	    return TCL_ERROR;
    }

    fp = fopen(argv[i+1], "rb");
    if( fp == NULL ){
	Tcl_AppendResult(interp, Tcl_PosixError(interp), (char *) NULL);
	return TCL_ERROR;
    }

    if( !read_hdr(fp, &width, &height) ){
	Tcl_AppendResult(interp, argv[i+1], ": not a suitable raw PPM file",
			 (char *) NULL);
	fclose(fp);
	return TCL_ERROR;
    }

    if( width <= 0 || height <= 0 )
	return TCL_OK;

    zw = (width + dec_x - 1) / dec_x * zoom_x;
    zh = (height + dec_y - 1) / dec_y * zoom_y;
    PhotoExpand(pp, x + zw, y + zh);

    blk.width = width;
    blk.pixel_size = 3;
    blk.pitch = 3 * width;
    blk.comp_off[0] = 0;
    blk.comp_off[1] = 1;
    blk.comp_off[2] = 2;

    lpb = (MAX_MEMORY / (width * dec_y)) * dec_y;
    if( lpb <= 0 )
	lpb = dec_y;
    else if( lpb > height )
	lpb = height;
    hpb = (lpb + dec_y - 1) / dec_y * zoom_y;
    nb = width * lpb * 3;
    if( (blk.ptr = (unsigned char *) malloc(nb)) == NULL ){
	Tcl_AppendResult(interp, argv[2], ": could not allocate memory",
			 (char *) NULL);
	fclose(fp);
	return TCL_ERROR;
    }

    for( h = 0; h < height; h += lpb ){
	if( lpb + h > height ){
	    lpb = height - h;
	    hpb = (lpb + dec_y - 1) / dec_y * zoom_y;
	    nb = width * lpb * 3;
	}
	if( fread(blk.ptr, 1, nb, fp) != nb ){
	    Tcl_AppendResult(interp, argv[2], ": too short or read error",
			     (char *) NULL);
	    fclose(fp);
	    free((char *) blk.ptr);
	    return TCL_ERROR;
	}
	blk.height = lpb;
	if( zoom_x == 1 && zoom_y == 1 && dec_x == 1 && dec_y == 1 ){
	    PhotoPutBlock(pp, &blk, x, y, width, lpb);
	} else {
	    PhotoPutZoomedBlock(pp, &blk, x, y, zw, hpb,
				zoom_x, zoom_y, dec_x, dec_y);
	}
	y += hpb;
    }

    fclose(fp);
    free((char *) blk.ptr);

    return TCL_OK;
}

static char *
ppm_fgets(line, len, fp)
    char *line;
    int len;
    FILE *fp;
{
    int incomment = 0;

    for(;;){
	if( fgets(line, len, fp) == NULL )
	    return NULL;
	if( !incomment && line[0] != '#' )
	    break;
	incomment = line[strlen(line) - 1] != '\n';
    }
    return line;
}

static int
read_hdr(fp, wp, hp)
FILE *fp;
int wp, hp;
{
    int pmax;
    char line[64];

    if( ppm_fgets(line, sizeof(line), fp) == NULL
       || strcmp(line, "P6\n") != 0 )
	return 0;
    if( ppm_fgets(line, sizeof(line), fp) == NULL
       || sscanf(line, "%d%d", wp, hp) != 2 )
	return 0;
    if( ppm_fgets(line, sizeof(line), fp) == NULL
       || sscanf(line, "%d", &pmax) != 1
       || pmax != 255 )
	return 0;
    return 1;
}
