/*
 *  This file is part of the KDE libraries
 *  Copyright (c) 2001 Michael Goffioul <kdeprint@swing.be>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License version 2 as published by the Free Software Foundation.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 **/

#include "printwrapper.h"

#include <unistd.h>
#include <signal.h>
#include <sys/time.h>

#include <qstring.h>
#include <qstringlist.h>
#include <stdlib.h>
#include <kmessagebox.h>
#include <qfile.h>
#include <qtimer.h>
#include <qregexp.h>
#include <qsocketnotifier.h>

#include <kapplication.h>
#include <kcmdlineargs.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <kio/netaccess.h>
#include <kurl.h>
#include <kdebug.h>
#include <ktempfile.h>

#include <kprinter.h>
#include <kdeprint/kmmanager.h>
#include <kdeprint/kmprinter.h>
#include <kdeprint/kprintdialog.h>

void signal_handler(int);
QString tempFile;
bool fromStdin = false;
char job_output = 0;	// 0: dialog, 1: console, 2: none
char readchar = '\0';
bool dataread = false;
bool docopy = false;

void showmsgdialog(const QString& msg, int type = 0)
{
	switch (type)
	{
	   case 0: KMessageBox::information(NULL,msg,i18n("Print Information")); break;
	   case 1: KMessageBox::sorry(NULL,msg,i18n("Print Warning")); break;
	   case 2: KMessageBox::error(NULL,msg,i18n("Print Error")); break;
	}
}

void showmsgconsole(const QString& msg, int type = 0)
{
	QString	errmsg = QString::fromLatin1("%1 : ").arg((type == 0 ? i18n("Print info") : (type == 1 ? i18n("Print warning") : i18n("Print error"))));
	kdDebug() << errmsg << msg << endl;
}

void showmsg(const QString& msg, int type = 0)
{
	switch (job_output) {
	   case 0: showmsgdialog(msg,type); break;
	   case 1: showmsgconsole(msg,type); break;
	   default: break;
	}
}

void errormsg(const QString& msg)
{
	showmsg(msg,2);
	exit(1);
}

void signal_handler(int s)
{
	QFile::remove(tempFile);
	exit(s);
}

QString copyfile( const QString& filename )
{
	kdDebug( 500 ) << "Copying file " << filename << endl;
	QString result;
	QFile f( filename );
	if ( f.open( IO_ReadOnly ) )
	{
		KTempFile temp;
		temp.setAutoDelete( false );
		QFile *tf = temp.file();
		if ( tf )
		{
			char buffer[ 0xFFFF ];
			int b = 0;
			while ( ( b = f.readBlock( buffer, 0xFFFF ) ) > 0 )
			{
				if ( tf->writeBlock( buffer, b ) != b )
					break;
			}
			tf->close();
			if ( b > 0 )
				temp.setAutoDelete( true );
			else
			{
				kdDebug( 500 ) << "File copied to " << temp.name() << endl;
				result = temp.name();
			}
		}
		else
			temp.setAutoDelete( true );
		f.close();
	}
	return result;
}

//******************************************************************************************************

PrintWrapper::PrintWrapper()
: QWidget(), force_stdin(false), check_stdin(true)
{
}

void PrintWrapper::slotPrint()
{
	KCmdLineArgs	*args = KCmdLineArgs::parsedArgs();

#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
	struct sigaction action;
#endif /* HAVE_SIGACTION && !HAVE_SIGSET*/

	// read variables from command line
	QString	printer = args->getOption("d");
	QString	title = args->getOption("t");
	int	ncopies = QString(args->getOption("n")).toInt();
	QString	job_mode = args->getOption("j");
	QString	system = args->getOption("system");
	QCStringList	optlist = args->getOptionList("o");
	QMap<QString,QString>	opts;
	KURL::List	files;
	QStringList	filestoprint;
	force_stdin = args->isSet("stdin");
	docopy = args->isSet( "c" );
	bool	nodialog = !(args->isSet("dialog"));
	
	if( isatty( 0 ))
	    {
	    kdDebug( 500 ) << "stdin is a terminal, disabling it" << endl;
	    check_stdin = false;
	    }

	// parse options
	for (QCStringList::ConstIterator it=optlist.begin(); it!=optlist.end(); ++it)
	{
		QStringList	l = QStringList::split('=',QString(*it),false);
		if (l.count() >= 1) opts[l[0]] = (l.count() == 2 ? l[1] : QString::null);
	}

	// read file list
	for (int i=0; i<args->count(); i++)
		files.append(args->url(i));

	// some clean-up
	args->clear();

	// set default values if necessary
	if (job_mode == "console") job_output = 1;
	else if (job_mode == "none") job_output = 2;
	else job_output = 0;

	// some checking
	if ( files.count() > 0)
	{
		check_stdin = false;
		
		if( force_stdin )
		{
			showmsg(i18n("A file has been specified on the command line. Printing from STDIN will be disabled."), 1);
			force_stdin = false;
		}
	}
	if (nodialog && files.count() == 0 &&!force_stdin && !check_stdin )
	{
		errormsg(i18n("When using '--nodialog', you must at least specify one file to print or use the '--stdin' flag."));
	}
	
	if( check_stdin )
	{ // check if there's any input on stdin
	    fd_set in;
	    struct timeval tm;
	    tm.tv_sec = 0;
	    tm.tv_usec = 0;
	    FD_ZERO( &in );
	    FD_SET( 0, &in );
	    if( select( 1, &in, NULL, NULL, &tm ) )
	    { // we have data on stdin
			if ( read( 0, &readchar, 1 ) > 0 )
			{
				force_stdin = true;
				check_stdin = false;
				dataread = true;
				kdDebug( 500 ) << "input detected on stdin" << endl;
			}
			else
			{
				force_stdin = check_stdin = false;
				kdDebug( 500 ) << "stdin closed and empty" << endl;
			}
	    } 
		else
			kdDebug( 500 ) << "no input on stdin at startup" << endl;
	}

        // force_stdin ? or also check_stdin ?
	KPrinter::ApplicationType	dialog_mode = (force_stdin || nodialog ? KPrinter::StandAlone : KPrinter::StandAlonePersistent);
	KPrinter::setApplicationType(dialog_mode);
	if (!force_stdin)
		KPrinter::addStandardPage(KPrinter::FilesPage);

	KPrinter	kprinter;
	if (nodialog)
	{
		KMPrinter	*prt(0);
		KMManager	*mgr = KMManager::self();

		mgr->printerList(false);
		if (!printer.isEmpty())
			prt = mgr->findPrinter(printer);
		else
			prt = mgr->defaultPrinter();

		if (prt == 0)
			errormsg(i18n("The specified printer or the default printer could not be found."));
		else if (!prt->autoConfigure(&kprinter))
			errormsg(i18n("Operation aborted."));
	}
	else if (!printer.isEmpty())
		kprinter.setSearchName(printer);
	kprinter.setDocName(title);
	kprinter.initOptions(opts);
	kprinter.setOption("kde-filelist", files.toStringList().join("@@"));
	kdDebug( 500 ) << kprinter.option( "kde-filelist" ) << endl;
	if (ncopies > 0)
		kprinter.setNumCopies(ncopies);

	if (nodialog)
		slotPrintRequested(&kprinter);
	else
	{
		dlg = KPrintDialog::printerDialog(&kprinter, 0);
		if (dlg)
		{
			connect(dlg, SIGNAL(printRequested(KPrinter*)), SLOT(slotPrintRequested(KPrinter*)));
			if( check_stdin )
			{
			    notif = new QSocketNotifier( 0, QSocketNotifier::Read, this );
			    connect( notif, SIGNAL( activated( int )), this, SLOT( slotGotStdin()));
			    kdDebug( 500 ) << "waiting for input on stdin" << endl;
			}
			dlg->exec();
			delete dlg;
		}
		else
			errormsg(i18n("Unable to construct the print dialog."));
	}

	QTimer::singleShot(10,kapp,SLOT(quit()));
}

void hack( KPrintDialog* dlg );

void PrintWrapper::slotGotStdin()
{
	delete notif;
	if ( read( 0, &readchar, 1 ) > 0 )
	{
		force_stdin = true;
		check_stdin = false;
		dataread = true;
		dlg->enableDialogPage( 0, false );
		kdDebug( 500 ) << "got delayed input on stdin" << endl;
	}
}

void PrintWrapper::slotPrintRequested(KPrinter *kprinter)
{
	// re-initialize docName
	kprinter->setDocName(QString::null);

	// download files if needed
	QStringList	files = QStringList::split("@@", kprinter->option("kde-filelist"), false), filestoprint;
	for (QStringList::ConstIterator it=files.begin(); it!=files.end(); ++it)
	{
		QString	tmpFile;
		KURL	url(*it);
		kdDebug( 500 ) << url.url() << endl;
		if (KIO::NetAccess::download(url, tmpFile))
		{
			filestoprint << tmpFile;
			kprinter->setDocName(url.fileName());
		}
	}

	if (filestoprint.count() > 1)
		kprinter->setDocName(i18n("Multiple files (%1)").arg(filestoprint.count()));
	else if (kprinter->docName().isEmpty())
		kprinter->setDocName(force_stdin ? "<STDIN>" : "KPrinter");
	if (filestoprint.count() == 0)
	{
		// print from stdin

#  if defined(HAVE_SIGSET)
		sigset(SIGHUP, signal_handler);
		sigset(SIGINT, signal_handler);
		sigset(SIGTERM, signal_handler);
#  elif defined(HAVE_SIGACTION)
		memset(&action, 0, sizeof(action));
		action.sa_handler = signal_handler;

		sigaction(SIGHUP, &action, NULL);
		sigaction(SIGINT, &action, NULL);
		sigaction(SIGTERM, &action, NULL);
#  else
		signal(SIGHUP, signal_handler);
		signal(SIGINT, signal_handler);
		signal(SIGTERM, signal_handler);
#  endif

		tempFile = locateLocal("tmp","kprinter_")+QString::number(getpid());
		filestoprint.append(tempFile);
		fromStdin = true;
		FILE	*fout = fopen(QFile::encodeName(filestoprint[0]),"w");
		if (!fout) errormsg(i18n("Unable to open temporary file."));
		char	buffer[8192];
		int	s;

		// check for previously read data
		if ( dataread )
			fwrite( &readchar, 1, 1, fout );
		// read stdin and write to temporary file
		while ((s=fread(buffer,1,sizeof(buffer),stdin)) > 0)
			fwrite(buffer,1,s,fout);

		s = ftell(fout);
		fclose(fout);
		if (s <= 0)
		{
			showmsg(i18n("Stdin is empty, no job sent."), 2);
			QFile::remove(filestoprint[0]);
			return;
		}
	}
	else if ( docopy )
	{
		for ( QStringList::Iterator it=filestoprint.begin(); it!=filestoprint.end(); ++it )
		{
			QString tmp = copyfile( *it );
			if ( tmp.isEmpty() )
			{
				errormsg( i18n( "Unable to copy file %1." ).arg( *it ) );
				return;
			}
			*it = tmp;
		}
		fromStdin = true;
	}
	else
		fromStdin = false;

	// print all files. Remove it after if printing from
	// stdin. "kprinter" shouldn't remove temp file itself,
	// otherwise the temp file might get removed before the
	// print process finishes.
	bool ok = kprinter->printFiles(filestoprint, fromStdin);

	if (!ok)
		showmsg(i18n("Error while printing files"), 2);
	// Do not show this dialog anymore. Code sould be removed definitively
	// if nobody complains.
	/*else
	{
		QString	msg = i18n("<nobr>File(s) sent to printer <b>%1</b>.</nobr>").arg(kprinter->printerName());
		showmsg(msg,0);
	}*/
}

#include "printwrapper.moc"
