/*
 * $Id: printPrefs.cxx,v 4.5 2004/05/24 23:45:12 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 print preference panel using fluid.
 *
 * Hugues Talbot	21 Apr 1998
 *      
 *-----------------------------------------------------------------------*/

#include "imnmspc.hxx"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "imunistd.h"
#include <errno.h>
#include <string> // needed by nocase.H
#include <FL/Fl_File_Chooser.H>
#include <FL/fl_ask.H>
#include "imview.hxx"
#include "nocase.hxx"
#include "io/imSystem.hxx"
#include "imageIO.hxx"
#include "imageViewer.hxx"
#include "menubar.hxx"
#include "printPrefs.hxx"
#include "io/newprefio.hxx"
#include "io/cpostscript.hxx"
#include "machine.hxx"

extern char appName[];
extern imageIO *IOBlackBox;
extern imageViewer *mainViewer;
extern imViewMenuBar *mainMenuBar;
extern imprefs *fileprefs;
extern printprefs *printPrefsPanel;

// the `broken pipe' signal handler
static void brokenPipeHandler(int sig);

// the siglongjmp buffer
static im_jmp_buf bpenv;

// print panel methods

printprefs::printprefs()
{
    dbgprintf("Print panel constructed\n");
    prefWindow = 0; // fluid depend on that...
    return;
}

void printprefs::setDefaults()
{
    static char buff[100];
    char *myenv;

    // we print to a real printer by default
    printerCheck->set();

    // default printing output
    if ((myenv = getenv("PRINTER")) != 0)
	sprintf(buff, "lpr -P%s", myenv);
    else
	strcpy(buff, "lpr");
    commandInput->value((const char *)buff);

    // default value for file
    sprintf(buff, "%s.ps", appName);
    filenameInput->value(buff);

    // empty title is fine...

    // orientation should be portrait
    portraitCheck->set();

    // Paper size should be A4
    // A4Check->set();
    papertype_ = PAGESIZE_A4;
    papersizeChoice->value(papertype_);
    
    // language should be PS
    psCheck->set();

    // by default we only print the image that is showing now
    oneImageCheck->set();

    // number of copies should be just one
    nbcopiesInput->value("1");

    // By default we print the whole image
    wholeCheck->set();
    
    // that's it
    
    return;
}

// this method switches between the `File' and the `Printer' defaults
void printprefs::toggleToFile()
{
    // disable the Print command
    commandInput->deactivate();
    commandInput->color(47); // got that from fluid...
    commandInput->textcolor(31);
    commandInput->labelcolor(31);
    // enable the "file" command
    filenameInput->activate();
    filenameInput->color(43);
    filenameInput->textcolor(FL_BLACK);
    filenameInput->labelcolor(FL_BLACK);
    // enable the browse button
    browseButton->activate();
    browseButton->labelcolor(FL_BLACK);

    prefWindow->redraw();
    
    return;
}

void printprefs::toggleToPrinter()
{
    // disable the Print command
    filenameInput->deactivate();
    filenameInput->color(47); // got that from fluid...
    filenameInput->textcolor(31);
    filenameInput->labelcolor(31);
    // enable the "file" command
    commandInput->activate();
    commandInput->color(43);
    commandInput->textcolor(FL_BLACK);
    commandInput->labelcolor(FL_BLACK);
    // disable the browse button
    browseButton->deactivate();
    browseButton->labelcolor(31);

    prefWindow->redraw();

    return;
}

void printprefs::allowTitle(bool b)
{
    if (b) {
	// setting the title is allowed
	titleInput->activate();
	titleInput->color(43); // fluid...
	titleInput->textcolor(FL_BLACK);
	titleInput->labelcolor(FL_BLACK);
    } else {
	titleInput->deactivate();
	titleInput->color(47);
	titleInput->textcolor(31);
	titleInput->textcolor(31);
    }
    
    prefWindow->redraw();
    
    return;
}

void printprefs::setTitle(const char *t)
{
    titleInput->value(t);
    return;
}

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

void printprefs::setMargins(long whichItem)
{
    switch ((margin_size)whichItem) {
    case MARGIN_SIZE_STANDARD:
    case MARGIN_SIZE_SMALL:
    case MARGIN_SIZE_LARGE:
    case MARGIN_SIZE_ZERO:
	postscript_set_margin_index((margin_size)whichItem);
	// then set valus for margin inputs
	break;
    case MARGIN_SIZE_CUSTOM:
	postscript_set_margin_index((margin_size)whichItem);
	// need to set custom margins too
	// then GET value from margin inputs
	break;
    default:
	warnprintf("Incorrect size option %ld\n", whichItem);
	break;
    }
    return;
}

static void brokenPipeHandler(int sig)
{
    // reset the signal handler
    signal(sig, brokenPipeHandler);
    im_longjmp(bpenv, 1);
}

// the following is a tiny bit hackish.
// the vp=&... are seemingly useless statements that
// prevent the compiler from optimizing the automatic variable
// into registers, thus putting the longjmp in jeopardy.
// in fact there is no problem, but that's the easiest way
// I found for stopping gcc warning me about the possibility.
int printprefs::doThePrinting(bool preview)
{
    HIMAGE  *tbp; // don't you love C?
    volatile void *vp;
    int     retval = 0; vp = &retval;
    byte    win_type; vp = &win_type;
    byte    orientation; vp = &orientation;
    bool    usePipe = false; vp = &usePipe;
    int     nbcopies, nbitems, i; vp = &nbcopies; vp = &nbitems; vp = &i;
    char   *title, *filename, *pipeCommand; vp = &title; vp = &filename; vp = &pipeCommand;
    char   *fileList, *tmpPreview; vp = &fileList; vp = &tmpPreview;
    uchar  *buf0, *buf1, *buf2; vp = &buf0;vp = &buf1; vp = &buf2;
    FILE   *outfile; vp = &outfile;

    // how many images are we going to save?
    if (wholeListCheck->value() == 1) {
	fileList = mainMenuBar->getItemListContent(IMAGE_LIST_ID, nbitems);
    } else {
	fileList = 0;
	nbitems = 1;
    }

    tbp = new HIMAGE[nbitems];

    dbgprintf("about to print %d items\n", nbitems);
    
    for (i = 0 ; i < nbitems ; i++) {
	// fill up what we can from the outset
	if (IOBlackBox->getCurrImgPixType() == IM_BINARY)
	    tbp[i].type = PS_BINARY;
	else
	    tbp[i].type = PS_CHAR; // we don't care, but not binary    

	buf0 = buf1 = buf2 = 0;
    
	// first question: are we printing the whole image or only a subset?
	if (wholeCheck->value() == 1) {
	    // the whole thing
	    tbp[i].nbrows = IOBlackBox->imageHeight();
	    tbp[i].nbcols = IOBlackBox->imageWidth();
	    if ((tbp[i].nbcomp = IOBlackBox->wholeImage(&buf0, &buf1, &buf2)) != 0) {
		tbp[i].buf0 = buf0;
		tbp[i].buf1 = buf1;
		tbp[i].buf2 = buf2;
	    } else {
		errprintf("Can't print images with %d samples per pixel\n",
			  IOBlackBox->imageDepth());
		return(10);
	    }
	} else {
	    // the subset currently displayed
	    int bx, by, bw, bh; // dimensions of the subset...
       
	    if ((tbp[i].nbcomp = IOBlackBox->subsetImage(&buf0, &buf1, &buf2,
							 bx, by, bw, bh)) != 0) {

		tbp[i].nbrows = bh;
		tbp[i].nbcols = bw;
		tbp[i].buf0 = buf0;
		tbp[i].buf1 = buf1;
		tbp[i].buf2 = buf2;	    
	    } else {
		errprintf("Can't print images with %d samples per pixel\n",
			  IOBlackBox->imageDepth());
		return(11);
	    }
	}
	// display the next image 
	if (nbitems > 1) {
	    dbgprintf("Showing the next image\n");
	    nextimage_cb(0,0);
	}
    }

    

    // get all the options now
    // paper type selection with Choice menu now.
    // if (A3Check->value()) papertype = A3_FMT;
    //     else if (letterCheck->value()) papertype = LE_FMT;
    //     else papertype = A4_FMT;

    if (epsCheck->value()) win_type = WIN_EPS;
    else win_type = WIN_ALL;

    if (landscapeCheck->value()) orientation = ORI_LDS;
    else orientation = ORI_PRT;

    nbcopies = MYMAX(atoi(nbcopiesInput->value()), 1);

    title = (char *)(titleInput->value());
    if ((title != 0) && (title[0] == '\0'))
	title = 0; // empty string
    
    // open the pipe!
    

    if (printerCheck->value() && !preview) {
	// we pipe to a command, which hopefully will print
	// (not our problem if it doesn't...

	pipeCommand = (char *)commandInput->value();
	if ((pipeCommand != 0) && (pipeCommand[0] == '\0')
	    || (pipeCommand == 0)) {
	    errprintf("Please specify a print command\n");
	    retval = 1;
	} else {
	    // 2:set the `broken pipe' signal handler
	    outfile = 0;
	    im_signal(SIGPIPE, brokenPipeHandler);
	    if (im_setjmp(bpenv, 1) == 0) { // save the mask buffer
		// 3: try to open the pipe for writing
		if ((outfile = popen(pipeCommand, "w")) == 0) {
		    errprintf("Cannot open pipe to command %s\n%s",
			      pipeCommand,
			      strerror(errno));
		    retval = 2;
		}
	    } else {
		// we got there only through the signal handler
		if (outfile != 0)
		    fclose(outfile);
		if (pipeCommand != 0)
		    errprintf("Broken pipe: invalid command %s\n", pipeCommand);
		retval = 3;
	    }
	    usePipe = true; // we'll have to use pclose later on
	}
    } else if ((printerCheck->value() == 0) && !preview) {
	// we send to a file
	filename = (char *)filenameInput->value();
	if ((filename != 0) && (filename[0] == '\0')
	    || (filename == 0)) {
	    errprintf("Please specify a file to print to\n");
	    retval = 4;
	} else {
	    if (access(filename, W_OK) == 0) {
		// the file aready exist, do we really want to do this?
		static char alrtmsg[1024];
		sprintf(alrtmsg,
			"File %s already exists\n"
			"Do you want to overwrite?\n",
			filename);
		if (fl_ask(alrtmsg) == 0) {
		    retval = 6;
		    goto error;
		} 
	    } 
	    if ((outfile = im_fopen(filename, "wb")) == 0) {
		errprintf("Can't open file %s\n%s",
			  filename,
			  strerror(errno));
		retval = 5;
	    }
	}
    } else {
	// preview	// 1: get the preview command
	tmpPreview = tempnam(0, "ImViu");

	if ((outfile = im_fopen(tmpPreview, "wb")) == 0) {
	    errprintf("Can't open temporary file %s\n%s",
		      tmpPreview,
		      strerror(errno));
	    free(tmpPreview);
	    retval = 5;
	}
    }

    if (retval == 0) {
	// all is fine, we can send the postscript code away.
	dbgprintf("Composing PS code\n");
	compose_PostScript_code(tbp,nbitems,1.0,
				papertype_,
				win_type,
				orientation,
				nbcopies,
				0,0,
				title,
				"Times-Roman",0, 1,
				outfile);
	if (preview) {
	    int gslen = strlen(fileprefs->gvpath()) + 2*strlen(tmpPreview);
	    char *previewCommand = new char[gslen+20];

	    sprintf(previewCommand, "(%s %s ; rm -f %s) &",
		    fileprefs->gvpath(),
		    tmpPreview, tmpPreview);
	    
	    fflush(outfile); // send Everything, but do not close.
	    system(previewCommand);

	    delete[] previewCommand;
	    free(tmpPreview); // we can get rid of this now.
	} 


	if (usePipe) { // if we are previewing, pclose will have to occur later
	    pclose(outfile);
	    outfile = 0;
	} else  {  // even if we are previewing
	    fclose(outfile);
	    outfile = 0;
	}
    }

 error:
    // probably deleting 0 would be acceptable, but hey.
    for (i = 0 ; i < nbitems ; i++) {
	if (tbp[i].buf0 != 0) delete[] tbp[i].buf0;
	if (tbp[i].buf1 != 0) delete[] tbp[i].buf1;
	if (tbp[i].buf2 != 0) delete[] tbp[i].buf2;
    }

    if (fileList != 0) delete[] fileList;
    delete[] tbp;
    
    return retval;
}

void printprefs::show()
{
    prefWindow->show();
    return;
}

void printprefs::hide()
{
    prefWindow->hide();
    return;
}

    

// print panel associated callbacks

void printercheck_cb(Fl_Check_Button *, printprefs *panel)
{
    dbgprintf("Printer check callback\n");
    panel->toggleToPrinter();
    
    return;
}

void filecheck_cb(Fl_Check_Button *, printprefs *panel)
{
    dbgprintf("File check callback\n");
    panel->toggleToFile();

    
    return;
}

void commandInput_cb(Fl_Input *, printprefs *)
{
    dbgprintf("Command input callback\n");

    return;
}

void filenameInput_cb(Fl_Input *, printprefs *)
{
    dbgprintf("filename input callback\n");

    return;
}

void browsebutton_cb(Fl_Button *, printprefs *panel)
{
    const char *currentfile;
    static const char *p = 0;

    dbgprintf("Browse Button pressed on print prefs panel\n");

    currentfile = panel->filenameInputValue();
    
    p = fl_file_chooser("Print to file", "*[pP][sS]", currentfile);
			
    if (p) panel->printFileName(p);
    
    return;
}


void titleInput_cb(Fl_Input *, printprefs *)
{
    dbgprintf("title input callback\n");

    return;
}

void portraitcheck_cb(Fl_Check_Button *, printprefs *)
{
    dbgprintf("Portrait check callback\n");

    return;
}

void landscapecheck_cb(Fl_Check_Button *, printprefs *)
{
    dbgprintf("Landscape check callback\n");

    return;
}

void papersize_cb(Fl_Choice *c, printprefs *s)
{
    dbgprintf("Paper choice = %d\n", c->value());
    s->setpapertype(c->value());
}

void a4check_cb(Fl_Check_Button *, printprefs *)
{
    dbgprintf("A4 check callback\n");

    return;
}

void lettercheck_cb(Fl_Check_Button *, printprefs *)
{
    dbgprintf("Letter check callback\n");

    return;
}

void a3check_cb(Fl_Check_Button *, printprefs *)
{
    dbgprintf("A3 check callback\n");

    return;
}

void pscheck_cb(Fl_Check_Button *, printprefs *panel)
{
    dbgprintf("PS check callback\n");
    panel->allowTitle(true);
    
    return;
}

void epscheck_cb(Fl_Check_Button *, printprefs *panel)
{
    dbgprintf("EPS check callback\n");
    panel->allowTitle(false);
    
    return;
}

void nbcopies_cb(Fl_Input *, printprefs *)
{
    dbgprintf("Nb of copies callback\n");
    return;
}


void oneimagecheck_cb(Fl_Check_Button *, printprefs *)
{
    dbgprintf("One image check callback\n");
    
    return;
}

void wholelistcheck_cb(Fl_Check_Button *, printprefs *)
{
    dbgprintf("Whole list check callback\n");
    
    return;
}

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

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

// 2- Margin tab callbacks
void marginchoice_cb(Fl_Choice *, printprefs *panel)
{
    dbgprintf("Margin dimensions callback\n");
    return;
}

void chosenmargin_cb(Fl_Menu_ *, void *value)
{
    long v = (long)value;
    dbgprintf("Margin chosen = %d\n", v);
    printPrefsPanel->setMargins(v);
    return;
}

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

void previewbutton_cb(Fl_Button *bt, printprefs *panel)
{
    dbgprintf("Preview button pressed\n");
    panel->doThePrinting(true); // this is the preview switch
    return;
}

// This is really the `print' button...
void okbutton_cb(Fl_Return_Button *, printprefs *panel)
{
    dbgprintf("Print Button pressed on print prefs panel\n");
    if (panel->doThePrinting() == 0) {
	// hide the window
	panel->hide();
    } // else leave the panel around for the user to correct things
    return;
}


