/* Motenpoche 
 *
 * (c) 2023 Daniele Lacamera <root@danielinux.net>
 *
 *
 * Motenpoche is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Motenpoche is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 *
 */
#include <stdlib.h>
#include <string.h>
#include "display.h"
#include "hardware/i2c.h"
#include "bsp/board.h"

#define DISPLAY_I2C_ADDR 0x3C   

static uint8_t contrast = 0xcf;
static i2c_inst_t *i2c_inst = NULL;


static void display_send_data(const uint8_t *buf, int len)
{
    const uint8_t start_data = 0x40;
    int i;
    uint8_t data_buf[200];
    data_buf[0] = 0x40;
    if (len > 199)
        len = 199;
    memcpy(data_buf + 1, buf, len);
    if (!i2c_inst)
        return;
    i2c_write_blocking(i2c_inst, DISPLAY_I2C_ADDR, data_buf, len+1, 0);
}

static void display_send_cmd(uint8_t cmd)
{
    uint8_t buf[2] = {0x00, cmd};
    if (!i2c_inst)
        return;
    i2c_write_blocking(i2c_inst, DISPLAY_I2C_ADDR, buf, 2, 0);
}

static void display_send_cmd1(uint8_t cmd, uint8_t arg1)
{
    uint8_t buf[3] = {0x00, cmd, arg1};
    if (!i2c_inst)
        return;
    i2c_write_blocking(i2c_inst, DISPLAY_I2C_ADDR, buf, 3, 0);
}
static void display_send_cmd2(uint8_t cmd, uint8_t arg1, uint8_t arg2)
{
    uint8_t buf[4] = {0x00, cmd, arg1, arg2};
    if (!i2c_inst)
        return;
    i2c_write_blocking(i2c_inst, DISPLAY_I2C_ADDR, buf, 4, 0);
}


void display_scroll(uint8_t line)
{
    display_send_cmd1(SSD1306_SETSTARTLINE, (line % 0x40) + 0x40);
}

void display_setcontrast(uint8_t c)
{
    contrast = c; 
    display_send_cmd1(SSD1306_SETCONTRAST, contrast);
}

uint8_t display_getcontrast(void)
{
    return contrast;
}

void display_clear(void)
{

    int i,j;
    uint8_t zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0 };
    display_send_cmd(0x00);
    display_send_cmd(0x10);

    for (i = 0; i < 8; i++)
    {
        display_send_cmd2(SSD1306_PAGEADDR, i, 0xFF);
        for (j = 0; j < WIDTH; j++)
            display_send_data(zeros, 8);
    }
}

void display_text(int row, const char *text)
{
    int k;
    display_send_cmd(0x00);
    display_send_cmd(0x10);
    display_send_cmd2(SSD1306_PAGEADDR, 7 - row, 0xFF);
    for(k = 0; k < strlen(text); k++)
        display_send_data(fb_font[text[k]], 8);
}

void display_text_inverse(int row, int col, const char *text)
{
    int k, j;
    uint8_t inv_buf[8];
    uint8_t ones[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
    int len = strlen(text);
    if (len > 15)
        len = 15;
    display_send_cmd(0x00);
    display_send_cmd(0x10);
    sleep_us(1000);
    display_send_cmd2(SSD1306_PAGEADDR, 7 - row, 0x00);
    display_send_cmd2(SSD1306_COLUMNADDR, col << 3, WIDTH - 1);
    for(k = 0; k < len; k++) {
        for (j = 0; j < 8; j++)
            inv_buf[j] = ~fb_font[text[k]][j];
        display_send_data(inv_buf, 8);
    }
    display_send_cmd2(SSD1306_COLUMNADDR, 0, WIDTH - 1);
}


int display_init(void *priv)
{
    int i;
    int k = 0;
    int row = 0;
    volatile int j;
    uint8_t dbuf2[64] = {};
    int page = 0;
    int seg = 0;
    volatile uint32_t now;

    i2c_inst = (i2c_inst_t *)priv;


    for (i = 1; i < 64; i++) {
        dbuf2[i] = 0;
    }
    display_send_cmd(SSD1306_DISPLAYOFF);
    display_send_cmd1(0xD6, 0x01);
    display_send_cmd(0xA1);
    display_setcontrast(contrast);
    display_send_cmd1(SSD1306_CHARGEPUMP,  0x14);
    display_send_cmd1(SSD1306_MEMORYMODE,  0x00);
    display_send_cmd1(SSD1306_SETCOMPINS, 0x12);
    display_send_cmd1(SSD1306_SETDISPLAYOFFSET, 0x00);
    display_send_cmd1(SSD1306_SETVCOMDETECT, 0x00);
    display_send_cmd1(SSD1306_SETMULTIPLEX, 63);
    display_send_cmd(SSD1306_COMSCANINC);
    display_send_cmd(SSD1306_DISPLAYALLON_RESUME);
    display_send_cmd(SSD1306_DISPLAYON);
    display_send_cmd(0x2E);
    
    display_send_cmd2(SSD1306_PAGEADDR, 0, 0xFF);
    display_send_cmd2(SSD1306_COLUMNADDR, 0, WIDTH - 1);
    display_send_cmd1(SSD1306_SETSTARTLINE, 0);
    display_send_cmd(0x00);
    display_send_cmd(0x10);
    for (page = 0; page < 8; page++) {
        display_send_cmd2(SSD1306_PAGEADDR, page, 0xFF);
        for (seg= 0; seg < 32; seg++) {
            display_send_data(dbuf2, 8);
        }
        display_send_cmd1(SSD1306_SETSTARTLINE, row);
    }
    return 0;
}
