/***************************************************************************
          TrackPixmap.h  -  buffered pixmap for displaying a kwave track
                             -------------------
    begin                : Tue Mar 20 2001
    copyright            : (C) 2001 by Thomas Eschenbacher
    email                : Thomas.Eschenbacher@gmx.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 TRACK_PIXMAP_H
#define TRACK_PIXMAP_H

#include "config.h"
#include "libkwavegui_export.h"

#include <math.h>

#include <QtGlobal>
#include <QBitArray>
#include <QColor>
#include <QMutex>
#include <QObject>
#include <QPixmap>
#include <QVector>

#include "libkwave/Sample.h"
#include "libkwave/SampleArray.h"
#include "libkwave/Utils.h"

#include "libgui/Colors.h"

/**
 * The TrackPixmap is a graphical representation of a track's sample
 * data. It is connected directly to a Track object so that it is able
 * to get any needed sample data on it's own. It provides internal
 * caching mechanisms for reducing (slow) accesses to the track; this
 * is especially needed for speeding up the handling large wav files.
 *
 * @note The sample ranges in this class are a kind of "virtual", it is
 *       possible that the length of this pixmap in samples is larger
 *       than the track. However, this should not do any harm and might
 *       even be useful if a track grows.
 *
 * @todo If the "interpolated mode" is used, the sample buffer should
 *       contain some samples before and some samples after the current
 *       view. (m_extra_samples, calculated in set_zoom, !=0 only in
 *       interpolation mode, ignored in all other modes.
 *
 * @todo Check setOffset()
 * @todo optimizations if zoom factor is multiple of last zoom factor
 * @todo optimizations in slotSamplesDeleted and slotSamplesInserted if
 *       parts of the current buffers can be re-used
 *
 * @author Thomas Eschenbacher <Thomas.Eschenbacher@gmx.de>
 */
namespace Kwave
{
    class Track;

    class LIBKWAVEGUI_EXPORT TrackPixmap: public QObject
    {
        Q_OBJECT

    public:

        /** Default constructor */
        explicit TrackPixmap(Kwave::Track &track);

        /** Destructor */
        ~TrackPixmap() override;

        /**
         * Resize the pixmap.
         * @param width new width in pixels
         * @param height new height in pixels
         */
        virtual void resize(int width, int height);

        /**
         * Get the width of the pixmap
         * @return the width of the pixmap in pixels
         */
        virtual int width() const { return m_pixmap.width(); }

        /**
         * Get the height of the pixmap
         * @return the height of the pixmap in pixels
         */
        virtual int height() const { return m_pixmap.height(); }

        /**
         * Get the internal QPixmap object
         * @return reference to m_pixmap
         */
        virtual const QPixmap &pixmap() const { return m_pixmap; }

        /**
         * Repaints the current pixmap. After the repaint the pixmap is no
         * longer in status "modified". If it was not modified before, this
         * is a no-op.
         */
        virtual void repaint();

        /**
         * Sets a new vertical zoom factor.
         * @param zoom new vertical zoom
         */
        virtual void setVerticalZoom(double zoom);

        /**
         * Returns "true" if the buffer has changed and the pixmap has to
         * be re-painted.
         */
        virtual bool isModified();

        /** Sets the internal "modified" flag */
        virtual void setModified() { m_modified = true; }

    signals:

        /** Emitted if the content of the pixmap was modified. */
        void sigModified();

    public slots:

        /**
         * Sets a new sample offset and moves the signal display
         * left or right. Only the new areas that were moved in
         * will be redrawn.
         * @param offset index of the first visible sample
         */
        void setOffset(sample_index_t offset);

        /**
         * Sets a new zoom factor in samples per pixel. This normally
         * affects the number of visible samples and a redraw of
         * the current view.
         */
        void setZoom(double zoom);

    private slots:

        /**
         * Connected to the track's sigSamplesInserted.
         * @param src source track
         * @param offset position from which the data was inserted
         * @param length number of samples inserted
         * @see Track::sigSamplesInserted
         * @internal
         */
        void slotSamplesInserted(Kwave::Track *src, sample_index_t offset,
                                sample_index_t length);

        /**
         * Connected to the track's sigSamplesDeleted.
         * @param src source track
         * @param offset position from which the data was removed
         * @param length number of samples deleted
         * @see Track::sigSamplesDeleted
         * @internal
         */
        void slotSamplesDeleted(Kwave::Track *src, sample_index_t offset,
                                sample_index_t length);

        /**
         * Connected to the track's sigSamplesModified
         * @param src source track
         * @param offset position from which the data was modified
         * @param length number of samples modified
         * @see Track::sigSamplesModified
         * @internal
         */
        void slotSamplesModified(Kwave::Track *src, sample_index_t offset,
                                sample_index_t length);

        /**
         * Sets the state of the pixmap to "modified" whenever the
         * selection of the track has changed
         */
        void selectionChanged();

    private:

        /**
         * Resizes the current buffer and sets all new entries to
         * invalid (if any).
         */
        void resizeBuffer();

        /**
         * Sets the current buffer to "invalid" state. Note: this does
         * not include any resize!
         */
        void invalidateBuffer();

        /**
         * Adapts the current mode and size of the buffers and fills all
         * areas that do not contain valid data with fresh samples. In other
         * words: it makes the buffer "valid" and consistent.
         * @return true if the buffer content has changed, false if no
         *         invalid samples were found
         */
        bool validateBuffer();

        /**
         * Draws the signal as an overview with multiple samples per
         * pixel.
         * @param p reference to a QPainter
         * @param middle the y position of the zero line [pixels]
         * @param height the height of the pixmap [pixels]
         * @param first the offset of the first pixel
         * @param last the offset of the last pixel
         */
        void drawOverview(QPainter &p, int middle, int height,
            int first, int last);

        /**
         * Calculates the parameters for interpolation of the graphical
         * display when zoomed in. Allocates (new) buffer for the
         * filter coefficients of the low pass filter used for interpolation.
         * @see m_interpolation_alpha
         */
        void calculateInterpolation();

        /**
         * Draws the signal and interpolates the pixels between the
         * samples. The interpolation is done by using a simple FIR
         * lowpass filter.
         * @param p reference to a QPainter
         * @param width the width of the pixmap in pixels
         * @param middle the y position of the zero line in the drawing
         *               area [pixels]
         * @param height the height of the drawing are [pixels]
         * @see #calculateInterpolation()
         */
        void drawInterpolatedSignal(QPainter &p, int width, int middle,
            int height);

        /**
         * Draws the signal and connects the pixels between the samples
         * by using a simple poly-line. This gets used if the current zoom
         * factor is not suitable for either an overview nor an interpolated
         * signal display.
         * @param p reference to a QPainter
         * @param width the width of the pixmap in pixels
         * @param middle the y position of the zero line in the drawing
         *               area [pixels]
         * @param height the height of the drawing are [pixels]
         */
        void drawPolyLineSignal(QPainter &p, int width, int middle, int height);

        /**
         * Converts a pixel offset into a sample offset.
         */
        inline sample_index_t pixels2samples(int pixels) {
            return static_cast<sample_index_t>(rint(
                static_cast<double>(pixels) * m_zoom));
        }

        /**
         * Converts a sample offset into a pixel offset.
         */
        inline int samples2pixels(sample_index_t samples) {
            if (m_zoom <= 0) return 0;
            return Kwave::toInt(rint(static_cast<double>(samples) / m_zoom));
        }

        /**
         * Converts the offset and length of an overlapping region into buffer
         * indices, depending on the current mode. If the given region does
         * not overlap at all, the length of the area will be set to zero.
         * The length will be truncated to the end of the current buffer(s).
         * @note If the resulting or given length is zero, the offset value
         *       is not necessarily valid and should be ignored!
         * @param offset reference to the source sample index, will be converted
         *               into buffer index
         * @param length reference to the length in samples, will be converted
         *               to the number of buffer indices
         */
        void convertOverlap(sample_index_t &offset, sample_index_t &length);

    private:

        /** The QPixmap with the bitmap data */
        QPixmap m_pixmap;

        /**
         * Reference to the track with our sample data.
         */
        Kwave::Track &m_track;

        /**
         * Index of the first sample. Needed for converting pixel
         * positions into absolute sample numbers. This is always
         * in units of samples, independent of the current mode!
         */
        sample_index_t m_offset;

        /**
         * Zoom factor in samples/pixel. Needed for converting
         * sample indices into pixels and vice-versa.
         */
        double m_zoom;

        /**
         * vertical zoom factor. Default is 1.0
         */
        double m_vertical_zoom;

        /**
         * If true, we are in min/max mode. This means that m_sample_buffer
         * is not used and m_minmax_buffer contains is used instead.
         */
        bool m_minmax_mode;

        /**
         * Array with samples needed for drawing when not in min/max mode.
         * This might sometimes include one sample before or after the
         * current view.
         */
        Kwave::SampleArray m_sample_buffer;

        /**
         * Array with minimum sample values, if in min/max mode.
         */
        Kwave::SampleArray m_min_buffer;

        /**
         * Array with maximum sample values, if in min/max mode.
         */
        Kwave::SampleArray m_max_buffer;

        /** Indicates that the buffer content was modified */
        bool m_modified;

        /**
         * Array with one bit for each position in the internal
         * buffers. If the bit corresponding to a certain buffer
         * index is set to "1", the position contains valid data,
         * if "0" the content of the buffer is invalid.
         */
        QBitArray m_valid;

        /** Mutex for exclusive access to the buffers. */
        QMutex m_lock_buffer;

        /**
         * order of the low pass filter used for interpolation
         */
        int m_interpolation_order;

        /**
         * Buffer for filter coefficients of the low pass used for
         * interpolation.
         * @see #calculateInterpolation()
         */
        QVector<double> m_interpolation_alpha;

        /** set of colors for drawing */
        Kwave::Colors::ColorSet m_colors;

    };
}

#endif /* TRACK_PIXMAP_H */

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