/*
 * $Id: savePrefs.cxx,v 4.1 2003/05/13 14:55:26 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.
 * */

/*------------------------------------------------------------------------
 *
 * A save preference panel using fluid
 *
 * Hugues Talbot	27 Apr 1998
 *      
 *-----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "imunistd.h"
#include <string.h>
#include <errno.h>
#include <FL/Fl_File_Chooser.H>
#include <FL/fl_ask.H>
#include "imview.hxx"
#include "imageIO.hxx"
#include "imageViewer.hxx"
#include "savePrefs.hxx"
#include "io/savetiff.hxx"
#include "io/saveZimage.hxx"
#include "io/writeMagick.hxx"

// 
typedef void (menucb)(Fl_Menu_ *, void *);

// application components
extern char appName[];
extern imageIO *IOBlackBox;
extern imageViewer *mainViewer;
extern saveprefs   *savePrefsPanel;

// compression names
extern char *comp_schemes[];


static const char *formatExtensionList[] = {
    ".tif",
    "_z",
    ".euro", // not implemented, but come in this number
    ".png",
    ".jpg",
    0
};


saveprefs::saveprefs()
{
    dbgprintf("Save panel constructed\n");
    saveWindow = 0; // fluid depends on that
    return;
}

void saveprefs::setDefaults()
{
    static char          buff[DFLTSTRLEN];
    static Fl_Menu_Item  *newMenu = 0;
    int                   newMenuSize, i;

    dbgprintf("Setting default for save panel\n");
    
    // the choice format menu is now set up within fluid
    latestGreyTiffOption = TIFF_CMP_LZW;
    latestBinaryTiffOption = TIFF_CMP_CCITTG4;
    setCompression(TIFF_FMT);
    
    // we save the whole image by default
    wholeCheck->set();

    // we save the visible image by default
    visibleCheck->set();
    // Let's reactivate the deactivated... Hugues Talbot	 2 Dec 2002
    //underlyingCheck->deactivate();
    //underlyingCheck->labelcolor(40); // deactivated for now...

    // I'm aghast fluid doesn't do that by itself...
    formatMenu = formatChoice->menu();
    newMenuSize = formatMenu->size();
    newMenu = new Fl_Menu_Item[newMenuSize];
    for (i = 0 ; i < newMenuSize ; i++) {
	newMenu[i] = formatMenu[i];
    }
    formatChoice->menu(newMenu);
    
    tiffmenuitem = newMenu + TIFF_FMT;
    pngmenuitem = newMenu + PNG_FMT;
    jpgmenuitem = newMenu + JPG_FMT; // those CAN be modified.
    
    // disable extra formats if ImageMagick not compiled in
#ifndef HAVE_MAGICK
    pngmenuitem->hide();
    jpgmenuitem->hide();
#endif

#ifndef HAVE_TIFF
    tiffmenuitem->hide();
#endif

    // set the default as Z-IMAGE (only format always available)
    formatChoice->value(ZIMAGE_FMT);
    
    // default value for file
    strncpy(buff, IOBlackBox->getImName(), DFLTSTRLEN);
    buff[DFLTSTRLEN-1] = '\0'; 
    filenameInput->value(buff);
    toggleExtension(ZIMAGE_FMT);
    
    
    return;
}


// this functions changes the .tiff in _z, etc...
void saveprefs::toggleExtension(int format)
{
    static char inputContent[DFLTSTRLEN]; 
    char *substr;

    strcpy(inputContent, filenameInput->value());
    
    dbgprintf("Looking for extensions\n");
    if (inputContent != 0) {
	for (int i = 0 ; formatExtensionList[i] != 0 ; i++) {
	    // look for the common extension of the format
	    if (((substr = strstr(inputContent, formatExtensionList[i])) != 0)
		&& (format < LAST_FMT)) {
		strcpy(substr, formatExtensionList[format]);
		filenameInput->value(inputContent);
		dbgprintf("Extension found\n");
		break;
	    }
	}
    }
    
    return;
}



void saveprefs::setCompression(int format)
{
    static const Fl_Menu_Item noCompressionMenu[] = {
	{"None", 0, 0},
	{0}
    };
    static const Fl_Menu_Item binaryTiffCompression[] = {
	{"None", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_NONE},
	{"LZW", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_LZW},
	{"PackBits", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_PACKBITS},
	{"Group 3", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_CCITTG3},
	{"Group 4", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_CCITTG4},
	{"RLE", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_RLE},
	{"RLEW", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_RLEW},
	{"Thunderscan", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_THUNDER},
	{0}
    };
    static const Fl_Menu_Item greyTiffCompression[] = {
	{"None", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_NONE},
	{"LZW", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_LZW},
	{"PackBits", 0, (Fl_Callback *)chosencompression_cb, (void *)TIFF_CMP_PACKBITS},
	{0}
    };

    dbgprintf("Setting up the Choice buttons\n");
    switch (format) {
      case TIFF_FMT:
	compressionChoice->activate();
	compressionChoice->labelcolor(FL_BLACK);
	compressionChoice->textcolor(FL_BLACK);
	if (IOBlackBox->getCurrImgPixType() == IM_BINARY) {
	    compressionChoice->menu(binaryTiffCompression);
	    compressionChoice->value(latestBinaryTiffOption);
	} else {
	    compressionChoice->menu(greyTiffCompression);
	    compressionChoice->value(latestGreyTiffOption);
	}
	saveWindow->redraw();
	break;

      case ZIMAGE_FMT:
      case PNG_FMT:
      case JPG_FMT:
	compressionChoice->menu(noCompressionMenu);
	compressionChoice->deactivate();
	compressionChoice->labelcolor(40); // from FLUID
	compressionChoice->textcolor(40);
	saveWindow->redraw();
	break;
	
      default:
	dbgprintf("Unknown file format\n");
	compressionChoice->menu(noCompressionMenu);
	compressionChoice->deactivate();
	compressionChoice->labelcolor(40); // from FLUID
	compressionChoice->textcolor(40);
	saveWindow->redraw();
	break;
    }

    
    return;
}

void saveprefs::saveFileName(const char *fn)
{
    // this copies it to something more permanent.
    filenameInput->value(fn);
    return;
}




int saveprefs::doTheSaving()
{
    int     retval = 0;
    uchar  *buf0, *buf1, *buf2;
    int     origx, origy, origz, nbrows, nbcols, nbcomps;
    int     val;

    dbgprintf("Doing the saving now\n");
    // we can't go any further until we have a writexxx procedure...

    const char *filename = filenameInput->value();
    if ((filename == 0) || (filename[0] == '\0')) {
	errprintf("Please specify a file to save to\n");
	return(1);
    } else {
	if (access(filename, W_OK) == 0) {
	    // the file already exists
	    static char alrtmsg[DFLTSTRLEN] ;
	    sprintf(alrtmsg,
		    "File %s already exists\n"
		    "Do you want to overwrite?\n",
		    filename);
	    if (fl_ask(alrtmsg) == 0) {
		// no, we don't want to overwrite...
		return(2);
	    }
	}
    }

    buf0 = buf1 = buf2 = 0;
    // are we saving the visible image?
    if (visibleCheck->value() == 1) {
	// we save the whole image
	if (wholeCheck->value() == 1) {
	    origx = IOBlackBox->getXOffset();  // this is a cop-out for the moment
	    origy = IOBlackBox->getYOffset();
            origz = IOBlackBox->getZOffset();
	    nbcols = IOBlackBox->imageWidth();
	    nbrows = IOBlackBox->imageHeight();
	    if ((nbcomps= IOBlackBox->wholeImage(&buf0, &buf1, &buf2)) == 0) {
		errprintf("Can't save image with %d samples per pixel\n",
			  IOBlackBox->imageDepth());
		return(10);
	    } 
	} else {
	    if ((nbcomps= IOBlackBox->subsetImage(&buf0, &buf1, &buf2,
						  origx, origy,
						  nbcols, nbrows)) == 0) {
		errprintf("Can't save image with %d samples per pixel\n",
			  IOBlackBox->imageDepth());
		return(11);
	    }
            origx += IOBlackBox->getXOffset();  // this is a cop-out for the moment
	    origy += IOBlackBox->getYOffset();
            origz = IOBlackBox->getZOffset();
	}

	switch (val = formatChoice->value()) {
	  case TIFF_FMT: {
	    int    pi, sf, spp, bps;
	    int    pixeltype, imagetype;
	    int    start[2], end[2];
	    void  *outbuffp[3];

	    // get the pixtype & imgtype
	    pixeltype = trivmin(IOBlackBox->getCurrImgPixType(), IM_UINT1);
	    imagetype = (nbcomps == 3) ? IM_RGB : IM_SPECTRUM ;
	    spp = nbcomps;
	    setTifftype(pixeltype, imagetype, &pi, &sf, &spp, &bps);
	    // sanity check
	    if (spp != nbcomps) {
		errprintf("This can't happen: spp(%d) != nbcomps(%d)\n",
			  spp, nbcomps);
		break;
	    }
	    outbuffp[0] = buf0;
	    outbuffp[1] = buf1;
	    outbuffp[2] = buf2;
	    start[0] = origx;
	    start[1] = origy;
	    end[0] = origx + nbcols - 1;
	    end[1] = origy + nbrows - 1;
	    retval = save_tiff(outbuffp, 0,
			       start, end,
			       pi, sf, spp, bps,
			       0, 0,
			       filename,
			       compressionChoice->value());
	    break;
	  }

	  case PNG_FMT: 
	    if (buf0 != 0) // no hard at all...
		retval = save_magick(filename, "png", nbcomps, nbcols, nbrows,
				       buf0, buf1, buf2);
	    break;

	  case JPG_FMT:
	    if (buf0 != 0) // no hard at all...
		retval = save_magick(filename, "jpg", nbcomps, nbcols, nbrows,
				       buf0, buf1, buf2);
	    break;
	  
	  case ZIMAGE_FMT: {
	    int nx[3], ny[3], nz[3], x0[3], y0[3], z0[3], ptype[3];
	    void *outbuffp[3];
	    
	    nx[0] = nx[1] = nx[2] = nbcols;
	    ny[0] = ny[1] = ny[2] = nbrows;
	    nz[0] = nz[1] = nz[2] = 1; // not a 3D image...
	    x0[0] = x0[1] = x0[2] = origx;
	    y0[0] = y0[1] = y0[2] = origy;
	    z0[0] = z0[1] = z0[2] = origz;
	    ptype[0] = ptype[1] = ptype[2] = \
		trivmin(IOBlackBox->getCurrImgPixType(), IM_UINT1);
	    outbuffp[0] = buf0;
	    outbuffp[1] = buf1;
	    outbuffp[2] = buf2;

	    retval = save_zimage(filename, nbcomps,
				 nx, ny, nz,
				 x0, y0, z0,
				 ptype,
				 outbuffp);
	    if (retval != 0)
		errprintf("Error in Z-IMAGE saving code\n"
			  "writing %s: %s\n",
			  filename, strerror(errno));
	    
	    break;
	  }

	  default:
	    errprintf("Saving under an unknown format: %d can't do that!\n", val);
	    retval = 5;
	    break;
	}
    } else {
        dbgprintf("Saving the raw data\n");
        switch (val = formatChoice->value()) {

        case TIFF_FMT: {
            int start[2], end[2], nz;
            int pi, sf, spp, bps;
            int ptype, itype;
            void **outbuffp;
                
            // get the pixtype & imgtype
	    ptype = IOBlackBox->getCurrImgPixType();
	    itype = IOBlackBox->getCurrImgImgType();
	    spp = IOBlackBox->getCurrImgNbSamples();
	    setTifftype(ptype, itype, &pi, &sf, &spp, &bps);
            dbgprintf("TIFF types: pi = %d, sf = %d, spp = %d, bps = %d\n",
                      pi, sf, spp, bps);
	    start[0] = IOBlackBox->getXOffset();
	    start[1] = IOBlackBox->getYOffset();
	    end[0] = start[0] + IOBlackBox->imageWidth() - 1;
	    end[1] = start[1] + IOBlackBox->imageHeight() - 1;
            
            outbuffp = IOBlackBox->getCurrBuffp();

	    retval = save_tiff(outbuffp, 0,
			       start, end,
			       pi, sf, spp, bps,
			       0, 0,
			       filename,
			       compressionChoice->value());

            nz = IOBlackBox->imageThickness();

            /* save 3D image as extra images (slices) in the file */
            /* as in a multi-page document */
            if (nz > 1) {
                void **slices;
                int nx, ny, bytespersample;
                
                warnprintf("3D images saved as a multi-page TIFF image\n");
                // Tru64 cxx 6.3 objects with that line for some reason
                slices = new void *[spp];
                nx = IOBlackBox->imageWidth();
                ny = IOBlackBox->imageHeight();
                bytespersample = trivmax(bps/8, 1);

                for (int i = 1 ; i < nz ; ++i) {
                    for (int j = 0 ; j < spp ; ++j) {
                        slices[j] = (void*)((char *)(outbuffp[j]) + i * (nx * ny * bytespersample));
                    }
                    retval = save_tiff(slices, 1,
                                       start, end,
                                       pi, sf, spp, bps,
                                       0, 0,
                                       filename,
                                       compressionChoice->value());
                }

                delete[] slices;
            }
        }
            break;


        case ZIMAGE_FMT: {
            int *nx = 0, *ny = 0, *nz = 0;
            int *x0 = 0, *y0 = 0, *z0 = 0, *ptype = 0;
            int nbcomp = IOBlackBox->getCurrImgNbComps();
            int fixedtype = static_cast<int>(IOBlackBox->getCurrImgPixType());

            if (nbcomp > 0) {
                nx = new int[nbcomp];
                ny = new int[nbcomp];
                nz = new int[nbcomp];
                x0 = new int[nbcomp];
                y0 = new int[nbcomp];
                z0 = new int[nbcomp];
                ptype = new int[nbcomp];

                for (int i = 0 ; i < nbcomp ; ++i) {
                    nx[i] = IOBlackBox->imageWidth();
                    ny[i] = IOBlackBox->imageHeight();
                    nz[i] = IOBlackBox->imageThickness();
                    x0[i] = IOBlackBox->getXOffset();
                    y0[i] = IOBlackBox->getYOffset();
                    z0[i] = IOBlackBox->getZOffset();
                    ptype[i] = fixedtype;
                }
                
                // this function handles type promotion
                retval = save_zimage(filename, 
                                     nbcomp,
                                     nx,
                                     ny,
                                     nz,
                                     x0,
                                     y0,
                                     z0,
                                     ptype,
                                     IOBlackBox->getCurrBuffp());
                if (retval)
                    errprintf("Saving of file %s failed with error %d\n", 
                              filename, retval);

                delete[] nx;
                delete[] ny;
                delete[] nz;
                delete[] x0;
                delete[] y0;
                delete[] z0;
                delete[] ptype;
            }
        }
            break;
        default:
            errprintf("Only TIFF and Z-IMAGE format supported for this\n");
            retval = 20;
            break;
        }

    }
    
    if (buf0 != 0) delete[] buf0;
    if (buf1 != 0) delete[] buf1;
    if (buf2 != 0) delete[] buf2;
    
    return retval;
}

void saveprefs::show()
{
    saveWindow->show();
}

void saveprefs::hide()
{
    saveWindow->hide();
}


// save panel associated callbacks

void filenameinput_cb(Fl_Input *, saveprefs *)
{
    dbgprintf("File name input callback\n");
    return;
}


void browsebutton_cb(Fl_Button *, saveprefs *panel)
{
    const char *currentfile;
    static const char *p = 0;
    
    dbgprintf("Browse button pressed on save prefs panel\n");
    
    currentfile = panel->filenameInputValue();
    switch (panel->selectedFileFormat()) {
      case TIFF_FMT:
	p = fl_file_chooser("Save to file", "*.[Tt][Ii][Ff]*", currentfile);
	break;
      case ZIMAGE_FMT:
	p = fl_file_chooser("Save to file", "*_z", currentfile);
	break;
      case PNG_FMT:
	p = fl_file_chooser("Save to file", "*.[Pp][Nn][Gg]", currentfile);
	break;
      case JPG_FMT:
	p = fl_file_chooser("Save to file", "*.[Jj][Pp][Gg]", currentfile);
	break;
      default:
	p = fl_file_chooser("Save to file", "*", currentfile);
	break;
    }
			
    if (p) panel->saveFileName(p);
    
    return;
}

void wholecheck_cb(Fl_Check_Button *, saveprefs *)
{
    dbgprintf("Whole image check button callback\n");
    return;
}

void subsetcheck_cb(Fl_Check_Button *, saveprefs *)
{
    dbgprintf("Subset image check button callback\n");
    return;
}

void visiblecheck_cb(Fl_Check_Button *, saveprefs *)
{
    dbgprintf("Visible image check button callback\n");
    return;
}

void underlyingcheck_cb(Fl_Check_Button *, saveprefs *)
{
    dbgprintf("Underlying image check button callback\n");
    return;
}

void formatchoice_cb(Fl_Choice *, saveprefs *)
{
    dbgprintf("Format choice menu callback\n");
    return;
}

void compressionchoice_cb(Fl_Choice *, saveprefs *)
{
    dbgprintf("Compression choice menu callback\n");
    return;
}

void cancelbutton_cb(Fl_Button *, saveprefs *panel)
{
    dbgprintf("Cancel Button pressed on save prefs panel\n");
    // hide the window
    panel->hide();
    return;
}

void okbutton_cb(Fl_Return_Button *, saveprefs *panel)
{
    dbgprintf("Print Button pressed on save prefs panel\n");
    if (panel->doTheSaving() == 0) {
	// hide the window
	panel->hide();
    }
    return;
}

// callback for the choice menu

void chosenformat_cb(Fl_Menu_ *, void *fmt)
{
    long val = (long)fmt;
    
    switch (val) {
      case TIFF_FMT:
	dbgprintf("TIFF Format selected\n");
	// selects the compressions available for TIFF
	savePrefsPanel->setCompression(TIFF_FMT);
	break;
      case ZIMAGE_FMT:
	dbgprintf("Z-IMAGE format selected\n");
	savePrefsPanel->setCompression(ZIMAGE_FMT);
	break;
      case PNG_FMT:
	dbgprintf("PNG format selected\n");
	savePrefsPanel->setCompression(PNG_FMT);
	break;
      case JPG_FMT:
	dbgprintf("PNG format selected\n");
	savePrefsPanel->setCompression(JPG_FMT);
	break;
	
    }
    savePrefsPanel->toggleExtension(val);
}

void chosencompression_cb(Fl_Menu_ *, void *cmp)
{
    long val = (long)cmp;

    switch(val) {
      case TIFF_CMP_NONE:
      case TIFF_CMP_LZW:
      case TIFF_CMP_PACKBITS:
	savePrefsPanel->setGreyTifComp(val);
      case TIFF_CMP_CCITTG4:
      case TIFF_CMP_CCITTG3:
      case TIFF_CMP_RLE:
      case TIFF_CMP_RLEW:
      case TIFF_CMP_THUNDER:
	savePrefsPanel->setBinTifComp(val);
	dbgprintf("TIFF compression selected: "
		  "%s\n", comp_schemes[val]);
	break;

      default:
	errprintf("Unknown compression scheme: %d\n", val);
	break;
    }

    return;
}
