/*
 * $Id: machine.cxx,v 4.4 2004/06/20 12:39:17 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 * */

/*------------------------------------------------------------------------
 *
 * This hosts the bits of imview that are not portable.
 *
 * Except for drawing calls, such as in imDrawPoint.C,
 * NO other file should have any #ifdev STRANGEMACHINE, etc
 * That's the theory anyway.
 *
 * Hugues Talbot	19 Jan 2000
 * 
 *      
 *-----------------------------------------------------------------------*/

#include <imcfg.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#ifdef HAVE_SYS_TIME_H
#   include <sys/time.h>
#endif
#include <sys/types.h>

#ifndef WIN32_NOTCYGWIN
# include <sys/utsname.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
#endif

#include "imunistd.h"

#include <FL/Fl.H>
#include <FL/x.H>
#include "imview.hxx"


// globals
#if HAVE_PTHREADS

#  include <pthread.h> // good for both DEC and P threads

#  if !HAVE_DECTHREADS
    pthread_mutexattr_t *pthread_mutexattr_default = 0;
    pthread_condattr_t *pthread_condattr_default = 0;
#  endif // !HAVE_DECTHREAD

#endif // HAVE_PTHREADS

char const *systemName(void)
{
#ifdef WIN32_NOTCYGWIN
        static char const *retval = "win32";        
#else
    static char const *retval;
    
    static struct utsname thisname;
    uname(&thisname);
    retval = thisname.sysname;
    
#endif

    return retval;
}

// a temporary file name utility
// Someone is welcome to come up with a secure version of this,
// It's probably impossible, in the meantime gcc complains...

#define TemporaryTemplate "ImView"

// return a "unique" file name in filename, that can be opened, etc
// This is not secure, but this is portable.
// filename must be writeable, therefore must not be a string but an
// array of chars.

void imtempnam(char *filename)
{
    char *name;
    assert(filename != NULL);

    name = (char *) tempnam(NULL, TemporaryTemplate);
    if (name == NULL)
	tmpnam(filename);
    else {
	strcpy(filename, name);
	free(name);
    }

    return;
}



// This emulates the `basename' not-very-standard
// system call. The basename command line is
// much more complicated, but we can't emulate it
// here short of allocating a new buffer, which we
// don't want to do.
//
// The win32 call will cope (sort of) with both / and '\\'
// but it doesn't do that very well. It will do.
const char *myBaseName(const char *astring)
{
#ifdef WIN32
    // look for forward slash
    const char *retval = strrchr(astring, '/');

    if (retval == 0) {
	// look for backward slash
	retval = strrchr(astring, '\\');
	if (retval == 0)
	    retval = astring;
	else
	    retval++;
    } else
	retval++;
    
#else    
    const char *retval = strrchr(astring, '/');

    if (retval == 0)
	retval = astring;
    else 
	retval++; // go past the `/'
#endif

    return retval;
}

// This is the Nightmare Call.
int myFork(void)
{
    return 0;
}


extern int debugIsOn;
// Synchronize call for debugging
void synchronize_GUI(void)
{
#if !defined(WIN32) && !defined(MACOSX_CARBON)
    if (debugIsOn) {
	// this is only for SERIOUS debugging
	XSynchronize(fl_display, 1);
	dbgprintf("Display synchronized for debugging\n");
    }
#endif  
    
    return;
}


// skip over a \0 in a file. Necessary for
// reading Z-IMAGE files.
// NT doesn't require this for some reason
int skipzero(FILE *fp)
{
    int retval;
    
#ifdef WIN32
    retval = '\0'; // do nothing
#else
    retval = getc(fp);
#endif

    return retval;
}

#if HAVE_USLEEP

#ifdef OSF1
// DEC Unix's prototype is unreachable
extern "C" {
    extern int usleep (unsigned int);
}
#endif

int myusleep(unsigned int usec)
{
    struct timezone dummy;
    struct timeval  start, end;
    double          time0, time1, dt;

    gettimeofday(&start, &dummy);
    usleep(usec);
    gettimeofday(&end, &dummy);

    time0 = start.tv_sec + start.tv_usec/1e6;
    time1 = end.tv_sec + end.tv_usec/1e6;
    dt = time1-time0;

    dbgprintf("Asked to wait for %f sec, actually waited %f sec\n",
	      usec/1e6, dt);

    return 0;
}

#elif HAVE_NANOSLEEP

int myusleep(unsigned int usec)
{
    
    struct timespec req;
    struct timezone dummy;
    struct timeval  start, end;
    double          time0, time1, dt;
    
    req.tv_sec = (int)(usec/1e6);
    req.tv_nsec = (long)((usec - req.tv_sec * 1e6) * 1000);

    gettimeofday(&start, &dummy);
    nanosleep(&req, NULL);
    gettimeofday(&end, &dummy);
    
    time0 = start.tv_sec + start.tv_usec/1e6;
    time1 = end.tv_sec + end.tv_usec/1e6;
    dt = time1-time0;

    dbgprintf("Asked to wait for %f sec, actually waited %f sec\n",
	      usec/1e6, dt);

    return 0;
}

#elif !defined(WIN32) && HAVE_SELECT

// Calls select on sterr, waiting to read
// obviously this is going to fail, this is what we want:
// select will time out and give us a portable time sleep.
// the usual usleep is not widely available.
// NOTE: This rely on a side effect of select. Time will
// in fact tell if this is portable...
int myusleep(unsigned int usec)
{
    fd_set         rfds;
    struct timeval tv, start, end;
    struct timezone dummy;
    int            retval;
    double         time0, time1, dt;
    static char    tmppath[1024];
    static int     somedesc = -1;

    if (somedesc < 0) {
	char *tmp;
	// open the pipe
	tmp = tempnam(0, "flim");
	strncpy(tmppath, tmp, 1024);
	retval = mkfifo(tmppath, S_IRUSR|S_IWUSR);
	if (retval != 0) {
	    dbgprintf("Can't create fifo %s\n", tmppath);
	    return 1; // can't go further
	}
	somedesc = open(tmppath, O_RDONLY | O_NDELAY, 0);
	if (somedesc < 0) {
	    dbgprintf("Can't open fifo %s for reading\n");
	    return 2; // Can't go further either
	}
    }

    if (usec == 0) {
	usec = 10; // 0 actually means forever
    }
    
    FD_ZERO(&rfds);
    FD_SET(somedesc, &rfds);

    /* Wait up to the given time period */
    tv.tv_sec = 0;
    tv.tv_usec = usec;

    gettimeofday(&start, &dummy);
    /* this assumes nothing come from stderr... */
    retval = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
    gettimeofday(&end, &dummy);

    time0 = start.tv_sec + start.tv_usec/1e6;
    time1 = end.tv_sec + end.tv_usec/1e6;
    dt = time1-time0;

    dbgprintf("Asked to wait for %f sec, actually waited %f sec\n",
	      usec/1e6, dt);

    unlink(tmppath);
	somedesc = -1;
    
    return retval;
}

#else
// fake it really bad
int myusleep(int usec)
{
    sleep((int)(usec/1e6));
    return 0;
}

#endif // SLEEP functions

// semaphore replacement functions, if needs be
#ifndef HAVE_PTHREADS
void semaphore_down(int *) {
}

void semaphore_up(int *) {
}

void semaphore_init(int *) {
}

void semaphore_destroy(int *) {
}

#endif // ! HAVE_PTHREADS
