/***************************************************************************
 *   Copyright (C) 2004 by Predrag Viceic                                  *
 *   viceic@net2000.ch                                             *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "spectrumview.h"

SpectrumView::SpectrumView(QWidget *parent, const char *name)
 : QFrame(parent, name,WNoAutoErase)
{
    setFrameStyle(LineEditPanel | Sunken);
    frames=2048;
    position=0;
    lastRenderedPosition=0;
    subband_number=64;
    steps=32;
    in=new float[frames];
    //float* in2=new float[frames];
    out=(fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*frames);
    //float* out2=new float[frames];
    p = fftwf_plan_dft_r2c_1d(frames, in, out, FFTW_ESTIMATE);
    subband_energies=0;
    setPaletteBackgroundColor(QColor(0,0,0));
    playing=FALSE;
}


SpectrumView::~SpectrumView()
{
    fftwf_destroy_plan(p);
    zaparr(in);
    //zaparr(in2);
    zaparr(out);
    //zaparr(out2);
    zaparr(subband_energies);
}

void SpectrumView::showEvent(QShowEvent* /*ev*/){
    if(contentsRect().size().isValid()){
        //subband_number=round(contentsRect().width()/10);
        //steps=round(contentsRect().height()/3);
    }
}

void SpectrumView::paintEvent(QPaintEvent* /*ev*/){
    static QPixmap pixmap;
    bool resized=false;  //force repaint on resize even if between frames.
    if(pixmap.size()!=contentsRect().size() && contentsRect().size().isValid()){
        pixmap.resize(contentsRect().size());
        resized=true; //force repaint on resize even if between frames.
    }

    if((!playing || ((position>=lastRenderedPosition+frames)&&
        position+frames<soundManager->getFrames()) || resized) &&
         subband_number>0 && steps>0 && contentsRect().size().isValid()){
        int x=contentsRect().x();
        int y=contentsRect().y();
        if(playing){
            float* lchannel=&soundManager->getLeftChannel()[position];
            //float* rchannel=&soundManager->getRightChannel()[position];



            //fill in and out buffers
            for (int i=0;i<frames;i++){
                float w = (0.5 + 0.5*cos((2*M_PI * i) / frames)); //Hanning
                in[i]=lchannel[i]*w;
                //in2[i]=rchannel[i]*w;
            }
            //end fill in and out buffers
            fftwf_execute(p);
            //fftw_execute(p, in2, out2); //compute fft

            int subbandSize=int(round(float(frames/2)/float(subband_number)));
            float* subband_buffer=new float[frames/2];
            for (int j=1;j<frames/2-1;j++){
                subband_buffer[j]=  sqrt(pow(out[j][0],2) + pow(out[j][1],2));
            }

            //compute the energy of n subbands
            int reminder=(frames/2-1) % subbandSize;
            for (int i2=0;i2<frames/2-1-reminder;i2+=subbandSize){
                double subbandE=0;

                for (int i3=i2;i3<i2+subbandSize;i3++){
                    subbandE+=log10(subband_buffer[i3]);
                }
                if(i2/subbandSize<subband_number)
                    subband_energies[i2/subbandSize]=subbandE/double(subbandSize);
            }
            //end compute the energy of n subbands
            zaparr(subband_buffer);
        }else{
            for (int i=0;i<subband_number;i++){
                subband_energies[i]=0;
            }
        }
        lastRenderedPosition=position;

        //QFrame::paintEvent(ev);

        QPainter p(&pixmap);
        p.fillRect(0,0,contentsRect().width(),contentsRect().height(),QBrush::black);
        float totalBandWidth=float(contentsRect().width())/float(subband_number);
        int rectWidth=int(round(round(totalBandWidth)*0.5f));
        int spaceH=int(round(round(totalBandWidth)*0.5f));
        float totalBandHeight=float(contentsRect().height())/float(steps);
        int rectHeight=int(round(round(totalBandHeight)*0.5f));
        int spaceV=int(round(round(totalBandHeight)*0.5f));

        QColor color(0,255,0);
        QColor fadedColor(0,255,0);
        QBrush brush(color);
        QBrush fadedBrush(fadedColor);
        for (int i=0;i<subband_number;i++){
            color.setRgb(0,255,0);
            for(int j=0;j<steps;j++){
                int r=color.red();
                int g=color.green();
                int b=color.blue();
                if(r<255){
                    r=(int)(255*(double(j)/double(steps)))*2;
                    r=r>255?255:r;
                }
                else if(g>0){
                    g=(int)(255*(double(steps-j)/double(steps)))*2;
                    g=g>255?255:g;
                }
                color.setRgb(r,g,b);
                fadedColor.setRgb(int(float(r)*0.2),int(float(g)*0.2),int(float(b)*0.2));
                brush.setColor(color);
                fadedBrush.setColor(fadedColor);
                if(subband_energies[i]>=double(j)/double(steps)){
                    p.fillRect(x+i*(rectWidth+spaceH),y+contentsRect().height()-(j*(rectHeight+spaceV)),
                        rectWidth,-rectHeight,brush);
                }else{
                    p.fillRect(x+i*(rectWidth+spaceH),y+contentsRect().height()-(j*(rectHeight+spaceV)),
                        rectWidth,-rectHeight,fadedBrush);
                }
            }
        }
        p.end();
    }
    if (position<lastRenderedPosition) lastRenderedPosition=0;
    if(pixmap.size().isValid()) bitBlt(this,contentsRect().x(),contentsRect().y(),&pixmap);
}

void SpectrumView::customEvent(QCustomEvent* ev)
{
    if(ev->type() == PLAY_POSITION_CHANGED_EVENT){
        position=(((PlayPositionChanged*)ev)->getPosition());
        playing=(((PlayPositionChanged*)ev)->getPlaying());
        update();
    }
}


/*!
    \fn SpectrumView::setSoundManager(SoundManager*)
 */
void SpectrumView::setSoundManager(SoundManager* sm)
{
    soundManager=sm;
}


/*!
    \fn SpectrumView::resizeEvent(QresizeEvent* ev)
 */
void SpectrumView::resizeEvent(QResizeEvent* ev)
{
    if(ev->size().isValid()){
        subband_number=int(round(ev->size().width()/10));
        steps=int(round(ev->size().height()/3));
        if(subband_number>0){
            if(subband_energies) zaparr(subband_energies);
            subband_energies=new float[subband_number];
        }
    }
}

QSize SpectrumView::sizeHint () const{
    return QSize(100,height());
}
