/***************************************************************************
                          recording-monitor-widget.cpp  -  description
                             -------------------
    begin                : So Sep 7 2003
    copyright            : (C) 2003 by Martin Witte
    email                : witte@kawo1.rwth-aachen.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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "recording-datamonitor.h"
//#include "recording-context.h"
#include <math.h>

#include <tqpainter.h>
#include <tqimage.h>
#include <tqpixmap.h>
#include <kimageeffect.h>  // fading, blending, ...
#include <kpixmapio.h>     // fast conversion between TQPixmap/TQImage
#include <limits.h>
#include <stdlib.h>

#include <tdelocale.h>

#define CHANNEL_H_MIN   20
#define BLOCK_W_MIN     10
#define W_MIN           (20 * (BLOCK_W_MIN))

RecordingDataMonitor::RecordingDataMonitor(TQWidget *parent, const char *name)
    : TQFrame(parent, name),
      m_channelsMax(NULL),
      m_channelsAvg(NULL),
      m_maxValue(INT_MAX),
      m_channels(0),
      m_pActiveBlocks(NULL)
{
    setFrameStyle(Box | Sunken);
    setLineWidth(1);
    setMidLineWidth(1);

    setChannels(2);

    setColors(TQColor(20, 244, 20),
              TQColor(10, 117, 10));

    setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding));
}


RecordingDataMonitor::~RecordingDataMonitor()
{
    if (m_channelsMax)   delete[] m_channelsMax;
    if (m_channelsAvg)   delete[] m_channelsAvg;
    if (m_pActiveBlocks) delete[] m_pActiveBlocks;
}



// own stuff

void RecordingDataMonitor::setChannels(int n)
{
    if (n != m_channels) {
        if (m_channelsMax)   delete[] m_channelsMax;
        if (m_channelsAvg)   delete[] m_channelsAvg;
        if (m_pActiveBlocks) delete[] m_pActiveBlocks;
        m_channels = n > 0 ? n : 0;
        if (m_channels > 0) {
            m_channelsMax   = new int[m_channels];
            m_channelsAvg   = new double[m_channels];
            m_pActiveBlocks = new int[m_channels];
            for (int i = 0; i < m_channels; ++i) {
                m_pActiveBlocks[i] = 0;
            }
        } else {
            m_channelsMax   = NULL;
            m_channelsAvg   = NULL;
            m_pActiveBlocks = NULL;
        }
    }

    for (int i = 0; i < m_channels; ++i) {
        m_channelsMax[i]   = 0;
        m_channelsAvg[i]   = 0;
    }
    setMinimumSize(TQSize(W_MIN, (m_channels + 1 )* CHANNEL_H_MIN));
}


// TQt/TDE ...

void RecordingDataMonitor::drawContents(TQPainter *painter)
{
    if (painter)
        internalDrawContents(*painter, true);
}

void RecordingDataMonitor::internalDrawContents(TQPainter &painter, bool repaintAll)
{
    if (m_channels <= 0) return;
    TQRect r = contentsRect();

    TQPen   activePen      (colorGroup().color(TQColorGroup::Text), 1);
    TQPen   inactivePen    (colorGroup().color(TQColorGroup::Mid),  1);
    TQBrush activeBrush   = colorGroup().brush(TQColorGroup::Text);
    TQBrush inactiveBrush = colorGroup().brush(TQColorGroup::Mid);
    TQBrush yellowBrush(TQColor(255,255,0));
    TQBrush orangeBrush(TQColor(255,192,0));
    TQBrush redBrush   (TQColor(255,0, 0));


    double  ranges [5] = { 0.75, 0.83, 0.91, 1.0, 999 };
    TQBrush *brushes[5] = { &activeBrush, &yellowBrush, &orangeBrush, &redBrush, &redBrush };

    painter.setBrush( isEnabled() ? activeBrush : inactiveBrush);

    int nBlocks  = (r.width()-1)  / BLOCK_W_MIN;
    int xoffs    = (r.width()-1)  % BLOCK_W_MIN;
    int chHeight = (r.height()-1-CHANNEL_H_MIN) / m_channels;
    int yoffs    = (r.height()-1) % m_channels;

    double min_dB = 20*log10(1 / (double)m_maxValue );

    int x0 = xoffs/2 + r.top();
    int y = yoffs/2 + r.left();
    for (int c = 0; c < m_channels; ++c) {
        int x = x0;


        int startBlock      = 0;
        int endBlock        = nBlocks - 1;
        int oldActiveBlocks = m_pActiveBlocks[c];

        double dBMax = isEnabled() ? 20*log10(m_channelsMax[c] / (double)m_maxValue ) : min_dB;

        m_pActiveBlocks[c] = m_channelsMax[c] ? (int)rint(nBlocks * (min_dB - dBMax) / min_dB) : 0;

        if (!repaintAll) {
            if (oldActiveBlocks > m_pActiveBlocks[c]) {
                startBlock = m_pActiveBlocks[c];
                endBlock   = oldActiveBlocks - 1;
            } else {
                startBlock = oldActiveBlocks;
                endBlock   = m_pActiveBlocks[c]-1;
            }
        }

        int range = 0;

        x += BLOCK_W_MIN * startBlock;
        for (int b = startBlock; b <= endBlock; ++b) {
            while (b >= nBlocks * ranges[range]) ++range;
            painter.fillRect(x+1, y+1, BLOCK_W_MIN-1, chHeight-1,
                             b < m_pActiveBlocks[c] ? *brushes[range] : inactiveBrush);
            x += BLOCK_W_MIN;
        }

        y += chHeight;
    }

    if (repaintAll) {
        TQFont f("Helvetica");
        painter.setPen (activePen);
        f.setPixelSize(CHANNEL_H_MIN);
        painter.setFont(f);

        int maxW = TQFontMetrics(f).width(i18n("%1 dB").arg((int)min_dB));
        int delta_dB  = 5;
        while (abs((long)min_dB) / delta_dB * maxW * 2 > r.width()) delta_dB *= 2;

        for (int dB = 0; dB >= min_dB; dB -= delta_dB) {
            TQString txt = i18n("%1 dB").arg(dB);
            int w = TQFontMetrics(f).width(txt);
            int x = x0 + (int)(nBlocks * BLOCK_W_MIN * (min_dB - dB) / min_dB) - w;
            if (x < x0) continue;
            painter.drawText(x, y + CHANNEL_H_MIN, txt);
        }
    }
}


bool RecordingDataMonitor::setColors(const TQColor &activeText,
                                     const TQColor &button)
{
    m_colorActiveText = activeText;
    m_colorButton     = button;

    TQPalette pl    = palette();
    TQColorGroup cg = pl.inactive();

    TQBrush fg  = cg.brush(TQColorGroup::Foreground),
           btn = cg.brush(TQColorGroup::Button),
           lgt = cg.brush(TQColorGroup::Light),
           drk = cg.brush(TQColorGroup::Dark),
           mid = cg.brush(TQColorGroup::Mid),
           txt = cg.brush(TQColorGroup::Text),
           btx = cg.brush(TQColorGroup::BrightText),
           bas = cg.brush(TQColorGroup::Base),
           bg  = cg.brush(TQColorGroup::Background);

    fg.setColor (m_colorActiveText);
    btn.setColor(m_colorButton);
    lgt.setColor(m_colorButton.light(180));
    drk.setColor(m_colorButton.light( 50));
    mid.setColor(m_colorButton.light( 75));
    txt.setColor(m_colorActiveText);
    btx.setColor(m_colorActiveText);
    bas.setColor(m_colorButton);
    bg.setColor (m_colorButton);

    TQColorGroup ncg(fg, btn, lgt, drk, mid, txt, btx, bas, bg);
    pl.setInactive(ncg);
    pl.setActive(ncg);
    setPalette(pl);

    if (parentWidget() && parentWidget()->backgroundPixmap() ){
        KPixmapIO io;
        TQImage  i = io.convertToImage(*parentWidget()->backgroundPixmap());
        KImageEffect::fade(i, 0.5, colorGroup().color(TQColorGroup::Dark));
        setPaletteBackgroundPixmap(io.convertToPixmap(i));
        setBackgroundOrigin(WindowOrigin);
    } else {
        setBackgroundColor(colorGroup().color(TQColorGroup::Button));
    }

    return true;
}


bool RecordingDataMonitor::noticeSoundStreamData(SoundStreamID /*id*/,
    const SoundFormat &sf, const char *data, size_t size, size_t &/*consumed_size*/,
    const SoundMetaData &/*md*/
)
{
    if (!isEnabled())
        return false;
    int nSamples = size / sf.frameSize();
    int sample_size = sf.sampleSize();

    int bias = 0;
    setChannels(sf.m_Channels);
    int old_max = m_maxValue;
    m_maxValue = sf.maxValue();
    if (!sf.m_IsSigned) {
        m_maxValue /= 2;
        bias = -m_maxValue;
    }

    int c = 0;
    for (int s = 0; s < nSamples; ++s, ++c, data += sample_size) {
        if (c >= m_channels) c -= m_channels;   // avoid slow c = s % m_channels

        int &m = m_channelsMax[c];
        int x = abs(sf.convertSampleToInt(data, false) + bias);
        if (m < x) m = x;
        m_channelsAvg[c] += x;
    }
    for (int i = 0; i < m_channels; ++i)
        m_channelsAvg[i] /= nSamples;

    TQPainter paint(this);
    if (m_maxValue != old_max) {
        repaint(true);
    } else {
        internalDrawContents(paint, false);
    }
    return true;
}


#include "recording-datamonitor.moc"
