#ifndef _DISPLAY_H
#define _DISPLAY_H

#include <stdint.h>
#ifdef _AVR_LIBC_VERSION__
#include <avr/pgmspace.h>
#endif
#include "check.h"

/**
 * \addtogroup Display OLED Display
 *
 * \brief Controls the optional 128×64 pixel monochrome OLED display
 *
 * The SPiCboard v3 has a TWI (two wire interface, also known as I²C),
 * which can be used to connect an OLED display.
 * Especially the SSD1306 monochrome OLED display fits perfectly onto the board.
 * It has a 0.96 inch diameter with 128 horizontal and 64 vertical pixels.
 *
 * For the sake of simplicity the horizontal pixels are termed <i>columns</i>.
 * The vertical pixels are divided into 8 so-called <i>pages</i>, therefore
 * a byte can represent the contents of a <i>page segment</i> (with 8 pixels - from
 * the <a href="https://en.wikipedia.org/wiki/Least_significant_bit">LSB</a> on
 * top to the <a href="https://en.wikipedia.org/wiki/Most_significant_bit">MSB</a>
 * on bottom of each page segement).
 *
 * \image html display_addr.png
 *
 * For additional details please have a look into the <a href="SSD1306.pdf">SSD1306 Datasheet</a>.
 *
 * In case your run into memory issues (which is quite easy with only 2 KB SRAM)
 * you might want to have a look into the \ref DisplayFromFlash "FromFlash" function variants.
 *
 * \warning The display is quite sensitive to fluctuation of current.
 *          Try to avoid contact of any [semi-]conductive material (including your fingers) with the pins
 *          on the Xplained Mini Board -- especially connecting PC3 with PC4 -- or the connection to your display
 *          might stop (it stalls until you restart it, the LED on the Xplained Mini Board can indicate the error).
 *
 * @{
 * \file display.h
 * \version \$Rev:  $
 */

/**
 * \brief Activate (and setup) the OLED-display
 *
 * \retval  0  success
 * \retval -1  initialization of I²C failed
 * \retval -2  setting I²C device address failed
 * \retval -3  writing to I²C device failed
 * \retval -4  timeout during I²C communication
 * \retval -5  out of bounds (addressing nonexistent page/column)
 */
int8_t sb_display_enable();

/**
 * \brief Check if a functional OLED display is present
 *
 * Tries to connect to the SSD1306 display (I²C device address <tt>0x78</tt>)
 * without enabling it.
 *
 * \retval 0   display is available and seems to work
 * \retval <0  display not available or not functional
 */
int8_t sb_display_available();

/**
 * \brief Turn the OLED display off
 *
 * \retval 0   success
 * \retval <0  on I²C error (\see sb_display_enable() return codes)
 */
int8_t sb_display_disable();

/**
 * \brief Draw a single page segment onto the OLED display
 *
 * \param pageNum  page number (0-7) for vertical positioning the output
 * \param colNum   column number (0-127) for the horizontal position of the output
 * \param content  contents to be drawn on the page segment (LSB on top and MSB on bottom)
 *
 * \retval 0   success
 * \retval <0  on I²C error (\see sb_display_enable() return codes)
 */
int8_t sb_display_draw(uint8_t pageNum, uint8_t colNum, uint8_t content);

/**
 * \brief Draw a (rectangular) bitmap onto the OLED display
 *
 * The result is similar to multiple calls of sb_display_draw()
 * (although the execution of this function is way faster):
 * \code {.c}
 *	for (uint8_t page = 0; page < pageCount; page++){
 *		for (uint8_t col = 0; col < colCount; col++){
 *			sb_display_draw(pageStart + page, colStart + col, contents[page * colCount + col]);
 *		}
 * }
 * \endcode
 *
 * \b Example:
 *
 * We want to draw a square with a point in its center with some spacing
 * to the to the left top corner of the screen.
 *
 * The bitmap will be drawn across two pages, each with eight page segments.
 * \image html display_square.png
 *
 * Hence we first initialize an array with 2*8 elements (columns 5-12 for
 * page 0 and 1) containing the contents of each page segement (1 for a white
 * pixel and 0 for a black/clear one) and then transfer them to the screen.
 * \include display_square.c
 *
 * \param pageStart  first page to set the top vertical position
 * \param colStart   first column to set the left horizontal position
 * \param pageCount  number of pages to set the bottom position
 *                   (including the first page)
 * \param colCount   number of columns to define the right border
 *                   (including the first column)
 * \param contents   array pointing to the bitmap (must have
 *                   <tt>pageCount * colCount</tt> elements) - or \c NULL
 *                   to clear the area
 *
 * \retval 0   success
 * \retval <0  on I²C error (see sb_display_enable() return codes)
 */
int8_t sb_display_drawBitmap(uint8_t pageStart, uint8_t colStart, uint8_t pageCount, uint8_t colCount, const uint8_t * contents);

/**
 * \brief Draw the contents of a \c 8*128 item 8-bit array (8 pages with
 *        128 columns, each filled with 8 bit) onto the OLED Display
 *
 * The function call is identical to
 * \code {.c}
 *	sb_display_drawBitmap(0, 0, 8, 128, contents);
 * \endcode
 *
 * In case the pointer is \c NULL, the entire screen will be cleared.
 *
 * \warning Make sure that the buffer points to an array with 1024 items -
 *          this is C, there is no range check!
 *
 * \param contents  pointer to a <tt>8*128</tt> item array - or NULL
 *                  to clear the screen
 *
 * \retval  0  success
 * \retval  <0 on I²C error (\see sb_display_enable() return codes)
 */
int8_t sb_display_fillScreen(const uint8_t *contents);

/**
 * \brief Print a \c \0 terminated text string on the OLED display
 *        using a 8×8 pixel font
 *
 * A single character will fit into one page (= eight rows) and eight columns.
 * Therefore it is possible to print 16 characters at maximum.
 *
 * If the length of the string exceeds the available columns
 * (= columns right of the start column) in the current page,
 * the output will \b not be continued on the next line!
 *
 * The font was extract from the
 * <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/lib/fonts">Linux kernel</a>
 * and is based on <a href="https://en.wikipedia.org/wiki/Code_page_437">code page 437</a>
 * (= <a href="https://en.wikipedia.org/wiki/Extended_ASCII">extended ASCII</a>,
 * every possible character has a graphical string representation).
 *
 * Consequently, the ASCII control characters (like line break) are
 * displayed with special symbols but don't have any functional impact.
 *
 * The \ref fonts "Fonts Table" contains a detailed list of the available characters
 *
 * \warning The font will be stored on the flash which will consume about 2300 bytes.
 *
 * \param pageNum   page number (0-7) for vertical positioning the output
 * \param colStart  first column to set the horizontal position of the output
 * \param str       0-terminated string
 *
 * \retval >=0  length of printed string (can be less than strlen(str) )
 * \retval <0   on I²C error (\see sb_display_enable() return codes)
 */
int8_t sb_display_showString(uint8_t pageNum, uint8_t colStart, const char *str);

/**
 * \brief Print a \c \0 terminated text string with a (slightly) wider
 *        8×8 pixel font onto the OLED display
 *
 * The character set is almost identical to the sb_display_showString() function
 * and the space requirement is the same (up to 16 characters per line)
 *
 * The \ref fonts "Fonts Table" contains a detailed list of the
 * available characters.
 *
 * \warning This font will consume about 2300 bytes, too.
 *
 * \param pageNum   page number (0-7) for vertical positioning the output
 * \param colStart  first column to set the horizontal position of the output
 * \param str       0-terminated string
 *
 * \retval >=0  length of printed string (can be less than strlen(str) )
 * \retval <0   on I²C error (see sb_display_enable() return codes)
 */
int8_t sb_display_showStringWide(uint8_t pageNum, uint8_t colStart, const char *str);

/**
 * \brief Print a \0 terminated text string on the OLED display using
 *        a small 6×4 pixel font
 *
 * A single character will fit into one page (eight rows, even though it is
 * just six pixels) and four columns, therefore it is possible to print 32
 * characters at maximum.
 *
 * If the length of the string exceeds the available columns
 * (= columns right of the start column) in the current page,
 * the output will not be continued on the next line!
 *
 * The \ref fonts "Fonts Table" contains a detailed list of the
 * available characters
 *
 * \warning In contrast to the other \c showString functions the character set
 *          is quite limited - it only supports printable ASCII characters (neither
 *          control characters nor the extended ASCII set).
 *          Hence, the required flash memory consumption is (with about 700 bytes) less
 *          than a third of the space requirements of the other fonts.
 *
 * \param pageNum   page number (0-7) for vertical positioning the output
 * \param colStart  first column to set the horizontal position of the output
 * \param str       0-terminated string
 *
 * \retval >=0  length of printed string (can be less than strlen(str) )
 * \retval <0   on I²C error (\see sb_display_enable() return codes)
 */
int8_t sb_display_showStringSmall(uint8_t pageNum, uint8_t colStart, const char * str);

/** @}*/

/**
 * \addtogroup DisplayFromFlash Display contents from Flash memory
 *
 * \brief Display functions enabling flash memory contents
 *
 * For microcontroller-based software development, memory usage is a crucial part.
 * The SRAM on the Atmega 328PB (the processor of the Xplained Mini / SPiCboard v3)
 * is extremly tight compared to PCs -- its 2 KB are the limiting factor for
 * using the 128×64 pixel monochrome OLED display:
 * Just the contents of a single screen (128×64 bits) would consume half of
 * the available SRAM!
 *
 * But since most data is constant, this can be stored and read from the flash
 * program memory (with 32 KB on this microprocessor it is sixteen times the SRAM).
 *
 * Due to the different address space, it is not possible to access the data in flash
 * in the same way you access SRAM contents -- a special instruction is required to
 * read from this memory: the \c ldm instruction (instead of \c ld).
 *
 * This will be done automatically by the compiler when using the
 * <a href="https://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Named-Address-Spaces.html">Named-Address-Space</a>
 * keyword \c __flash in the variable declaration.
 *
 * In case you have quite an old compiler not supporting this feature,
 * you have to use the <a href="http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html">Program Space Utilities</a>
 * from the <a href="http://www.nongnu.org/avr-libc/">AVR LibC</a>
 * and not only tag the variables with the \c PROGMEM keywords but although use the \c pgm_read_byte function to access them.
 *
 * The following functions use the same parameters (besides the \c flash keyword)
 * as their counter parts and their function name is appended by \c FromFlash
 *
 * @{
 * \file display.h
 * \version \$Rev:  $
 */

 /**
 * \brief Draw a bitmap from the flash memory onto the OLED display
 *
 * Almost the same function as sb_display_drawBitmap(), but the contents are
 * read from the flash (program memory) - so no SRAM is wasted.
 *
 * \see sb_display_drawBitmap
 *
 * \param pageStart  first page to set the top vertical position
 * \param colStart   first column to set the left horizontal position
 * \param pageCount  number of pages to set the bottom position
 * \param colCount   number of columns to define the right border
 * \param contents   array (<tt>pageCount * colCount</tt> elements) pointing to a bitmap
 *                   stored in the flash memory (using either
 *                   <a href="https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Named-Address-Spaces.html">__flash</a>
 *                   namespace or <a href="http://www.nongnu.org/avr-libc/user-manual/pgmspace.html">PROGMEM</a> macro)
 *
 * \retval 0   success
 * \retval <0  on I²C error (see sb_display_enable() return codes)
 */
#ifdef __FLASH
int8_t sb_display_drawBitmapFromFlash(uint8_t pageStart, uint8_t colStart, uint8_t pageCount, uint8_t colCount, const __flash uint8_t *contents);
#else
int8_t sb_display_drawBitmapFromFlash(uint8_t pageStart, uint8_t colStart, uint8_t pageCount, uint8_t colCount, const uint8_t * PROGMEM contents);
#endif

/**
 * \brief Draw the contents of the flash to the entire OLED display screen
 *
 * Almost same function as sb_display_fillScreen(), but the contents are
 * read from the flash (program memory) - so no SRAM is wasted.
 *
 * The function call is identical to
 * \code {.c}
 *	sb_display_drawBitmapFromFlash(0, 0, 8, 128, contents);
 * \endcode
 *
 * \param contents  pointer to a \c 8*128 item array in flash memory (using either
 *                  <a href="https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Named-Address-Spaces.html">__flash</a>
 *                  namespace or <a href="http://www.nongnu.org/avr-libc/user-manual/pgmspace.html">PROGMEM</a> macro)
 *
 * \retval 0   success
 * \retval <0  on I²C error (see sb_display_enable() return codes)
 */
#ifdef __FLASH
int8_t sb_display_fillScreenFromFlash(const __flash uint8_t *contents);
#else
int8_t sb_display_fillScreenFromFlash(const uint8_t * PROGMEM contents);
#endif

/**
 * \brief Print a \c \0 terminated text string from flash
 *        on the OLED display using a 8×8 pixel font
 *
 * Almost same function as sb_display_showString(), but the string is
 * read from the flash (program memory) - so no SRAM is wasted.
 *
 * The \ref fonts "Fonts Table" contains a detailed list of the available characters
 *
 * It is possible to define an inline flash string using the \c PSTR macro from the
 * <a href="http://www.nongnu.org/avr-libc/user-manual/pgmspace.html">Program Space Utilities</a>:
 * \code {.c}
 * static const __flash str[] = "Ipsum";
 *	sb_display_showString(0, 0, "Lorem");
 *	sb_display_showStringFromFlash(1, 0, str);
 *	sb_display_showStringFromFlash(2, 0, PSTR("dolor"));
 * \endcode
 * This will display \a Lorem , \a Ipsum and \a dolor on the subsequent lines,
 * but only the first string will consume SRAM during runtime.
 * The second and third call are different ways for reading from flash memory.
 *
 * \warning The font will be stored on the flash which will consume about 2300 bytes
 *          (but there is no additional overhead in case you are already
 *          using the sb_display_showString() ).
 *
 * \param pageNum   page number (0-7) for vertical positioning the output
 * \param colStart  first column to set the horizontal position of the output
 * \param str       0-terminated string in flash memory
 *
 * \retval >=0  length of printed string (can be less than strlen(str) )
 * \retval <0   on I²C error (\see sb_display_enable() return codes)
 */
#ifdef __FLASH
int8_t sb_display_showStringFromFlash(uint8_t pageNum, uint8_t colStart, const __flash char *str);
#else
int8_t sb_display_showStringFromFlash(uint8_t pageNum, uint8_t colStart, const char * PROGMEM str);
#endif

/**
 * \brief Print a \c \0 terminated text string from flash
 *        with a (slightly) wider 8×8 pixel font onto the OLED display
 *
 * Almost same function as sb_display_showStringWide(), but the string is
 * read from the flash (program memory) - so no SRAM is wasted.
 *
 * The \ref fonts "Fonts Table" contains a detailed list of the
 * available characters.
 *
 * \warning This font will consume about 2300 bytes, too (but no additional
 *          overhead for using sb_display_showStringWide() ).
 *
 * \param pageNum   page number (0-7) for vertical positioning the output
 * \param colStart  first column to set the horizontal position of the output
 * \param str       0-terminated string in flash memory
 *
 * \retval >=0  length of printed string (can be less than strlen(str) )
 * \retval <0   on I²C error (see sb_display_enable() return codes)
 */
#ifdef __FLASH
int8_t sb_display_showStringWideFromFlash(uint8_t pageNum, uint8_t colStart, const __flash char *str);
#else
int8_t sb_display_showStringWideFromFlash(uint8_t pageNum, uint8_t colStart, const char * PROGMEM str);
#endif

/**
 * \brief Print a \0 terminated text string from flash
 *        on the OLED display using a small 6×4 pixel font
 *
 * Almost same function as sb_display_showStringSmall(), but the string is
 * read from the flash (program memory) - so no SRAM is wasted.
 *
 * The \ref fonts "Fonts Table" contains a detailed list of the
 * available characters
 *
 * \warning This font will consume about 700 bytes (but no additional
 *          overhead for using sb_display_showStringSmall() ).
 *
 * \param pageNum   page number (0-7) for vertical positioning the output
 * \param colStart  first column to set the horizontal position of the output
 * \param str       0-terminated string from flash
 *
 * \retval >=0  length of printed string (can be less than strlen(str) )
 * \retval <0   on I²C error (\see sb_display_enable() return codes)
 */
#ifdef __FLASH
int8_t sb_display_showStringSmallFromFlash(uint8_t pageNum, uint8_t colStart, const __flash char *str);
#else
int8_t sb_display_showStringSmallFromFlash(uint8_t pageNum, uint8_t colStart, const char * PROGMEM str);
#endif

/** @}*/

#endif

