/* sh1106_i2c.c
 * Display driver for SH1106 I2C bus
 * (c) Jirka Chráska 2026; <jirka@lixis.cz>
 */


#include "sh1106_i2c.h"
#include "utf8.h"
#include "font.h"
#include <math.h>
#include <stdlib.h>

#define SH1106_DISPLAYOFF           0xAE

uint8_t pageBuffer[8][128+4];

inline static void swap(int32_t *a, int32_t *b) 
{
    int32_t *t=a;
    *a=*b;
    *b=*t;
};

void SH1106_init(sh1106_t *sh1106, i2c_inst_t *i2c, uint8_t address, uint8_t width, uint8_t height) {
    sh1106->address = address;
    sh1106->width = width;
    sh1106->height = height;
    sh1106->pages = height / 8;
    sh1106->i2c = i2c;
    sh1106->buffer = (uint8_t **) pageBuffer;
    
    for(uint8_t i = 0; i < 8; i++){ //dark screen
        for(uint8_t j = 0; j < 128+4; j++){
            pageBuffer[i][j] = 0x00;
        }
    }
    SH1106_Write_CMD(sh1106, SH1106_DISPLAYOFF); // off
    sleep_ms(100);
    SH1106_Write_CMD(sh1106, SET_SEG_REMAP | 0x01); //flip left-right
    SH1106_Write_CMD(sh1106, SET_SCAN_DIR | 0x08);  //flip top-bottom
    SH1106_Write_CMD(sh1106, SET_DISP | 0x01);
    sleep_ms(100);
}

void SH1106_Write_CMD(sh1106_t *sh1106, uint8_t command) {
    uint8_t buffer[2];
    buffer[0] = 0x80;
    buffer[1] = command;
    i2c_write_blocking(sh1106->i2c, sh1106->address, buffer, 2, false);
}
void SH1106_Write_Data(sh1106_t *sh1106, uint8_t* data) {
    size_t bufsize = sh1106->width+1;
    uint8_t broadCastBuffer[bufsize];
    broadCastBuffer[0] = 0x40;
    for (int i = 0; i < sh1106->width; i++) {
        broadCastBuffer[i+1] = data[i];
    }
    i2c_write_blocking(sh1106->i2c, sh1106->address, broadCastBuffer, bufsize, false);
}


// vykreslení celého pageBufferu na displeji
void SH1106_draw(sh1106_t *sh1106)
{
    for(uint8_t page = 0; page < sh1106->pages; page++){
        SH1106_Write_CMD(sh1106, SET_PAGE_ADDR | page);
//        SH1106_Write_CMD(sh1106, LOW_COL_ADDR  | 0x02);
        SH1106_Write_CMD(sh1106, LOW_COL_ADDR  | 0x02);
        SH1106_Write_CMD(sh1106, HIGH_COL_ADDR | 0x00);
        SH1106_Write_Data(sh1106, pageBuffer[page]);
    }
}

// kreslení bodu do pageBufferu
void SH1106_drawPixel(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t color)
{
    if(x > sh1106->width || y > sh1106->height){
        return;
    }
    if(color == 0){
        pageBuffer[y/8][x] &= ~(1 << (y % 8));
    }else{
        pageBuffer[y/8][x] |= (1 << (y % 8));
    }

}

// kreslení čáry -- dole je lepší funkce
// void SH1106_drawLine(sh1106_t *sh1106, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
// {
//     if(x1 > x2) {
//         swap(&x1, &x2);
//         swap(&y1, &y2);
//     }
//     if(x1 == x2) {
//         if(y1 > y2) {
//             swap(&y1, &y2);
//         }
//         for( int32_t i=y1; i<=y2; ++i) {
//             SH1106_drawPixel(sh1106, x1, i, 1);
//         }
//         return;
//     }
//     
//     float m = (float) (y2-y1) / (float) (x2-x1);
//     for(int32_t i=x1; i<=x2; ++i) {
//         float y = m * (float) (i-x1) + (float) y1;
//         SH1106_drawPixel( sh1106, i, (uint32_t) y, 1);
//     }
// }


// kreslení čáry (Breselham)
void SH1106_drawLine(sh1106_t *sh1106, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint8_t color)
{
   int dx =  abs(x1-x0), sx = x0<x1 ? 1 : -1;
   int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
   int err = dx+dy, e2; /* error value e_xy */

   for(;;){  /* loop */
      SH1106_drawPixel(sh1106, x0, y0, color);
      if (x0==x1 && y0==y1) break;
      e2 = 2*err;
      if (e2 >= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */
      if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */
   }
}
// kreslení vodorovné linky do pageBufferu
void SH1106_drawHLine(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t w, uint8_t color)
{
    if(x > sh1106->width || y > sh1106->height){
        return;
    }
    if((x + w) > sh1106->width){
        w = sh1106->width - x;
    }
    for(uint8_t i = 0; i < w; i++){
        SH1106_drawPixel(sh1106, x + i, y, color);
    }
};

// kreslení svislé linky do pageBufferu
void SH1106_drawVLine(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t w, uint8_t color)
{
    if(x > sh1106->width || y > sh1106->height){
        return;
    }
    if((x + w) > sh1106->height){
        w = sh1106->height - x;
    }
    for(uint8_t i = 0; i < w; i++){
        SH1106_drawPixel(sh1106, x, y+i, color);
    }
};

// kreslení kružnice

void SH1106_drawCircle(sh1106_t *sh1106, int xm, int ym, int r, uint8_t color)
{
   int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */ 
   do {
      SH1106_drawPixel(sh1106, xm-x, ym+y, color); /*   I. Quadrant */
      SH1106_drawPixel(sh1106, xm-y, ym-x, color); /*  II. Quadrant */
      SH1106_drawPixel(sh1106, xm+x, ym-y, color); /* III. Quadrant */
      SH1106_drawPixel(sh1106, xm+y, ym+x, color); /*  IV. Quadrant */
      r = err;
      if (r <= y) err += ++y*2+1;           /* e_xy+e_y < 0 */
      if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
   } while (x < 0);
}
  

// vymazání pageBufferu
void SH1106_clear(sh1106_t *sh1106)
{
    for(uint8_t i = 0; i < 8; i++){ //dark screen
        for(uint8_t j = 0; j < 128; j++){
            pageBuffer[i][j] = 0x00;
        }
    }
};

// kreslení obdélníku: x,y jsou souřadnice pravého horního rohu, width je výška a height je šířka obdélníku
void SH1106_drawRectangle(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) 
{
    for(uint8_t i=0; i<width; ++i)
        for(uint8_t j=0; j<height; ++j)
            SH1106_drawPixel(sh1106, x+i, y+j, color);

};

/*
kreslení jednoho znaku 
function: SH1106_drawChar
parameters:
    p		: ukazatel na strukturu sh1106_t
    x		: souřadnice x
    y		: souřadnice y
    Font	: ukazatel na strukturu bitmapFONT
    Index	: index znaku ve fontu
vrací šířku znaku v pixelech
*/
uint16_t SH1106_drawChar(sh1106_t * p, uint32_t x, uint32_t y, const bitmapFONT* Font, uint32_t Index, uint8_t color) 
{
uint8_t row, column, fcolor;

    if( x > p->width || y > p->height ) {
	return 0;
    }
    
uint8_t  col    = Font->Width/8; // sloupce v bytech
uint8_t  fcol   = col;
uint8_t  zb     = Font->Width%8;

    if( zb != 0) {
        col ++;
    }

uint8_t chwidth = Font->Widths[Index]; // šířka znaku
uint16_t chsize = Font->Height * col;
uint32_t bindex = Index*chsize;

    for (row = 0; row < Font->Height; row++ ) {
        // lezeme po bytech (po osmičkách)
        uint8_t chw = chwidth;
        for (column = 0; column < col; column++, chw-- ) {
            for( int j = 0; j<8; j++ ) {
                if( chw == 0 ) break;
                fcolor = ((uint8_t) Font->Bitmap[bindex+row*col+column]) & (0x80 >> j);
                if( fcolor ) {
                    SH1106_drawPixel(p, x + column*8 + j, y + row, color);
                } else {
                    SH1106_drawPixel(p, x + column*8 + j, y + row, !color);
                }
            }
        } 
    }
return chwidth;
}

/**
 * kreslení UTF-8 řetězce
parameters:
    p                : ukazatel na strukturu sh1106_t
    x                : souřadnice x 
    y                : souřadnice y
    Font             : ukazatel na strukturu bitmapFONT
    pString          : řetězec
returns:
    šířka v pixelech zabíraná řetězcem
*/
uint16_t SH1106_drawString(sh1106_t *p, uint32_t x, uint32_t y, bitmapFONT * Font, const char * pString, uint8_t color )
{
uint32_t Xpoint = x;
uint32_t Ypoint = y;
uint16_t index; 
uint16_t str_width  = 0;
uint16_t char_width = 0;

    if (x > p->width || y > p->height) {
        return 0;
    }

    utf8_string    ustr = make_utf8_string(pString);
    utf8_char_iter iter = make_utf8_char_iter(ustr);
    utf8_char c;
    uint32_t codepoint;
    while ((c = next_utf8_char(&iter)).byte_len > 0 ) {
        codepoint = unicode_code_point(c);
        index = 0;
        for(int i=0; i < Font->Chars; i++) { 
            if(Font->Index[i] == codepoint ) {
                index = i;
                break;
                }
            }
        if((Xpoint + Font->Width ) > p->width ) {
            Xpoint = x;
            Ypoint += Font->Height;
        }
        if ((Ypoint  + Font->Height ) > p->height ) {
            Xpoint = x;
            Ypoint = y;
        }
        char_width = SH1106_drawChar(p, Xpoint, Ypoint, Font, index, color);
        Xpoint    += char_width;
        str_width += char_width;
    }
return str_width;
}

