/*
 * $Id: readMagick.cxx,v 4.2 2005/02/05 14:46:02 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.
 * */


/*------------------------------------------------------------------------
 *
 * Interface to the Image Magick Library.
 *
 * This library reads squillions of formats. Too good to be true.
 * Hugues Talbot	22 Jul 2000
 *
 * Yeah, too good to be true: the API keeps changing from under me, it's
 * a real nightmare! 14 May 2002
 *      
 *-----------------------------------------------------------------------*/

#include "imview.hxx"

#ifdef HAVE_MAGICK


#include <stdio.h>
#include <setjmp.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include "../imageIO.hxx"

extern imageIO *IOBlackBox;
// typical simple C solution


#include <magick/api.h>

static ImageInfo     *image_info = 0;
static Image         *magickImage = 0;
static char          currentName[1024];

jmp_buf              magick_jmp;

static void MagickWarningHandler(const ExceptionType warning,
				 const char *message,const char *qualifier);

static void MagickErrorHandler(const ExceptionType error,
			       const char *message,
			       const char *qualifier);

#ifdef MAGICK_NEEDS_MORE_EXCEPTION
#  define myIsGrayImage IsGrayImage
#  define myDispatchImage DispatchImage
#else /* extra exception argument not expected */
#  define myIsGrayImage(X,Y) IsGrayImage(X)
#  define myDispatchImage(A,B,C,D,E,F,G,H,I) DispatchImage(A,B,C,D,E,F,G,H)
#endif

void initImageMagick(char *path)
{
    dbgprintf("Image Magick Library initialized\n");
#ifdef MAGICK_USES_INIT  // #$#@%!
    InitializeMagick(path);
#else       
    MagickIncarnate(path);
#endif      
    SetErrorHandler(MagickErrorHandler);
    SetWarningHandler(MagickWarningHandler);
}

// returns the number of frames present
// in a Magick File
int magicknbsubfiles(const char *)
{
    int    nbframes;
    Image *p;
    
    // there is no other way than reading all the frames here
    for (p=magickImage, nbframes = 0; p != (Image *) NULL; p=p->next) {
	nbframes++;
    }
    return nbframes; // not too strenuous an effort here...
}

// this function tests if an image can be read with the ImageMagick library
// the idea is to call ReadImage only once. It's slow enough as it is...
bool testMagickImage(const char *name)
{
    ExceptionInfo        exception;

    dbgprintf("Test Magick called. Can ImageMagick read this image?\a");

    GetExceptionInfo(&exception);
    if (image_info == 0)
        image_info=CloneImageInfo((ImageInfo *) NULL);
    strcpy(image_info->filename, name);
    magickImage=ReadImage(image_info,&exception); // so far so good
    if (magickImage == 0) {
#ifdef MAGICK_USES_REASON // I hate gratuitous API changes
        // for ImageMagick 5.2.7-2 (on RH7.1)
        MagickWarning(exception.severity,exception.reason,exception.description);
#else
	// for other ImageMagick versions < 5.2.7-2
        MagickWarning(exception.severity,exception.message,exception.qualifier);
#endif
	DestroyImageInfo(image_info); // not needed anymore
	image_info = 0;
	dbgprintf("... No\b");
	return false; // can not read
    } else {
        strncpy(currentName, name, 1024);
	dbgprintf("... Yes!\b");
	return true; // can read
    }
}

int readMagickImage(const char *name, int frame)
{
    ExceptionInfo        exception;
    Image               *p;
    int                  retval = 0;
    uchar               *outbuf; 
    int                  w, h, d, frameId;
    static char          desc[100];


    dbgprintf("ReadMagickImage called\n");

    
    if (setjmp(magick_jmp)) {
	// if we get here, ImageMagick has detected an error. Bail out
	if (magickImage) {
	    DestroyImage(magickImage);
	    magickImage = 0;
	}
	if (image_info) {
	    DestroyImageInfo(image_info);
	    image_info = 0;
	}
	return 100;
    }

    // initializes the exception information
    GetExceptionInfo(&exception);
    
    if ((magickImage == 0) || (strncmp(currentName, name, 1024) != 0)) {
	// we need to read the image again
	if (image_info == 0)
	    image_info=CloneImageInfo((ImageInfo *) NULL);
	strcpy(image_info->filename, name);
	magickImage=ReadImage(image_info,&exception); // so far so good
    }
    
    if (magickImage != 0) {
	for (p=magickImage, frameId = 0; (p != (Image *) NULL) && (frameId != frame) ; p=p->next) {
	    frameId++;
	}
	
	w = p->columns;
	h = p->rows;
	if (myIsGrayImage(p,&exception)) {
	    d = 1;
	    outbuf = (uchar *) malloc(w*h*d*sizeof(uchar));
	    myDispatchImage(p, 0, 0, w, h, "R", CharPixel, outbuf,&exception);
	} else {
	    d = 3;
	    outbuf = (uchar *) malloc(w*h*d*sizeof(uchar));
	    myDispatchImage(p, 0, 0, w, h, "RGB", CharPixel, outbuf, &exception);
	}
	// talk to the black box
	IOBlackBox->setImData(outbuf);
	IOBlackBox->setCurrImgWidth(w);
	IOBlackBox->setCurrImgHeight(h);
	IOBlackBox->setCurrImgThickness(1);
	IOBlackBox->setXOffset(0);
	IOBlackBox->setYOffset(0);
	IOBlackBox->setZOffset(0);
	IOBlackBox->setCurrImgNbComps(1); // only a single image in a Magick file 
	IOBlackBox->setCurrImgNbSamples(d);
	IOBlackBox->setCurrImgSpp(d); // needed here because no currBuffp
	IOBlackBox->setCurrPixType(IM_UINT1);
	IOBlackBox->setCurrImgType((d == 1) ? IM_SINGLE : ((d == 3) ? IM_RGB : IM_SPECTRUM));

	strncpy(desc, magickImage->magick, 100);
	IOBlackBox->setImgDesc(desc);	

	DestroyImage(magickImage);
	currentName[0] = '\0';
	magickImage = 0; // so we know it has not been read yet.
    } else {
#ifdef MAGICK_USES_REASON // God I hate gratuitous API changes
        // for ImageMagick 5.2.7-2 (on RH7.1)
        MagickError(exception.severity,exception.reason,exception.description);
#else
	// for other ImageMagick versions < 5.2.7-2
        MagickError(exception.severity,exception.message,exception.qualifier);
#endif
    }

    DestroyImageInfo(image_info);
    image_info = 0;
    
    return retval;
}


// Runs wa
static void MagickErrorHandler(const ExceptionType error,
				const char *message,
				const char *qualifier)
{
    char errMsg[1024], tmpMsg[1024];
    int  msglen;
    
    // No longer needed ? DestroyDelegateInfo();
    if (message == (char *) NULL) {
	errprintf("ImageMagick reports NULL error message...\n");
	longjmp(magick_jmp, 1);
    }
    (void) snprintf(errMsg, 1024,"%.256s: %s\n",SetClientName((char *) NULL),
		    message);
    msglen = strlen(errMsg);
    if (qualifier != (char *) NULL) {
	snprintf(tmpMsg, 1024-msglen, "(%s)\n", qualifier);
	strcat(errMsg, tmpMsg);
	msglen = strlen(errMsg);
    }
    if ((error != OptionError) && errno) {
	snprintf(tmpMsg, 1024-msglen, "[%.1024s]",strerror(errno));
        strcat(errMsg, tmpMsg);
    }
    errprintf(errMsg);
    longjmp(magick_jmp,1);
}


static void MagickWarningHandler(const ExceptionType warning,
				 const char *message,const char *qualifier)
{
    char errMsg[1024], tmpMsg[1024];
    int  msglen;
    
    if (message == (char *) NULL)
	return;
    snprintf(errMsg, 1024, "%.256s: %s\n",SetClientName((char *) NULL),
	     message);
    
    msglen = strlen(errMsg);

    if (qualifier != (char *) NULL) {
	snprintf(tmpMsg, 1024-msglen, "(%s)\n",qualifier);
	strcat(errMsg, tmpMsg);
	msglen = strlen(errMsg);
    }
    if ((warning != OptionWarning) && errno) {
	snprintf(tmpMsg, 1024-msglen, "[%.1024s]",strerror(errno));
	strcat(errMsg, tmpMsg);
    }
    
    warnprintf(errMsg);

    return;
}


#else // HAVE_MAGICK

void initImageMagick(char *)
{
    // nothing to do   
}

bool testMagickImage(const char *)
{
    return false; // we can do nothing
}

int readMagickImage(const char *, int)
{
    errprintf("This application was not linked with the ImageMagick Library\n");
    return 1;
}

int magicknbsubfiles(const char *)
{
    return 0;
}



#endif // HAVE_MAGICK
