// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//

static char copyright[] = "Copyright 1993, Center for Computer Vision and Visualization,\nUniversity of Florida.  All rights reserved.\n";


static char UcharDI_rcsid[] = "$Id: UcharDI.c,v 1.13.1.3 1995/01/13 19:37:42 thoth Exp thoth $";

//
// $Log: UcharDI.c,v $
// Revision 1.13.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.13.1.2  1995/01/09  18:15:45  thoth
// Make functions match prototype.
//
// Revision 1.13.1.1  1994/12/28  16:46:34  thoth
// Image I/O is now ROF.
// removed obsolete instantiation cruft.
//
// Revision 1.13  1994/09/16  14:57:11  thoth
// more DOS-inspired renaming.
//
// Revision 1.12  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.11  1994/05/17  12:53:27  thoth
// Image Operations now get their own files.
//
// Revision 1.10  1994/03/14  15:48:33  thoth
// ValueSet has been replaced by container class Set.
//
// Revision 1.9  1994/01/31  15:49:53  thoth
// read_PGM now can return the maxval.
//
// Revision 1.8  1994/01/07  15:10:00  thoth
// Image class is now CoreImage and named image types are
// Image<P,T>.
//
// Revision 1.7  1993/12/29  17:06:11  thoth
// Workaround for woejus CFront bug.
// write_PGM functions will no longer calculate a maxval of 0.
//
// Revision 1.6  1993/11/17  18:27:13  thoth
// IPSIter is now PSIter<IntPoint>.
// restriction functions now go in the individual image implementation modules.
// PNM functions now throw errors.
// extensivep is now extensive.
//
// Revision 1.5  1993/10/20  18:34:27  thoth
// specializations for ostream methods that output the character as
// a number instead of a character.
//
// Revision 1.4  1993/10/05  19:33:08  thoth
// Move to new Closure naming convention.
//
// Revision 1.3  1993/09/27  15:56:38  thoth
// *** empty log message ***
//
// Revision 1.2  1993/09/24  11:36:35  jnw
// Fixed read_PGM to handle comments.
// Added display function
//
// Revision 1.1  93/09/15  12:57:04  thoth
// Initial revision
// 
// Revision 1.7  93/05/31  22:23:22  jnw
// Added instantiator for write_IAA
// 
// Revision 1.6  93/05/31  15:56:34  thoth
// write_PGM now supports maxval.
// 
// Revision 1.5  93/05/27  11:36:31  thoth
// Copyright Notices
// 
// Revision 1.4  93/05/26  17:03:15  thoth
// instantiate IAAIO
// 
// Revision 1.3  93/05/18  21:40:59  thoth
// IDM interface for Uchar DIs.
// support for min and max reduce operations.
// 
// Revision 1.2  93/04/17  18:48:15  thoth
// use IA_Point<int> instead of IntPoint
// 
// Revision 1.1  93/03/18  11:17:02  thoth
// Initial revision
// 

#include	"VectorI.h"
#include	"IntPoint.h"
#include	"PSIter.h"

// if we put this lonely bugger with its friends, the compiler issues
// syntax errors.  USL really needs to hire me.

ostream& IA_VectorI<IA_Point<int>,u_char>::print_this(ostream &o) const
{
    IA_PSIter<IA_Point<int> > iter(this->ps);
    IA_Point<int> p;
    int i;

    o << "VectorI - (point,value) pairs follow:\n";
    i = 0;
    while(iter(p)) {
	o << "(" << p << "," << (int)this->vec[i++] << ") ";
    }
    return o << "\n";
}

#include	"UcharDI.h"
#include	"ConstI.h"
#include	"ClosureI.h"
#include	"FuncI.h"
#include	"ErrorI.h"
#include	"ImageIter.h"

#include	<math.h>
#include	<fstream.h>
#include	<iostream.h>
#include	<unistd.h>
#include	<string.h>

#include	"Bit.h"

// definitions and data for display function

#define IMAGE_DISPLAY_ENV_VAR "IMAGE_DISPLAY"
#define DEFAULT_IMAGE_DISPLAY "xv"

#define SEPARATOR_STRING " "
#define CLEANUP_STRING "; rm -f "

static char display_filename[]= "/tmp/IA_display_XXXXXX";

//
//
//

#include "restrict.h"

IA_BaseImage<IA_Point<int>,u_char> *restrict_toD(const IA_BaseImage<IA_Point<int>, u_char> *im, const IA_Set<IA_Point<int> > &ips) {
    return restrictD_toD(im, ips);
}


// ws
// whitespace predicate
int ws(char c)
{
    switch (c) {
    case ' ':
    case '\t':
    case '\n': return 1;
    default: return 0;
    }
}

// wsc
// stream convenience function to skip white space and comment lines
// beginning with #
istream& wsc(istream &i)
{
    while (ws(i.peek())){
	if (i.peek() == '\n') {
	    i.get();
	    if (i.peek() == '#') {
		while(i.peek() != '\n') i.get();
	    } else {
		break;
	    }
	} else {
	    i.get();
	}
    }
    return i;
}

IA_Image<IA_Point<int>,u_char>
read_uchar_PGM(istream &i, int *maxval_ret)
{
    int	width, height,maxval;
    if (i.get()=='P' && i.get()=='5'
	&& i>>wsc>>width>>wsc>>height>>wsc>>maxval) {
	if (maxval_ret)
	    *maxval_ret = maxval;
	const unsigned	size=width*height;
	unsigned char	*data = new unsigned char[size];
	{
	    int	ch = i.get();
	    if (ch!='\n')
		i.putback(ch);
	}
	if (!i.read(data, size)) {
	    ia_throw(Image_PNMIO_Exception(__FILE__, __LINE__));
	    return IA_Image<IA_Point<int>,u_char>();
	}
	return     IA_Image<IA_Point<int>,u_char>
	  (IA_boxy_pset(IA_Point<int>(0,0), IA_Point<int>(height-1,width-1)),
	   data, size, 1);
    } else {
	ia_throw(Image_PNMIO_Exception(__FILE__, __LINE__));
	return IA_Image<IA_Point<int>,u_char>();
    }
}

IA_Image<IA_Point<int>,u_char>
read_uchar_PGM(const char *fname, int *maxval)
{
    ifstream	in(fname);
    return read_uchar_PGM(in, maxval);
}

ostream &write_PGM(const IA_Image<IA_Point<int>,u_char> &img,
		   ostream &o, unsigned maxval)
{
    IA_Set<IA_Point<int> > boxydomain
	= IA_boxy_pset(img.domain().inf(),img.domain().sup());
    IA_Point<int>	width(img.domain().sup()-img.domain().inf()+1);
    o<<"P5\n"<<width[1] << " " << width[0] << " " << maxval << "\n";
    IA_PSIter<IA_Point<int> >	iter(boxydomain);
    IA_Point<int>	ip;

    while (iter(ip)) {
	if (img.domain().contains(ip))
	    o.put((char) img(ip));
	else
	    o.put((char)0);
    }
    return o;
}

ostream &write_PGM(const IA_Image<IA_Point<int>,u_char> &img, ostream &o)
{
    const unsigned	m = max(img);
    return write_PGM(img, o,m>0?m:1);
}

void write_PGM(const IA_Image<IA_Point<int>,u_char> &img,
	       const char *fname, unsigned maxval)
{
    ofstream	out(fname);
    write_PGM(img, out, maxval);
}

void write_PGM(const IA_Image<IA_Point<int>,u_char> &img,
	       const char *fname)
{
    ofstream	out(fname);
    const unsigned	m = max(img);
    write_PGM(img, out, m>0?m:1);
}

#if 0
void IA_Image<IA_Point<int>,u_char>::IDM_display(IDP::LinkID lid, const char *label)
{
    if (label==0)
	label="unlabeled";

    IA_Set<IA_Point<int> >	boxy(domain().inf(),domain().sup());
    IA_Point<int>	width = domain().sup()-domain().inf() +1;
    IA_Array<unsigned> dims(width.dim());
    for (unsigned i=0; i<width.dim(); i++)
	dims[i] = width[i];

    IA_Array<u_char>	vals(::extend(*this, IA_Image<IA_Point<int>,u_char>(boxy,(u_char)0)) .value_array());

    IDP::WriteImage(lid, label, IDP::U_BYTE, 1, dims, vals.data_start());
}
#endif


// display
//
// Forks a process to display a UcharDiscreteImage.
//
// Returns: the uid of the process      if the fork succeeded
//          -1                          if the fork failed
//
// display writes the image to a temp file in pgm format.
//
// User can set environment variable whose name is given by define constant
//        IMAGE_DISPLAY_ENV_VAR to specify what image display application
//        is to be used to display the pgm image.
//
// Default display application name is given by DEFINE constant
//        DEFAULT_IMAGE_DISPLAY
//

pid_t display(IA_Image<IA_Point<int>,u_char> img)
{
    char *display_program;
    pid_t pid;

    // always use a fresh tmp filename
    strcpy(display_filename + strlen(display_filename) - 6, "XXXXXX");

    // Get the image display program filename
    display_program = getenv(IMAGE_DISPLAY_ENV_VAR);
    if (!display_program) {
	display_program = DEFAULT_IMAGE_DISPLAY;
    }

    // Fork and exec the display program
    if ((pid=fork())==0){
	// pid is zero, so this is the child

	// Write the image to a tmp file with a unique pid
	write_PGM(img, mktemp(display_filename));

	char *buffer;
	buffer = new char [strlen(display_program) + strlen(SEPARATOR_STRING)
			   + strlen(display_filename) + strlen(CLEANUP_STRING)
			   + strlen(display_filename)];
	strcat(strcat(strcat(strcat(strcpy(buffer, display_program),
				    SEPARATOR_STRING),
			     display_filename),
		      CLEANUP_STRING),
	       display_filename);
	// should exec a csh -c
	system(buffer);
	exit(0);
    }
    return pid;
}

//
//
//

ostream& IA_VectorI<IA_Point<double>,u_char>::print_this(ostream &o) const
{
    IA_PSIter<IA_Point<double> > iter(this->ps);
    IA_Point<double> p;
    int i;

    o << "VectorI - (point,value) pairs follow:\n";
    i = 0;
    while(iter(p)) {
	o << "(" << p << "," << (int)this->vec[i++] << ") ";
    }
    return o << "\n";
}

//

ostream& IA_ConstI<IA_Point<int>,u_char>::print_this(ostream &o) const
{
    o << "Constant Image - Value : " << (int)this->value ;
    if (this->ps.extensive()){
	IA_PSIter<IA_Point<int> > iter(this->ps);
	IA_Point<int> p;
	o << ", Points follow\n";
	while(iter(p)) {
	    o << p << ", ";
	}
    } else {
	o << ", Comprehensive PointSet";
    }
    return o << "\n";
}

ostream& IA_ConstI<IA_Point<double>,u_char>::print_this(ostream &o) const
{
    o << "Constant Image - Value : " << (int)this->value ;
    if (this->ps.extensive()){
	IA_PSIter<IA_Point<double> > iter(this->ps);
	IA_Point<double> p;
	o << ", Points follow\n";
	while(iter(p)) {
	    o << p << ", ";
	}
    } else {
	o << ", Comprehensive PointSet";
    }
    return o << "\n";
}

//

ostream& IA_FunctionRefI<IA_Point<int>,u_char>::print_this(ostream& o) const
{
    o << "FunctionRefI -";
    if (this->ps.extensive()){
	IA_PSIter<IA_Point<int> > iter(this->ps);
	IA_Point<int> p;
	
	o << "(point,value) pairs follow:\n";
	while(iter(p)) {
	    o << "(" << p << "," << (int)(*this->func)(p) << ") ";
	}
    } else {
	o << " Function : " << (int)(this->func) <<
	    ", PointSet : Comprehensive";
    }
    return o << "\n";
}

ostream& IA_FunctionRefI<IA_Point<double>,u_char>::print_this(ostream& o) const
{
    o << "FunctionRefI -";
    if (this->ps.extensive()){
	IA_PSIter<IA_Point<double> > iter(this->ps);
	IA_Point<double> p;
	
	o << "(point,value) pairs follow:\n";
	while(iter(p)) {
	    o << "(" << p << "," << (int)(*this->func)(p) << ") ";
	}
    } else {
	o << " Function : " << (int)(this->func) <<
	    ", PointSet : Comprehensive";
    }
    return o << "\n";
}


//

static IA_Point<int>	phooey;
