#include "fblib.h"

// helper function to 'plot' a pixel in given color
void put_pixel(screen fb, int x, int y, int r, int g, int b)
{
    // calculate the pixel's byte offset inside the buffer
    // note: x * 2 as every pixel is 2 consecutive bytes
    //unsigned int pix_offset = x * 2 + y * finfo.line_length;
    unsigned int pix_offset = x * fb.step + y * fb.line;

    // now this is about the same as 'fbp[pix_offset] = value'
    // but a bit more complicated for RGB565
    //unsigned short c = ((r / 8) << 11) + ((g / 4) << 5) + (b / 8);
    unsigned short c = ((r / 8) * 2048) + ((g / 4) * 32) + (b / 8);
    // write 'two bytes at once'
    *((unsigned short*)(fb.buffer + pix_offset)) = c;
}

// helper function to clear the screen - fill whole 
// screen with given color
void clear_screen(screen fb) {
   memset(fb.buffer, 0, fb.screensize);
}

// helper function to draw a line in given color
// (uses Bresenham's line algorithm)
void draw_line(screen fb, int x0, int y0, int x1, int y1, int r, int g, int b) {
    int dx = x1 - x0;
    dx = (dx >= 0) ? dx : -dx; // abs()
    int dy = y1 - y0;
    dy = (dy >= 0) ? dy : -dy; // abs()
    int sx;
    int sy;
    if (x0 < x1)
        sx = 1;
    else
        sx = -1;
    if (y0 < y1)
        sy = 1;
    else
        sy = -1;
    int err = dx - dy;
    int e2;
    int done = 0;
    while (!done) {
        put_pixel(fb, x0, y0, r, g, b);
        if ((x0 == x1) && (y0 == y1))
            done = 1;
        else {
            e2 = 2 * err;
            if (e2 > -dy) {
                err = err - dy;
                x0 = x0 + sx;
            }
            if (e2 < dx) {
                err = err + dx;
                y0 = y0 + sy;
            }
        }
    }
}

// helper function to draw a rectangle in given color
void fill_rect(screen fb, int x, int y, int w, int h, int r, int g, int b) {
    int cx, cy;
    for (cy = 0; cy < h; cy++) {
        for (cx = 0; cx < w; cx++) {
           put_pixel(fb, x + cx, y + cy, r, g, b);
        }
    }
}

// helper function to draw a rectangle outline in given color
void draw_rect(screen fb, int x0, int y0, int w, int h, int r, int g, int b) {
    draw_line(fb, x0, y0, x0 + w, y0, r, g, b); // top
    draw_line(fb, x0, y0, x0, y0 + h, r, g, b); // left
    draw_line(fb, x0, y0 + h, x0 + w, y0 + h, r, g, b); // bottom
    draw_line(fb, x0 + w, y0, x0 + w, y0 + h, r, g, b); // right
}

// helper function to draw a circle outline in given color
// (uses Bresenham's circle algorithm)
void draw_circle(screen fb, int x0, int y0, int radius, int r, int g, int b)
{
    int x = radius;
    int y = 0;
    int radiusError = 1 - x;

    while(x >= y)
    {
        // top left
        put_pixel(fb, -y + x0, -x + y0, r, g, b);
        // top right
        put_pixel(fb, y + x0, -x + y0, r, g, b);
        // upper middle left
        put_pixel(fb, -x + x0, -y + y0, r, g, b);
        // upper middle right
        put_pixel(fb, x + x0, -y + y0, r, g, b);
        // lower middle left
        put_pixel(fb, -x + x0, y + y0, r, g, b);
        // lower middle right
        put_pixel(fb, x + x0, y + y0, r, g, b);
        // bottom left
        put_pixel(fb, -y + x0, x + y0, r, g, b);
        // bottom right
        put_pixel(fb, y + x0, x + y0, r, g, b);

        y++;
        if (radiusError < 0)
        {
            radiusError += 2 * y + 1;
        } else {
            x--;
            radiusError+= 2 * (y - x + 1);
        }
    }
}

// helper function to draw a filled circle in given color
// (uses Bresenham's circle algorithm)
void fill_circle(screen fb, int x0, int y0, int radius, int r, int g, int b) {
    int x = radius;
    int y = 0;
    int radiusError = 1 - x;

    while(x >= y)
    {
        // top
        draw_line(fb, -y + x0, -x + y0, y + x0, -x + y0, r, g, b);
        // upper middle
        draw_line(fb, -x + x0, -y + y0, x + x0, -y + y0, r, g, b);
        // lower middle
        draw_line(fb, -x + x0, y + y0, x + x0, y + y0, r, g, b);
        // bottom 
        draw_line(fb, -y + x0, x + y0, y + x0, x + y0, r, g, b);

        y++;
        if (radiusError < 0)
        {
            radiusError += 2 * y + 1;
        } else {
            x--;
            radiusError+= 2 * (y - x + 1);
        }
    }
}


void render_string(screen fb, fbfont font, char *text, int x, int y, int r, int g, int b)
{
    int text_length = strlen(text);
    //printf("text_length = %d\n", text_length);
    
    int bytes_per_line = ceil(font.width / 8.0);
    //printf("bytes_per_line = %d\n", bytes_per_line);

    int char_offset = bytes_per_line * font.height;

    // each character
    for (int char_pos = 0; char_pos < text_length; char_pos++) {
        //int char_code = 0;
        int char_code = *(text + char_pos) + font.offset;
        int x_offset = char_pos * font.width + x;

        // each line of pixels
        for (int line = 0; line < font.height; line++) {
            int y_pos = line + y;

            // each byte in a line of pixels
            for (int byte_num = 0; byte_num < bytes_per_line; byte_num++) {
                int byte_offset = line * bytes_per_line + byte_num;
                char current_byte = *((font.xbm + char_code * char_offset) + byte_offset);

                // each bit/pixel in a byte
                for (int bit_number = 0; bit_number < 8; bit_number++) {
                    char mask = 1 << bit_number;
                    if(current_byte & mask) {
                        // plot the pixel
                        //printf("x");

                        int x_pos = x_offset + byte_num * 8 + bit_number;
                        put_pixel(fb, x_pos, y_pos, r, g, b);
                    }
                    else {
                        // leave empty (or maybe plot 'text backgr color')
                        //printf(" ");
                    }
                } // end "for bit_number"

            } // end "for byte_num"

			//printf("\n");

        } // end "for line"

    } // end "for char_pos"
}
