
#include "support.h"

static uint16_t *fb;
static uint32_t pitch;
static uint32_t width;
static uint32_t height;

#define render_width    800
#define render_height   800
#define max_work        4000
#define oversample      4
#define buddha          1
#define redraw_step     0xffff

// over 5
// max 2000

uint32_t Begin_Time;
uint32_t End_Time;

uint32_t buffer[render_width * render_height];

void RedrawBuffer()
{
    int start_x = (width - render_width) / 2;
    int start_y = (height - render_height) / 2;

    int out_offset = start_x + (start_y * pitch) / 2;
    int in_offset = 0;

    for (int y=0; y < render_height; y++) {

        for (int x=0; x < render_width; x++) {
            uint32_t rgb = buffer[in_offset + x];
            uint32_t r=96*rgb,g=128*rgb,b=256*rgb;

            b = (b + 1023) / 2048;
            r = (r + 1023) / 2048;
            g = (g + 511) / 1024;

            if (b > 31) b = 31;
            if (r > 31) r = 31;
            if (g > 63) g = 63;

            uint16_t c = b | (g << 5) | (r << 11);
            fb[out_offset + x] = LE16(c);
        }
        in_offset += render_width;
        out_offset += pitch/2;
    }
}

void RedrawBufferLine(int line)
{
    int start_x = (width - render_width) / 2;
    int start_y = line + (height - render_height) / 2;

    int out_offset = start_x + (start_y * pitch) / 2;
    int in_offset = render_width * line;

    for (int x=0; x < render_width; x++) {
        uint32_t rgb = buffer[in_offset + x];
        uint32_t r=96*rgb,g=128*rgb,b=256*rgb;

        b = (b + 1023) / 2048;
        r = (r + 1023) / 2048;
        g = (g + 511) / 1024;

        if (b > 31) b = 31;
        if (r > 31) r = 31;
        if (g > 63) g = 63;

        uint16_t c = b | (g << 5) | (r << 11);
        fb[out_offset + x] = LE16(c);
    }
}

typedef struct {
	double r;
	double i;
} complexno_t;

complexno_t workTrajectories[max_work];

unsigned long calculateTrajectory(double r, double i)
{
    double realNo, imaginaryNo, realNo2, imaginaryNo2, tmp;
    unsigned long trajectory;

    /* Calculate trajectory */
    realNo = 0;
    imaginaryNo = 0;

    for(trajectory = 0; trajectory < max_work; trajectory++)
    {
        /* Check if it's out of circle with radius 2 */
        realNo2 = realNo * realNo;
        imaginaryNo2 = imaginaryNo * imaginaryNo;

        if (realNo2 + imaginaryNo2 > 4.0)
            return trajectory;

        /* Next */
        tmp = realNo2 - imaginaryNo2 + r;
        imaginaryNo = 2.0 * realNo * imaginaryNo + i;
        realNo = tmp;

        if (buddha)
        {
            /* Store */
            workTrajectories[trajectory].r = realNo;
            workTrajectories[trajectory].i = imaginaryNo;
        }
    }

    return 0;
}

void Buddha()
{
    fb = get_fb();
    pitch = get_pitch();
    width = get_width();
    height = get_height();
    unsigned long trajectoryLength;
    unsigned long current;
    unsigned long total_path_lo = 0;
    unsigned long total_path_hi = 0;

#if buddha
    kprintf("[Buddha] Starting Buddhabrot fractal generator\n");
#else
    kprintf("[Buddha] Starting fractal generator\n");
#endif
    kprintf("[Buddha] Screen %dx%d at 0x%08x\n", width, height, fb);
    kprintf("[Buddha] Rendering image with size %dx%d\n", render_width, render_height);

    silence(1);

    for (unsigned ii=0; ii < render_width*render_height; ii++)
        buffer[ii] = 0;

    double x, y;
    double diff = 4.0 / ((double)(render_width * oversample));
    double diff_y = 4.0 / ((double)(render_height * oversample));
    double y_base = 2.0 - (diff / 2.0);
    double diff_sr = 4.0 / (double)render_width;

    Begin_Time = LE32(*(volatile uint32_t*)0xf2003004);
    const unsigned long line_size = render_width * oversample;
    unsigned long line_pos = 0;
    unsigned long line_number = 0;

    for (current = 0; current < (render_width * render_height * oversample * oversample); current++)
    {
        unsigned long val;

        /* Locate the point on the complex plane */
        x = ((double)(current % (render_width * oversample))) * diff - 2.0;
        y = ((double)(current / (render_width * oversample))) * diff - y_base;

        //kprintf("current=%d, x=%d, y=%d, ", current, (int)(1000*x), (int)(1000*y));

        /* Calculate the points trajectory ... */
        trajectoryLength = calculateTrajectory(x, y);

        total_path_lo += trajectoryLength;
        if (total_path_lo >= 1000000000)
        {
            total_path_lo -= 1000000000;
            total_path_hi++;
        }

        //kprintf("len=%d\n", trajectoryLength);

        if (buddha)
        {
            /* Update the display if it escapes */
            if (trajectoryLength > 0)
            {
                unsigned long pos;
                int i;

                for(i = 0; i < trajectoryLength; i++)
                {
                    unsigned long px = (workTrajectories[i].r + 2.0) / diff_sr;
                    unsigned long py = (workTrajectories[i].i + y_base) / diff_sr;

                    if (px < render_width && py < render_height)
                    {
                        pos = (unsigned long)(render_width * py + px);

                        val = buffer[pos];

                        if (val < 0xfff)
                            val++;

                        buffer[pos] = val;
                    }
                }
            }
            if ((current & redraw_step) == 0)
                RedrawBuffer();
        }
        else
        {
            unsigned long px = line_pos / oversample;
            unsigned long py = line_number / oversample;

            val = buffer[px + py * render_width];
            val += trajectoryLength;
            if (val > 0xfff)
                val = 0xfff;
            buffer[px + py * render_width] = val;
            line_pos++;
            if (line_pos == line_size) {
                line_pos = 0;
                RedrawBufferLine(py);
                line_number++;
            }
        }

    }
    RedrawBuffer();

    End_Time = LE32(*(volatile uint32_t*)0xf2003004) - Begin_Time;

    silence(0);

    kprintf("[Buddha] Time consumed: %d.%03d seconds\n", End_Time / 1000000, (End_Time % 1000000) / 1000);
    kprintf("[Buddha] Total calculation iterations: %d'%09d\n", total_path_hi, total_path_lo);
}
