#define USE_FORGE_CPU_COPY_HELPERS
#include <complex>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <iostream>
const unsigned IMGW      = 256;
const unsigned IMGH      = 256;
const unsigned DIMX      = 1000;
const unsigned DIMY      = 800;
const unsigned WIN_ROWS  = 1;
const unsigned WIN_COLS  = 2;
const unsigned NBINS     = 256;
using namespace std;
struct Bitmap {
    unsigned char *ptr;
    unsigned width;
    unsigned height;
};
class PerlinNoise
{
    private:
        float base[IMGW][IMGH];
        float perlin[IMGW][IMGH];
    public:
        PerlinNoise();
        float noise(float u, float v);
};
Bitmap createBitmap(unsigned w, unsigned h);
void destroyBitmap(Bitmap& bmp);
void kernel(Bitmap& bmp);
void populateBins(Bitmap& bmp, int *hist_array, const unsigned nbins, float *hist_cols);
int main(void)
{
    Bitmap bmp = createBitmap(IMGW, IMGH);
    
    
    wnd.
grid(WIN_ROWS, WIN_COLS);
    
    
    
    do {
        
        kernel(bmp);
        
        
        std::vector<int> histArray(NBINS, 0);
        std::vector<float> colArray(3*NBINS, 0.0f);
        populateBins(bmp, histArray.data(), NBINS, colArray.data());
        wnd.
draw(0, 0, img,  
"Dynamic Perlin Noise" );
        wnd.
draw(0, 1, chart, 
"Histogram of Noisy Image");
    releaseGLBuffer(handles[0]);
    releaseGLBuffer(handles[1]);
    releaseGLBuffer(handles[2]);
    return 0;
}
float interp(float x0, float x1, float alpha)
{
    return x0 * (1 - alpha) + alpha * x1;
}
PerlinNoise::PerlinNoise()
{
    std::srand(std::time(0));
    for(unsigned i=0; i < IMGW; i++)
    {
        for(unsigned j=0; j < IMGH; j++)
        {
            base[i][j] = std::rand()/(float)(RAND_MAX);
            perlin[i][j] = 0;
        }
    }
    float persistence = 0.5f;
    float amp  = 1.0f;
    float tamp = 0.0f;
    for (int octave=6; octave>=0; --octave)
    {
        int period = 1 << octave;
        float freq = 1.0f / period;
        for(unsigned i=0; i < IMGW; i++)
        {
            int si0 = (i/period) * period;
            int si1 = (si0 + period) % IMGW;
            float hblend = (i - si0) * freq;
            for(unsigned j=0; j < IMGH; j++)
            {
                int sj0 = (j/period) * period;
                int sj1 = (sj0 + period) % IMGH;
                float vblend = (j - sj0) * freq;
                float top = interp(base[si0][sj0], base[si1][sj0], hblend);
                float bot = interp(base[si0][sj1], base[si1][sj1], hblend);
                perlin[i][j] += (amp * interp(top, bot, vblend));
            }
        }
        tamp += amp;
        amp *= persistence;
    }
    for(unsigned i=0; i < IMGW; i++)
        for(unsigned j=0; j < IMGH; j++)
            perlin[i][j] /= tamp;
}
float PerlinNoise::noise(float u, float v)
{
    return perlin[(unsigned)(IMGW*u)][(unsigned)(IMGH*v)];
}
Bitmap createBitmap(unsigned w, unsigned h)
{
    Bitmap retVal;
    retVal.width = w;
    retVal.height= h;
    retVal.ptr   = new unsigned char[4*w*h];
    return retVal;
}
void destroyBitmap(Bitmap& bmp)
{
    delete[] bmp.ptr;
}
void kernel(Bitmap& bmp)
{
    PerlinNoise perlin;
    for (unsigned y=0; y<bmp.height; ++y) {
        for (unsigned x=0; x<bmp.width; ++x) {
            int offset  = x + y * bmp.width;
            float u = x/(float)(bmp.width);
            float v = y/(float)(bmp.height);
            unsigned char noiseVal = 255 * perlin.noise(u, v);
            bmp.ptr[offset*4 + 0]   = noiseVal;
            bmp.ptr[offset*4 + 1]   = noiseVal;
            bmp.ptr[offset*4 + 2]   = noiseVal;
            bmp.ptr[offset*4 + 3]   = 255;
        }
    }
}
void populateBins(Bitmap& bmp, int *hist_array, const unsigned nbins, float *hist_cols)
{
    for (unsigned y=0; y<bmp.height; ++y) {
        for (unsigned x=0; x<bmp.width; ++x) {
            int offset  = x + y * bmp.width;
            unsigned char noiseVal = bmp.ptr[offset*4];
            unsigned idx = (int)((float)noiseVal/255.f * nbins);
            hist_array[idx]++;
        }
    }
    for (unsigned b=0; b<nbins; ++b) {
        hist_cols[3*b+0] = std::rand()/(float)RAND_MAX;
        hist_cols[3*b+1] = std::rand()/(float)RAND_MAX;
        hist_cols[3*b+2] = std::rand()/(float)RAND_MAX;
    }
}