/*
 * $Id: imdebug.cxx,v 4.1 2004/03/16 07:11:24 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 file contains debugging code.
 * Essentially a trivial replacement for printf that will actually output
 * messages to the standard output only if a global debugging variable is
 * set to 1.
 *
 * Hugues Talbot	 5 Jan 1998
 *
 *-----------------------------------------------------------------------*/

#include <imcfg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <FL/fl_ask.H>
#include "imview.hxx"
#include "StatusBox.hxx"
#ifdef HAVE_PTHREADS
#  include "server/semaphore.hxx"
#endif
#include "machine.hxx"
#include "imunistd.h"

extern int debugIsOn;
extern int stopDebug;
extern int serverDebug;
extern Semaphore access_debug, access_error;

#define BUFSIZE 1000

static FILE *outfile = 0;

// these debugging routines are trickier than it seems
// Let's explain the context: under Unix there is always
// a console available (where to print messages, etc). Under
// win32 this is not necessarily the case, hence the need for
// a GUI solution, provided by the StatusBox. This is nice, but
// now observe the IM_STATUS message. This brings up a nice window
// the first time it is called, and in doing so (a show()), further
// debugging messages spring forth. However the IM_STATUS is within
// the protected area, and therefore a deadlock ensues! This seems
// to occur _only_ under windows, but the problem is genuine.
// But the GUI is precisely what the semaphores are there to protect,
// so now, while in the protected area, further messages are ignored
// debugIsOn is set to false. We will almost certainly lose some
// debugging messages, but that can't be helped.
int dbgprintf(const char * msg,...)
{
    char strarg[BUFSIZE+1];
    char mess[BUFSIZE+24];
    int  ret = 0, msglen;
    va_list args;
    static bool overflow = false;
    

    if (debugIsOn && (msg[0] != '\0')) {
	semaphore_down(&access_debug);
	overflow = true;
	debugIsOn = 0; // silence all messages
	im_va_start(args,msg);
	msglen = strlen(msg);
	assert(msglen > 0); // I have seen this happen
	if (msg[msglen-1] == '\n') {
	    snprintf(strarg, BUFSIZE, DEBUGPROMPT "%s", msg);
	}
	// default WIN32 printf is not that good.
	// neither is the FLTK-replacement for (v)snprintf.
#if defined(WIN32_NOTCYGWIN) || (HAVE_SNPRINTF == 0)
	char newmess[BUFSIZE];
	strncpy(newmess,msg, BUFSIZE);
	newmess[BUFSIZE-1] = '\0';
	msglen = trivmin(BUFSIZE-1, msglen);
	if (newmess[msglen-1] == '\a') {
	    newmess[msglen-1] = '\0'; // shorten the string
	    snprintf(strarg, BUFSIZE, DEBUGPROMPT "%s ", newmess); // FLTK's replacement will cope with this
	} else if (newmess[msglen-1] == '\b') {
	    newmess[msglen-1] = '\0'; // shorten the string
	    snprintf(strarg, BUFSIZE, "%s\n", newmess);  // FLTK's replacement will cope with this
	}  
#else
	else if (msg[msglen-1] == '\a') {
	    snprintf(strarg, BUFSIZE, DEBUGPROMPT "%.*s ",msglen-1, msg);
	} else if (msg[msglen-1] == '\b') {
	    snprintf(strarg, BUFSIZE, "%.*s\n", msglen-1, msg); 
	} 
#endif
	else {
	    snprintf(strarg, BUFSIZE, "%s", msg);
	}
	
	//ret = vfprintf(stderr,strarg,args);
	ret = vsnprintf(mess, BUFSIZE+24, strarg, args);
	if (outfile == 0) {
#ifdef WIN32_NOTCYGWIN
	    outfile = fopen("c:/temp/imbugs.txt", "w");
#else
	    outfile = fopen("/tmp/imbugs.txt", "w");
#endif
	}
	if (outfile != 0) {
	    fprintf(outfile, mess);
	    fflush(outfile); /* slow, but WHO CARES!! */
	}
	IM_STATUS(mess);
	va_end(args);
	debugIsOn = 1; // enable messages again
	overflow = false;
	semaphore_up(&access_debug);
	if (stopDebug) {
	    getchar();
	}
    } else if (overflow) {
	// simply printf the message, this is debugging the debugging process...
	// we are assuming that printf is thread-safe.
	im_va_start(args,msg);
	msglen = strlen(msg);
	if ((msg[msglen-1] == '\n') || (msg[msglen-1] == '\a')) {
	    snprintf(strarg, BUFSIZE, "lost message: %s", msg);
	} else if (msg[msglen-1] == '\b') {
	    snprintf(strarg, BUFSIZE, "%s\n", msg);
	}
	vprintf(strarg, args);
	va_end(args);
	// we are only doing it for dbgprintf at this stage to get a feel of how much is lost...
    }
    
    return ret;
}

/* same thing, except it doesn't stop for char input,
   and prints a slightly different message */
int srv_dbgprintf(const char * msg,...)
{
    char strarg[BUFSIZE];
    char mess[BUFSIZE+24];
    int  ret = 0, msglen;
    va_list args;

//     if (debugIsOn) {
//       im_va_start(args, msg);
//       vfprintf(stderr, msg, args);
//       return 0;
//     }

    if (debugIsOn) {
	semaphore_down(&access_debug);
	debugIsOn = 0; // ignore further messages, ha.
	im_va_start(args,msg);
	msglen = strlen(msg);
	if (msg[msglen-1] == '\n') {
	    snprintf(strarg, BUFSIZE,  SRVDBGPRMPT "%s", msg);
	}
#ifdef WIN32_NOTCYGWIN
	// far messier
	char newmess[BUFSIZE];
	strncpy(newmess,msg, BUFSIZE);
	newmess[BUFSIZE-1] = '\0';
	msglen = trivmin(BUFSIZE-1, msglen);
	if (newmess[msglen-1] == '\a') {
	    newmess[msglen-1] = '\0'; // shorten the string
	    snprintf(strarg, BUFSIZE, SRVDBGPRMPT "%s ", newmess);
	} else if (newmess[msglen-1] == '\b') {
	    newmess[msglen-1] = '\0'; // shorten the string
	    snprintf(strarg, BUFSIZE, "%s\n", newmess); 
	}
#else
	else if (msg[msglen-1] == '\a') {
	    snprintf(strarg, BUFSIZE, SRVDBGPRMPT "%.*s ",msglen-1, msg);
	} else if (msg[msglen-1] == '\b') {
	    snprintf(strarg, BUFSIZE, "%.*s\n", msglen-1, msg); 
	}
#endif
	else {
	    snprintf(strarg, BUFSIZE, "%s", msg);
	}
	//ret = vfprintf(stderr,strarg,args);
	ret = vsnprintf(mess, BUFSIZE+24, strarg, args);
	if (outfile == 0) {
#ifdef WIN32_NOTCYGWIN
	    outfile = fopen("c:/temp/imbugs.txt", "w");
#else
	    outfile = fopen("/tmp/imbugs.txt", "w");
#endif
	}
	if (outfile != 0) {
	    fprintf(outfile, mess);
	    fflush(outfile); /* slow, but WHO CARES!! */
	}
	IM_STATUS(mess);
	va_end(args);
	debugIsOn = 1;
	semaphore_up(&access_debug);
    }
    
    return ret;
}

/* same thing, except it doesn't stop for char input,
   and prints a slightly different message */
int srv_internal_dbgprintf(const char * msg,...)
{
    char strarg[BUFSIZE];
    char mess[BUFSIZE+24];
    int  ret = 0, msglen;
    va_list args;

    if (serverDebug) {
	semaphore_down(&access_debug);
	serverDebug = 0; // ignore further messages, ha!
	im_va_start(args,msg);
	msglen = strlen(msg);
	if (msg[msglen-1] == '\n') {
	    snprintf(strarg, BUFSIZE, SRVIDBGPRMPT "%s", msg);
	}
#ifdef WIN32_NOTCYGWIN
	char newmess[BUFSIZE];
	strncpy(newmess,msg, BUFSIZE);
	newmess[BUFSIZE-1] = '\0';
	msglen = trivmin(BUFSIZE-1, msglen);
	if (newmess[msglen-1] == '\a') {
	    newmess[msglen-1] = '\0'; // shorten the string
	    snprintf(strarg, BUFSIZE, SRVIDBGPRMPT "%s ", newmess);
	} else if (newmess[msglen-1] == '\b') {
	    newmess[msglen-1] = '\0'; // shorten the string
	    snprintf(strarg, BUFSIZE, "%s\n", newmess); 
	}
#else
	else if (msg[msglen-1] == '\a') {
	    snprintf(strarg, BUFSIZE, SRVIDBGPRMPT "%.*s ",msglen-1, msg);
	} else if (msg[msglen-1] == '\b') {
	    snprintf(strarg, BUFSIZE, "%.*s\n", msglen-1, msg); 
	}
#endif
	else {
	    snprintf(strarg, BUFSIZE, "%s", msg);
	}
	//ret = vfprintf(stderr,strarg,args);
	ret = vsnprintf(mess, BUFSIZE+24, strarg, args);
	IM_STATUS(mess);
	va_end(args);
	serverDebug = 1;
	semaphore_up(&access_debug);
    }
    
    return ret;
}

/* protected printf for the server */
int srv_printf(const char * msg,...)
{
    int  ret = 0;
    va_list args;

    semaphore_down(&access_debug);
    im_va_start(args,msg);
    ret = vprintf(msg,args);
    va_end(args);
    fflush(stdout);
    semaphore_up(&access_debug);
    
    return ret;
}

// this will actually print an error on the standard output stream
// as well as on the error console
int stderrprintf(const char * msg,...)
{
    char strarg[BUFSIZE];
    char mess[BUFSIZE+24];
    int  ret = 0;
    va_list args;

    semaphore_down(&access_error);
    im_va_start(args,msg);
    snprintf(strarg, BUFSIZE, ERRORPROMPT "%s", msg);
    //ret = vfprintf(stderr,strarg,args);
    ret = vsnprintf(mess, BUFSIZE+24, strarg, args);
    IM_ERROR(mess); // on the error/warning console
    fprintf(stderr, mess); // on the std error stream
    va_end(args);
    semaphore_up(&access_error);
    
    return ret;
}

// This will pop up an error dialog
int errprintf(const char * msg,...)
{
    char strarg[BUFSIZE];
    int  ret = 0;
    va_list args;

    semaphore_down(&access_error);
    im_va_start(args,msg);
    ret = vsnprintf(strarg, BUFSIZE, msg,args);
    va_end(args);

    fl_alert(strarg); // this can block for quite a long time!
    semaphore_up(&access_error);
    
    return ret;
}


// This will show up in the status box
int warnprintf(const char * msg,...)
{
    char strarg[BUFSIZE];
    char mess[BUFSIZE+24];
    int  i, j, ret = 0;
    va_list args;

    semaphore_down(&access_error);
    im_va_start(args,msg);
    // separate the lines
    IM_WARNING("Warning:\n");
    vsnprintf(strarg, BUFSIZE, msg, args);
    for (i=0, j= 0; ((i < BUFSIZE) && (strarg[i] != '\0')); i++, j++) {
	mess[j] = strarg[i];
	if (strarg[i] == '\n') {
	    mess[j+1] = '\0'; // OK due to +24
	    IM_WARNING(mess);
	    j = -1; // j++ gets executed
	}
    }
    IM_WARNING("\n");
    va_end(args);
    semaphore_up(&access_error);
    
    return ret;
}


