/* display.c */

#include <avr/io.h>
#include <avr/pgmspace.h>

#include "common.h"
#include "i2c.h"
#include "font.h"

#define ADDRESS 0x3c

#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))

#define HORIZONTAL_FLIP 0
#define VERTICAL_FLIP 0

#define COM_ALT 1
#define COM_LR_REMAP 0

#define DISPLAY_INVERT 0

#define DEFAULT_CONTRAST 0x7f

/* Init sequence from SSD1306 datasheet */
const uint8 display_init_seq[] = {
	0xa8, 0x3f, /* Multiplex ratio */
	0xd3, 0x00, /* Display offset */
	0x40, /* Display start line */
	0xa0 | HORIZONTAL_FLIP,
	0xc0 | (VERTICAL_FLIP << 3),
	0xda, 0x02 | (COM_ALT << 4) | (COM_LR_REMAP << 5),
	0x81, DEFAULT_CONTRAST,
	0xa4, /* Disable entire display on */
	0xa6 | DISPLAY_INVERT,
	0xd5, 0x80, /* clock divide ratio */
	0x8d, 0x14, /* charge pump setting */
	0xaf  /* display on */
};

uint8 curpos;

#if 0
void delay(uint8_t count)
{
    volatile uint8_t i;

    while (count--)
	for (i = 255; i; i--)
	    ;
}
#endif

void display_command(uint8_t command)
{
    i2c_set_xor(0);
    i2c_write(ADDRESS, 0x80, 1, &command);
}

void display_init(void)
{
    uint8 i;

    i2c_init();

    for (i = 0; i < ARRAY_SIZE(display_init_seq); i++)
	display_command(display_init_seq[i]);

    curpos = 0;
}

void display_data(uint8_t len, uint8_t *data)
{
    i2c_write(ADDRESS, 0x40, len, data);
    curpos += len;
#if 0
    /* This bit is only needed if you are relying on wraparound. */
    if (curpos > 127)
	curpos -= 128;
#endif
    //delay(255);
}

/* col = graphics column, row = text rows
 * 0-127                  0-7
 */
void display_setposition(uint8_t col, uint8_t row)
{
    display_command(0xb0 + (row & 0x0f));
    display_command(0x00 + (col & 0x0f));
    display_command(0x10 + ((col >> 4) & 0x0f));
    curpos = col;
}

void display_setinverse(bool on)
{
    i2c_set_xor(on ? 0xff : 0);
}

void display_putchar(char c)
{
    uint8_t *data = font_getchar(c);
    display_data(6, data);
}

void display_putstr(char *s)
{
    while (*s)
	display_putchar(*(s++));
}

void display_putstr_P(const char *s)
{
    char c;
    while ((c = pgm_read_byte(s++)) != '\0')
	display_putchar(c);
}

#ifdef FAST_CLEAR
const uint8_t zero_data[] = {0, 0, 0, 0, 0, 0, 0, 0};
#endif

void display_clearline(void)
{
    uint8_t remaining = 128 - curpos;

#ifdef FAST_CLEAR
    while (remaining) {
	uint8_t todo = remaining;
	if (todo > sizeof(zero_data))
	    todo = sizeof(zero_data);
	display_data(todo, (uint8_t *)zero_data);
	remaining -= todo;
    }
#else
    uint8_t zero = 0;

    while (remaining--)
	display_data(1, &zero);
#endif
}
