#include <cassert>
#include <FL/Fl_Box.H>
#include <FL/Fl.H>
#include "Fl_Osc_Widget.H"

//consider merging with Fl_Oscilloscope
class Fl_OscilSpectrum : public Fl_Box, public Fl_Osc_Widget
{
    public:
        Fl_OscilSpectrum(int x,int y, int w, int h, const char *label=0)
            :Fl_Box(x,y,w,h,label), nsamples(0), spc(NULL)
        {}

        ~Fl_OscilSpectrum(void)
        {
            delete[] spc;
            osc->removeLink(loc, (Fl_Osc_Widget*) this);
        }

        void init(bool base_spectrum_p)
        {
            Fl_Osc_Pane *og  = fetch_osc_pane(this);
            assert(og);

            loc = og->base + (base_spectrum_p ? "base-spectrum": "spectrum");
            osc = og->osc;
            assert(osc);

            osc->createLink(loc, (Fl_Osc_Widget*) this);
            update();
        }

        void update(void)
        {
            osc->requestValue(loc);
        }

        virtual void OSC_value(unsigned N, void *data) override
        {
            assert(!(N%4));
            const size_t new_samples = N / 4;

            //resize buffer if needed
            if(new_samples != nsamples) {
                delete [] spc;
                spc = new float[new_samples];
                nsamples = new_samples;
            }

            memcpy(spc, data, N);

            //normalize
            float max=0;
            for (unsigned i=0; i<nsamples; i++){
                float x=fabs(spc[i]);
                if (max<x) max=x;
            }
            if (max<0.000001) max=1.0;
            max=max*1.05;

            for(unsigned i=0; i<nsamples; ++i)
                spc[i]/=max;

            //Get widget to redraw new data
            redraw();
        }

        void draw(void)
        {
            const int ox=x(),oy=y(),lx=w(),ly=h();
            const int maxdb=60;//must be multiple of 10
            const int GX=2;
            int n=lx/GX-1;
            if (n>(int)nsamples)
                n=nsamples;
                
            fl_rectf(ox,oy,lx,ly,0,0,0);

            //draw
            if(this->active_r()) 
                fl_color(0,0,255);
            else 
                fl_color(this->parent()->color());
            fl_line_style(FL_DOT);

            for(int i=1; i<maxdb/10; i++){
                const int ky=((int)((float)i*ly*10.0/maxdb)/2)*2;
                fl_line(ox,oy+ky-1,ox+lx-2,oy+ky-1);
            }

            for(int i=2; i<n; i++){
                int tmp=i*GX-2;
                if(i%10==1)
                    fl_line_style(0);
                else
                    fl_line_style(FL_DOT);
                fl_line(ox+tmp,oy+2,ox+tmp,oy+ly-2);
            }

            if (this->active_r())
                fl_color(0,255,0);
            else
                fl_color(this->parent()->color());
            fl_line_style(0);

            if(!spc)
                return;
            //draws the spectrum
            for(int i=1; i<n; i++){
                int tmp=(i-1)*GX+2;
                float x=spc[i];

                if (x>dB2rap(-maxdb)) x=rap2dB(x)/maxdb+1;
                else x=0;

                int val=(int) ((ly-2)*x);
                if (val>0) fl_line(ox+tmp,oy+ly-2-val,ox+tmp,oy+ly-2);
            }
        }
    private:

        size_t nsamples;
        float *spc;
}; 
