/***************************************************************************
                          sq_glwidget.h  -  description
                             -------------------
    begin                : Mon Mar 15 2004
    copyright            : (C) 2004 by Baryshev Dmitry
    email                : ksquirrel.iv@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/


#ifndef SQ_GLWIDGET_H
#define SQ_GLWIDGET_H

#include <tqimage.h>
#include <tqrect.h>
#include <tqpair.h>
#include <tqdatetime.h>

#include <kurl.h>

#include <vector>

#include "sq_glparts.h"

#ifdef KSQUIRREL_PART
#include "sq_glview.h"
#endif

class SQ_GLSelectionPainter;
struct SQ_ImageFilterOptions;
struct SQ_ImageBCGOptions;
struct RGBA;

// parameters in GL matrix
#define    MATRIX_C1    tab->matrix[0]
#define    MATRIX_S1    tab->matrix[1]
#define    MATRIX_X     tab->matrix[3]
#define    MATRIX_S2    tab->matrix[4]
#define    MATRIX_C2    tab->matrix[5]
#define    MATRIX_Y     tab->matrix[7]
#define    MATRIX_Z     tab->matrix[11]

class TDEAction;
class TDEActionCollection;
class TDEToggleAction;
class TDEPopupMenu;
class TDERadioAction;
class KTempFile;

namespace TDEIO { class Job; }

class TQTimer;
class TQPopupMenu;
class TQSlider;
class TQLabel;

class SQ_ToolButtonPopup;
class SQ_ToolButton;
class SQ_ToolBar;

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

/*
 *  SQ_GLWidget represents a widget, which loads, shows and manipulates
 *  an image.
 *
 *  It contains complex decoding method. Since OpenGL can show textures
 *  only with dimensions of power of 2 (32x62, 512, 256 etc.), we should divide
 *  decoded image into quads and only then generate textures.
 *  It means, that the image you see consists of several parts:
 *
 *   |----------------------------------
 *   |                |                |
 *   |    tex N1      |     tex N2     |
 *   |                |                |
 *   -----------------------------------    <= Entire image
 *   |                |                |
 *   |    tex N3      |     tex N4     |
 *   |                |                |
 *   -----------------------------------
 *
 *
 *  The main class member is 'parts'. It's an array of decoded images
 *  and appropriate memory buffers and textures. Here comes an explanation:
 *  [ use monotype fonts :) ]
 *
 *                                                           textures & coordinates           Part
 *     +-----------------------------------+                 #####################     ##################
 *     |                          parts[2] |                 #         #         #===> #                #
 *     | +-------------------------------------+             #         #         #     # texture id     #
 *     | |                            parts[1] |    |======> #####################     # texture coords #
 *     | | +-------------------------------------+  |        #         #         #     # ...            #
 *     | | |    m_parts                 parts[0] |  |        #         #         #     ##################
 *     | | |    #####                            |  |        #####################
 *     | | |    #   #                            |  |
 *     | | |    #   #===============================|
 *     | | |    #   #                            |
 *     | | |    #####                            |
 *     | | |                                     |              memory buffers
 *     | | |     m32                             |       ###############################
 *     | | |    #####                            |       #RGBA.#.....#.....#.....#.....#
 *     | | |    #   #                            |       #RGBA.#.....#.....#.....#.....#
 *     | | |    #   #==================================> ###############################
 *     +-| |    #   #                            |       #RGBA.#.....#.....#.....#.....#
 *       +-|    #####                            |       #RGBA.#.....#.....#.....#.....#
 *         +-------------------------------------+       ###############################
 *                       \      / 
 *                         \  /
 *                        parts
 *
 *
 ******************************************************************************
 *
 */

class SQ_GLWidget : public TQGLWidget
{
    Q_OBJECT
  

    public:
        SQ_GLWidget(TQWidget *parent = 0, const char *name = 0);
        ~SQ_GLWidget();

        void setDownloadPercents(int);

        void removeNonCurrentTabs(int);

        void setExpectedURL(const KURL &u);

        TQString originalURL() const;

        /*
         *  Start decoding given image. We can call it from anywhere.
         */
        void startDecoding(const TQString &file);
        void startDecoding(const KURL &url);

        void zoom(GLfloat);

        /*
         *  Set zoom, move and rotate factors from config.
         */
        void updateFactors();

        /*
         *  Set clear color for context.
         */
        void setClearColor();

        void setOriginalURL(const KURL &);

        /*
         *  Get zoom value, e.g. 1.5, 2.2 ...
         */
        GLfloat getZoom() const;

        /*
         *  Get zoom value in percents, e.g. 150, 220 ...
         */
        GLfloat getZoomPercents() const;

        TDEActionCollection* actionCollection() const;

        /*
         *  Are we in fullscreen state ?
         */
        bool fullscreen() const;

        /*
         * Toggle fullscreen state.
         */
        void toggleFullScreen();

        /*
         *  Direct call to updateGL().
         */
        void updateGLA() { updateGL(); }

        /*
         *  Type of zoom: fit width, fit height...
         */
        int zoomType();

        /*
         *  Filter is GL_LINEAR ?
         */
        bool isnice();

        /*
         *  Direct call to glInit();
         */
        void glInitA() { TQGLWidget::glInit(); }

        /*
         *  Start animation, if loaded image is animated.
         */
        void startAnimation();

        /*
         *  Stop animation, if loaded image is animated.
         */
        void stopAnimation();

        /*
         *  Is animation stopped by user ?
         */
        bool manualBlocked();

        /*
         *  Change statusbar info according with
         *  current matrix (it shows current zoom & angle values).
         */
        void matrixChanged();

        /*
         *  Check or uncheck 'Slideshow' button in toolbar.
         */
        void updateSlideShowButton(bool toggled);

        void closeAllTabsFull();

        static SQ_GLWidget* window() { return m_instance; }

    protected:

        /*
         *  Next three methods should be reimplemented in
         *  every TQGLWidget's subclass.
         */
        void initializeGL();
        void paintGL();
        void resizeGL(int,int);

        /*
         *  Mouse wheel event. Let's load next/previous image, or
         *  zoom in/zoom out (depends on settings).
         */
        void wheelEvent(TQWheelEvent *);

        /*
         *  Palette changed. Let's update tickmarks and background color.
         */
        void paletteChange(const TQPalette &oldPalette);

        /*
         *  Accept drag-and-drop events. We can drop some images
         *  on this widget, SQ_GLWidget will try to decode first file.
         *
         *  TODO: find first supported image and decode it ?
         */
        void dragEnterEvent(TQDragEnterEvent *);
        void dropEvent(TQDropEvent *);

        /*
         *  Mouse events.
         */
        void mousePressEvent(TQMouseEvent *);
        void mouseReleaseEvent(TQMouseEvent *);
        void mouseMoveEvent(TQMouseEvent *);

    private:
        void copyURL();

        TQImage generatePreview();

        bool calcSelection();

        void changeSlider(GLfloat z = -1.0);

        void hackMatrix();

        void enableActions(bool U);

        void initAccelsAndMenu();

        void crop();
        void bcg();
        void filter();
        void editUpdate();

        /*
         *  Save current image page to clipboard
         */
        void toClipboard();

        /*
         *  Save current image page to file
         */
        void saveAs();

        void enableSettingsButton(bool enab);

        /*
         *  Remove currently loaded textures and memory buffers.
         */
        void removeCurrentParts();
        void removeCurrentTabs();

        void closeAllTabs();

        /*
         *  Since 0.6.0-final KSquirrel doesn't show error messages,
         *  if the image is broken or not supported. It uses "broken" image
         *  instead. This method does all needed init.
         */
        void initBrokenImage();

        /*
         *  Force using broken image + update context.
         *  Show appropriate error message in statusbar.
         */
        void useBrokenImage(const int err_index);

        /*
         *  Update filter. If 'nice' is true, use GL_LINEAR
         *  and GL_NEAREST otherwise.
         */
        void updateFilter(bool nice);

        /*
         *  Cleanup method.
         */
        void decodeFailedOn0(const int err_code);

        /*
         *  Create TDEActions.
         */
        void createActions();

        /*
         *  Create toolbars.
         */
        void createToolbar();

        /*
         *  Fill a w x h region with texture. Generate texture if needed.
         */
        void draw_background(void *bits, unsigned int *tex, int dim, GLfloat w, GLfloat h, bool &bind, bool deleteOld);

        void setupBits(Parts *p, RGBA *buffer, int y, int x);

        /*
         *  Jump to first or last image in animated sequence.
         */
        void jumpToImage(bool);

        /*
         *  Next image in animated sequence. Called by user (with F3).
         */
        void nextImage();

        /*
         *  Previous image in animated sequence. Called by user (with F2).
         */
        void prevImage();

        /*
         *  Draw (or not) image's background.
         */
        void toggleDrawingBackground();

        /*
         *  Show popup menu with external tools.
         */
        void showExternalTools();

        /*
         *  Generate textures for tickmarks and bind them.
         */
        void initMarks();

        /*
         *  Load and check tickmarks from disk.
         */
        void createMarks();

        /*
         *  Wrapper for 'delete' key. Called from keyPressEvent().
         */
        void deleteWrapper();

        /*
         *  Show current image's width, height and bpp in statusbar.
         */
        void updateCurrentFileInfo();

        /*
         *  Show/hide tickmarks.
         */
        void toogleTickmarks();

        /*
         *  Show current page number in multipaged images.
         * 
         *  For example: "3/11" means that current page is the third in current image,
         *  which has 11 pages.
         */
        void frameChanged();
        void calcFrameLabelWidth();

        /*
         *  Set current zoom to 'z'.
         */
        void internalZoom(const GLfloat &z);

        /*
         *  Find best tile's width and height for given width and height.
         */
        static void findCloserTiles(int w, int h, std::vector<int> &x, std::vector<int> &y);
        static TQPair<int, int> calcRealDimensions(Parts &, int y = -1, int x = -1);

        /*
         *  Prepare decoding. It will find proper library for decoding,
         *  clear old memory buffers, etc.
         */
        bool prepare();

        /*
         *  Zoom to 'r'. Will be called after somebody used mouse button
         *  to select zoom region. Return true, if zoomed.
         */
        bool zoomRect(const TQRect &r);

        /*
         *  Bind textures, draw them and create GL lists.
         *  If 'swap' it true, swap buffers.
         */
        bool showFrames(int y, Parts *, bool swap);

        /*
         *  OpenGL-related methods, not interesting :-)
         *
         *  Move, zoom, reset, flip and rotate current matrix.
         */
        void matrix_move(GLfloat x, GLfloat y);
        void matrix_move_z(GLfloat z);
        bool matrix_zoom(GLfloat ratio);
        void matrix_reset(bool = true);
        void matrix_pure_reset();
        void matrix_push();
        void matrix_pop();
        void write_gl_matrix();
        void matrix_rotate(GLfloat angle, bool = true);
        void matrix_rotate2(GLfloat angle);
        void flip(int, bool = true);
        void flip_h();
        void flip_v();
        void exifRotate(bool);

    signals:
        void tabCountChanged();
        void message(const TQString &);

    public slots:
        void slotPrint();

        void slotSelectionEllipse();
        void slotSelectionRect();
        void slotSelectionClear();

    private slots:
        void decode();

        void slotAccelActivated();

        void slotChangeTab(int);
        void slotCloseRequest(int);

        void slotCopyJobResult(TDEIO::Job *job);

        /*
         *  Slots for toolbar's actions:
         *  fit width, fit height, zoom+, zoom-, rotate, flip,
         *  first file, last file, reset...
         */
        void slotShowNav();
        void slotSetZoomPercents(int);
        void slotZoomW();
        void slotZoomH();
        void slotZoomWH();
        void slotZoomPlus();
        void slotZoom100();
        void slotZoomLast();
        void slotZoomMinus();
        void slotZoomIfLess();
        void slotRotateLeft();
        void slotRotateRight();
        void slotFlipV();
        void slotFlipH();
        void slotMatrixReset();
        void slotProperties(); // show image properties
        void slotFirst();
        void slotLast();
        void slotNext();
        void slotPrev();
        void slotZoomMenu();
        void slotAnimateNext();
        void slotToggleAnimate(); // start/stop animation
        void slotSetCurrentImage(int);
        void slotShowImages();
        void slotImagesHidden();
        void slotImagesShown();
        void slotShowHelp();
        void slotShowCodecSettings();
        void slotApplyCodecSettings();

        void slotBCG(SQ_ImageBCGOptions *);
        void slotFilter(SQ_ImageFilterOptions *fltopt);

        void slotCopyResult(TDEIO::Job *);

    private:
        TDEAction               *pASelectionClear;
        TDEToggleAction         *pAFull, *pAIfLess, *pAZoomW,
                              *pAZoomH, *pAZoomWH, *pAZoom100,
                              *pAZoomLast,
                              *pASelectionEllipse, *pASelectionRect;

        SQ_ToolButton         *pAToolQuick, *pAToolFull, *pAToolProp, *pAToolPrint;
        SQ_ToolButtonPopup    *pAToolZoom,  *pAToolImages;

        TDEActionCollection     *ac, *acMain;
        TQPopupMenu            *menu, *menuFile, *menuImage;
        int                             id_saveas, id_settings,
                                        id_f5, id_f6, id_f7, id_f8, id_del,
                                        id_prop;

        // popup menu with zoom types (fit width, fit height, zoom 100%...)
        TDEPopupMenu            *zoomMenu, *selectionMenu,

                                  // popup menu with image pages
                                  *images;

        TQImage                 BGpixmap, BGquads;

        TQTimer                 *timer_prev, *timer_next;
        TQTimer                 *timer_anim;

        TQImage                 mm[4];

        fmt_image              image_broken;
        SQ_GLSelectionPainter  *gls;

        Parts                  *parts_broken;

        GLfloat                saved[12], zoomfactor, movefactor, rotatefactor;

        unsigned int           texQuads, texPixmap, mark[4];
        int                    xmoveold, ymoveold, xmove, ymove,
                               zoom_type, old_id, total, errors, movetype;
        bool                   reset_mode, decoded, blocked, 
                               changed, marks, linear;
        float                  zoomFactor, oldZoom;
        RGBA                   *buffer;
        TQSlider                *slider_zoom;

        KTempFile              *tmp;
        KURL                   lastCopy, m_expected, m_original;
        TQTime                  clickTime, started;

        std::vector<Tab>        tabs;
        Tab                     *tab, *tabold;
        Tab                     tmptab, taborig;
        bool                    hackResizeGL, bindChecker;

        TQLabel                  *percentsLabel;

#ifdef KSQUIRREL_PART
        SQ_GLView         t_glv;
#endif

        static SQ_GLWidget     *m_instance;
};

inline
int SQ_GLWidget::zoomType()
{
    return zoom_type;
}

inline
bool SQ_GLWidget::isnice()
{
    return linear;
}

inline
TDEActionCollection* SQ_GLWidget::actionCollection() const
{
    return ac;
}

inline
void SQ_GLWidget::setOriginalURL(const KURL &u)
{
    m_original = u;
}

inline
TQString SQ_GLWidget::originalURL() const
{
    return tab->m_original.isLocalFile() ? tab->m_original.path() : tab->m_original.prettyURL();
}

inline
void SQ_GLWidget::setExpectedURL(const KURL &u)
{
    m_expected = u;
}

#endif
