// 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: UcharDiscreteImage.c,v 1.8 1994/01/02 19:34:42 jnw Exp $";

//
// $Log: UcharDiscreteImage.c,v $
// Revision 1.8  1994/01/02  19:34:42  jnw
// Modified pgm reader to handle comments
//
// 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_IntPoint instead of IntPoint
// 
// Revision 1.1  93/03/18  11:17:02  thoth
// Initial revision
// 

#include	"UcharDiscreteImage.h"
#include	"VectorDI.h"
#include	"ConstDI.h"
#include	"OtherDI.h"
#include	"ErrorDI.h"
#include	"DIVIter.h"
#include	"DIPIter.h"
#include	"IPSIter.h"

#include	<math.h>
#include	<fstream.h>
#include	<vfork.h>

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

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_UcharDiscreteImage IA_UcharDiscreteImage::read_PGM(istream &i)
{
    int	width, height,maxval;
    if (i.get()=='P' && i.get()=='5' && i>>wsc>>width>>wsc>>height>>wsc>>maxval) {
	const unsigned	size=width*height;
	unsigned char	*data = new unsigned char[size];
	if (i.peek() == '\n') i.get();
	if (!i.read(data, size))
	    return IA_UcharDiscreteImage();
	return     IA_UcharDiscreteImage
	  (IA_IntPointSet(IA_IntPoint(0,0), IA_IntPoint(height-1,width-1)),
	   data, size, 1);
    } else {
	return IA_UcharDiscreteImage();
    }
}

IA_UcharDiscreteImage IA_UcharDiscreteImage::read_PGM(const char *fname)
{
    ifstream	in(fname);
    return read_PGM(in);
}

ostream &IA_UcharDiscreteImage::write_PGM(ostream &o, unsigned maxval) const
{
    IA_IntPointSet boxydomain(domain().inf(),domain().sup());
    IA_IntPoint	width(domain().sup()-domain().inf()+1);
    o<<"P5\n"<<width[1] << " " << width[0] << " " << maxval << "\n";
    IA_IPSIter	iter(boxydomain);
    IA_IntPoint	ip;

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

ostream &IA_UcharDiscreteImage::write_PGM(ostream &o) const
{
    return write_PGM(o,max(*this));
}

void IA_UcharDiscreteImage::write_PGM(const char *fname, unsigned maxval) const
{
    ofstream	out(fname);
    write_PGM(out, maxval);
}

void IA_UcharDiscreteImage::write_PGM(const char *fname) const
{
    ofstream	out(fname);
    write_PGM(out, max(*this));
}

void IA_UcharDiscreteImage::IDM_display(IDP::LinkID lid, const char *label)
{
    if (label==0)
	label="unlabeled";

    IA_IntPointSet	boxy(domain().inf(),domain().sup());
    IA_IntPoint	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(this->extend(IA_UcharDiscreteImage(boxy,(u_char)0)) .value_array());

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

#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";

// 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_UcharDiscreteImage 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
	img.write_PGM(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);
	system(buffer);
	abort();
    }
    return pid;
}

//
// below are included the implementations of every image-image
// and image-scalar operation we could think of for u_char images.
//

inline int max(int a, int b)
{
    return (a<b)?b:a;
}

inline int min(int a, int b)
{
    return (a<b)?a:b;
}

#include "UcharImageOps.c"

//
//
//

class blah : public IA_OtherDI<u_char> {
  public:
    blah() 
    :IA_OtherDI<u_char>(IA_IntPointSet()) {}
    ostream &print_this(ostream &o) const { return o;}
    u_char operator()(const IA_IntPoint &) const { return 0; }
    IA_OtherDI<u_char>* clone_self() const { return 0; }
};
static void dummy001()
{
    IA_IntPoint		ip;
    IA_IntPointSet	ps;
    IA_UcharDiscreteImage	i1;
    IA_UcharDiscreteImage
	i2(i1),
	i3(ps,(u_char)0),
	i4(ps, (u_char*)0, 0, 1),
	i5(ps, (u_char*)0, 0),
	i6(blah()),
	i7(ps, (u_char(*)(const IA_IntPoint&))0),
	i10(IA_DiscreteImage<u_char>());
    i1 = i1.restrict(i2.range());
    (IA_DiscreteImage<u_char>&)i1 = 2;
    (void)i1.type();
    i1[ip] = i1(ip);
    i1 = i1.restrict(i2.domain());
    (void)i1.extend(i2);
    (void)i1.reduce((u_char (*)(const u_char&,const u_char&)) 0, 0);
    (void)i1.reduce((u_char (*)( u_char, u_char)) 0, 0);
    cout << (IA_DiscreteImage<u_char>&)i1;

    IA_UcharDiscreteImage::read_IAA("");
    IA_UcharDiscreteImage::read_IAA("","");

    i1.write_IAA("");
    i1.write_IAA("","");

    IA_DIVIter<u_char>	iter1(i1), iter2;
    IA_DIPIter<u_char>	iter3(i2), iter4;
    iter1 = iter2;
    iter3 = iter4;

    IA_ValueSet<u_char>	vs(i1.range());
    // I shouldn\'t have to do the following two lines
    // vs.value_type_compare(0,0);
}
