/** 
 * Copyright (C) 2000-2002 the KGhostView authors. See file AUTHORS.
 * 	
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef KPSWIDGET_H
#define KPSWIDGET_H

#include <stdio.h>

#include <qptrqueue.h>
#include <qsize.h>
#include <qstring.h>
#include <qwidget.h>

#include "dscparse_adapter.h"
#undef min

#include <X11/X.h>

class KProcess;

class KGVConfigDialog;
class MessagesDialog;

/*
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifndef None
#define None -1
#endif

#ifndef XlibSpecificationRelease
typedef char *XPointer;
#endif

#ifndef SEEK_SET
#define SEEK_SET 0
#endif

#ifndef XlibSpecificationRelease
typedef char *XPointer;
#endif

#include <errno.h>
// BSD 4.3 errno.h does not declare errno
extern int errno;
// Both error returns are checked for non-blocking I/O.
// Manufacture the other error code if only one exists.
#if !defined(EWOULDBLOCK) && defined(EAGAIN)
#define EWOULDBLOCK EAGAIN
#endif
#if !defined(EAGAIN) && defined(EWOULDBLOCK)
#define EAGAIN EWOULDBLOCK
#endif
*/

class KPSWidget : public QWidget
{
    Q_OBJECT

public:
    enum Palette { COLOR, GRAYSCALE, MONOCHROME };
	
    KPSWidget( QWidget* parent = 0, const char* name = 0 );
    ~KPSWidget();

    /**
     * @return the horizontal resolution of the screen in pixels/inch.
     */
    int logicalDpiX() const;
    /**
     * @return the vertical resolution of the screen in pixels/inch.
     */
    int logicalDpiY() const;

    /**
     * Allow the interpreter to be started, and try to start it.
     */
    void enableInterpreter();

    /**
     * Stop any running interpreter and don't allow a new one to start.
     */
    void disableInterpreter();
    
    /**
     * Returns true if the interpreter is ready for new input.
     */
    bool isInterpreterReady() const;
    
    bool isInterpreterBusy() const;

    /**
     * Returns true if the interpreter is running.
     */
    bool isInterpreterRunning() const;
    
    /**
     * Tell ghostscript to start the next page.
     * Returns false if ghostscript is not running, or not ready to start
     * another page. If another page is started, sets the _interpreterReady 
     * flag and cursor.
     */
    bool nextPage();

    /**
     * Queue a portion of a PostScript file for output to ghostscript and 
     * start processing the queue.
     *
     * fp: FILE* of the file in question. NOTE: if you have several 
     *     KPSWidget's reading from the same file, you must open a unique 
     *     FILE* for each widget.
     * begin: position in file to start.
     * len: number of bytes to write.
     *
     * If an interpreter is not running, nothing is queued and false is 
     * returned.
     */
    bool sendPS( FILE*, unsigned int begin, unsigned int end );
   
    /**
     * Sets the filename of the ghostscript input. 
     * @p usePipe indicates whether we use a pipe for
     * communication or let ghoscript read the file itself.
     */
    void setFileName( const QString&, bool usePipe );

    /**
     * Set the bounding box of the drawable. See my comment in the source
     * file.
     */
    void setBoundingBox( KDSCBBOX );

    /**
     * Set the orientation of the page.
     */
    void setOrientation( CDSC_ORIENTATION_ENUM );
    
    /**
     * Set the resolution in pixels/inch with which ghostscript should
     * render the page. 
     */
    void setResolution( int dpiX, int dpiY );
 
    /**
     * Convenience method. It sets the resolution according to the physical
     * resolution of the screen and the magnification value.
     */
    void setMagnification( double magnification );
    
    /**
     * @return the boundingbox of the drawable.
     */
    KDSCBBOX boundingBox() const;

    /**
     * @return the current orientation.
     */
    CDSC_ORIENTATION_ENUM orientation() const;
   
    void layout();
    
    bool configure();
   
    /**
     * Helper function. Returns an angle for the given orientation.
     */
    static int orientation2angle( CDSC_ORIENTATION_ENUM );

    /**
     * Helper function. Returns a string representation of the given
     * palette.
     */
    static QCString palette2String( Palette );

signals:
    void pageFinished();
	
protected:    
    struct Record 
    {
	FILE* fp;
	long begin;
	unsigned int len;
    };
    
    // void resizeEvent( QResizeEvent* );
    bool x11Event( XEvent* );

    /**
     * Setup the widget according to the current settings for the 
     * boundingBox, the resolution and the orientation. This involves
     * the following things:
     * - Resize the widget
     * - Resize and clear the background pixmap.
     * - Setup the GHOSTVIEW and GHOSTVIEW_COLORS properties.
     *
     * Make sure ghostscript isn't running when calling this method.
     */
    void setupWidget();
    
    void setGhostscriptPath( const QString& );
    void setGhostscriptArguments( const QStringList& );
    void setPalette( Palette );
    
    void startInterpreter();
    void stopInterpreter();

    void readSettings();

protected slots:
    void gs_input();
    void gs_output( KProcess*, char* buffer, int len );
    void interpreterFailed();
    void slotProcessExited( KProcess* );
	
private:
    Window _gsWindow;   // Destination of ghostscript messages.

    enum AtomName { GHOSTVIEW = 0, GHOSTVIEW_COLORS, NEXT, PAGE, DONE };
    Atom _atoms[5];

    QPixmap _backgroundPixmap;

    /**
     * The following properties determine how Ghostscript is started.
     * If any of these is changed, Ghostscript needs to be restarted.
     */
    QString     _ghostscriptPath;
    QStringList _ghostscriptArguments;
    QString     _fileName;
    bool        _usePipe;

    /**
     * Flag set when one of the properties _ghostscriptPath, 
     * _ghostscriptArguments or _fileName has been changed.
     */
    bool _ghostscriptDirty;
 
    /**
     * The following properties determine how Ghostscript renders its
     * pages. If any of these is changed, the widget needs to be setup, 
     * and Ghostscript needs to be restarted.
     */
    CDSC_ORIENTATION_ENUM _orientation;
    KDSCBBOX              _boundingBox;
    float                 _dpiX, _dpiY;
    Palette               _palette;
    
    /**
     * Flag set when one of the properties _orientation, _boundingBox,
     * _dpi[X|Y] or _palette has been changed.
     */
    bool _widgetDirty;

    bool _showMessages;

    int	_logicalDpiX;
    int _logicalDpiY;
    
    MessagesDialog*	messages;
    KGVConfigDialog*	intConfig;

    KProcess* _process;
 
    QPtrQueue<Record> _inputQueue;
    Record*           _currentRecord;
    unsigned int      _bytesLeft; // Bytes left in _currentRecord to be read.
    char*             _inputBuffer;
    

    bool _stdinReady;
    bool _interpreterBusy;
    bool _interpreterReady;
    bool _interpreterDisabled; // Disables starting the interpreter.
};

inline int KPSWidget::logicalDpiX() const
{
    return _logicalDpiX;
}

inline int KPSWidget::logicalDpiY() const
{
    return _logicalDpiY;
}

inline KDSCBBOX KPSWidget::boundingBox() const
{
    return _boundingBox;
}

inline CDSC_ORIENTATION_ENUM KPSWidget::orientation() const
{
    return _orientation;
}

#endif // KPSWIDGET_H

