/*

  u8g2_font.c

  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)

  Copyright (c) 2016, olikraus@gmail.com
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, 
  are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice, this list 
    of conditions and the following disclaimer.
    
  * Redistributions in binary form must reproduce the above copyright notice, this 
    list of conditions and the following disclaimer in the documentation and/or other 
    materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  

*/

#include "u8g2.h"

/* size of the font data structure, there is no struct or class... */
/* this is the size for the new font format */
#define U8G2_FONT_DATA_STRUCT_SIZE 23

/*
  font data:

  offset	bytes	description
  0		1		glyph_cnt		number of glyphs
  1		1		bbx_mode	0: proportional, 1: common height, 2: monospace, 3: multiple of 8
  2		1		bits_per_0	glyph rle parameter
  3		1		bits_per_1	glyph rle parameter

  4		1		bits_per_char_width		glyph rle parameter
  5		1		bits_per_char_height	glyph rle parameter
  6		1		bits_per_char_x		glyph rle parameter
  7		1		bits_per_char_y		glyph rle parameter
  8		1		bits_per_delta_x		glyph rle parameter

  9		1		max_char_width
  10		1		max_char_height
  11		1		x offset
  12		1		y offset (descent)
  
  13		1		ascent (capital A)
  14		1		descent (lower g)
  15		1		ascent '('
  16		1		descent ')'
  
  17		1		start pos 'A' high byte
  18		1		start pos 'A' low byte

  19		1		start pos 'a' high byte
  20		1		start pos 'a' low byte

  21		1		start pos unicode high byte
  22		1		start pos unicode low byte

  Font build mode, 0: proportional, 1: common height, 2: monospace, 3: multiple of 8

  Font build mode 0:		
    - "t"
    - Ref height mode: U8G2_FONT_HEIGHT_MODE_TEXT, U8G2_FONT_HEIGHT_MODE_XTEXT or U8G2_FONT_HEIGHT_MODE_ALL
    - use in transparent mode only (does not look good in solid mode)
    - most compact format
    - different font heights possible
    
  Font build mode 1:		
    - "h"
    - Ref height mode: U8G2_FONT_HEIGHT_MODE_ALL
    - transparent or solid mode
    - The height of the glyphs depend on the largest glyph in the font. This means font height depends on postfix "r", "f" and "n".

*/

/* use case: What is the width and the height of the minimal box into which string s fints? */
void u8g2_font_GetStrSize(const void* font, const char* s, u8g2_uint_t* width, u8g2_uint_t* height);
void u8g2_font_GetStrSizeP(
    const void* font,
    const char* s,
    u8g2_uint_t* width,
    u8g2_uint_t* height);

/* use case: lower left edge of a minimal box is known, what is the correct x, y position for the string draw procedure */
void u8g2_font_AdjustXYToDraw(const void* font, const char* s, u8g2_uint_t* x, u8g2_uint_t* y);
void u8g2_font_AdjustXYToDrawP(const void* font, const char* s, u8g2_uint_t* x, u8g2_uint_t* y);

/* use case: Baseline origin known, return minimal box */
void u8g2_font_GetStrMinBox(
    u8g2_t* u8g2,
    const void* font,
    const char* s,
    u8g2_uint_t* x,
    u8g2_uint_t* y,
    u8g2_uint_t* width,
    u8g2_uint_t* height);

/* procedures */

/*========================================================================*/
/* low level byte and word access */

/* removed NOINLINE, because it leads to smaller code, might also be faster */
//static uint8_t u8g2_font_get_byte(const uint8_t *font, uint8_t offset) U8G2_NOINLINE;
static uint8_t u8g2_font_get_byte(const uint8_t* font, uint8_t offset) {
    font += offset;
    return u8x8_pgm_read(font);
}

static uint16_t u8g2_font_get_word(const uint8_t* font, uint8_t offset) U8G2_NOINLINE;
static uint16_t u8g2_font_get_word(const uint8_t* font, uint8_t offset) {
    uint16_t pos;
    font += offset;
    pos = u8x8_pgm_read(font);
    font++;
    pos <<= 8;
    pos += u8x8_pgm_read(font);
    return pos;
}

/*========================================================================*/
/* new font format */
void u8g2_read_font_info(u8g2_font_info_t* font_info, const uint8_t* font) {
    /* offset 0 */
    font_info->glyph_cnt = u8g2_font_get_byte(font, 0);
    font_info->bbx_mode = u8g2_font_get_byte(font, 1);
    font_info->bits_per_0 = u8g2_font_get_byte(font, 2);
    font_info->bits_per_1 = u8g2_font_get_byte(font, 3);

    /* offset 4 */
    font_info->bits_per_char_width = u8g2_font_get_byte(font, 4);
    font_info->bits_per_char_height = u8g2_font_get_byte(font, 5);
    font_info->bits_per_char_x = u8g2_font_get_byte(font, 6);
    font_info->bits_per_char_y = u8g2_font_get_byte(font, 7);
    font_info->bits_per_delta_x = u8g2_font_get_byte(font, 8);

    /* offset 9 */
    font_info->max_char_width = u8g2_font_get_byte(font, 9);
    font_info->max_char_height = u8g2_font_get_byte(font, 10);
    font_info->x_offset = u8g2_font_get_byte(font, 11);
    font_info->y_offset = u8g2_font_get_byte(font, 12);

    /* offset 13 */
    font_info->ascent_A = u8g2_font_get_byte(font, 13);
    font_info->descent_g = u8g2_font_get_byte(font, 14);
    font_info->ascent_para = u8g2_font_get_byte(font, 15);
    font_info->descent_para = u8g2_font_get_byte(font, 16);

    /* offset 17 */
    font_info->start_pos_upper_A = u8g2_font_get_word(font, 17);
    font_info->start_pos_lower_a = u8g2_font_get_word(font, 19);

    /* offset 21 */
#ifdef U8G2_WITH_UNICODE
    font_info->start_pos_unicode = u8g2_font_get_word(font, 21);
#endif
}

/* calculate the overall length of the font, only used to create the picture for the google wiki */
size_t u8g2_GetFontSize(const uint8_t* font_arg) {
    uint16_t e;
    const uint8_t* font = font_arg;
    font += U8G2_FONT_DATA_STRUCT_SIZE;

    for(;;) {
        if(u8x8_pgm_read(font + 1) == 0) break;
        font += u8x8_pgm_read(font + 1);
    }

    /* continue with unicode section */
    font += 2;

    /* skip unicode lookup table */
    font += u8g2_font_get_word(font, 0);

    for(;;) {
        e = u8x8_pgm_read(font);
        e <<= 8;
        e |= u8x8_pgm_read(font + 1);
        if(e == 0) break;
        font += u8x8_pgm_read(font + 2);
    }

    return (font - font_arg) + 2;
}

/*========================================================================*/
/* u8g2 interface, font access */

uint8_t u8g2_GetFontBBXWidth(u8g2_t* u8g2) {
    return u8g2->font_info.max_char_width; /* new font info structure */
}

uint8_t u8g2_GetFontBBXHeight(u8g2_t* u8g2) {
    return u8g2->font_info.max_char_height; /* new font info structure */
}

int8_t u8g2_GetFontBBXOffX(u8g2_t* u8g2) U8G2_NOINLINE;
int8_t u8g2_GetFontBBXOffX(u8g2_t* u8g2) {
    return u8g2->font_info.x_offset; /* new font info structure */
}

int8_t u8g2_GetFontBBXOffY(u8g2_t* u8g2) U8G2_NOINLINE;
int8_t u8g2_GetFontBBXOffY(u8g2_t* u8g2) {
    return u8g2->font_info.y_offset; /* new font info structure */
}

uint8_t u8g2_GetFontCapitalAHeight(u8g2_t* u8g2) U8G2_NOINLINE;
uint8_t u8g2_GetFontCapitalAHeight(u8g2_t* u8g2) {
    return u8g2->font_info.ascent_A; /* new font info structure */
}

/*========================================================================*/
/* glyph handling */

/* optimized */
uint8_t u8g2_font_decode_get_unsigned_bits(u8g2_font_decode_t* f, uint8_t cnt) {
    uint8_t val;
    uint8_t bit_pos = f->decode_bit_pos;
    uint8_t bit_pos_plus_cnt;

    //val = *(f->decode_ptr);
    val = u8x8_pgm_read(f->decode_ptr);

    val >>= bit_pos;
    bit_pos_plus_cnt = bit_pos;
    bit_pos_plus_cnt += cnt;
    if(bit_pos_plus_cnt >= 8) {
        uint8_t s = 8;
        s -= bit_pos;
        f->decode_ptr++;
        //val |= *(f->decode_ptr) << (8-bit_pos);
        val |= u8x8_pgm_read(f->decode_ptr) << (s);
        //bit_pos -= 8;
        bit_pos_plus_cnt -= 8;
    }
    val &= (1U << cnt) - 1;
    //bit_pos += cnt;

    f->decode_bit_pos = bit_pos_plus_cnt;
    return val;
}

/*
    2 bit --> cnt = 2
      -2,-1,0. 1

    3 bit --> cnt = 3
      -2,-1,0. 1
      -4,-3,-2,-1,0,1,2,3

      if ( x < 0 )
	r = bits(x-1)+1;
    else
	r = bits(x)+1;

*/
/* optimized */
int8_t u8g2_font_decode_get_signed_bits(u8g2_font_decode_t* f, uint8_t cnt) {
    int8_t v, d;
    v = (int8_t)u8g2_font_decode_get_unsigned_bits(f, cnt);
    d = 1;
    cnt--;
    d <<= cnt;
    v -= d;
    return v;
    //return (int8_t)u8g2_font_decode_get_unsigned_bits(f, cnt) - ((1<<cnt)>>1);
}

#ifdef U8G2_WITH_FONT_ROTATION
u8g2_uint_t u8g2_add_vector_y(u8g2_uint_t dy, int8_t x, int8_t y, uint8_t dir) {
    switch(dir) {
    case 0:
        dy += y;
        break;
    case 1:
        dy += x;
        break;
    case 2:
        dy -= y;
        break;
    default:
        dy -= x;
        break;
    }
    return dy;
}

u8g2_uint_t u8g2_add_vector_x(u8g2_uint_t dx, int8_t x, int8_t y, uint8_t dir) {
    switch(dir) {
    case 0:
        dx += x;
        break;
    case 1:
        dx -= y;
        break;
    case 2:
        dx -= x;
        break;
    default:
        dx += y;
        break;
    }
    return dx;
}

/*
// does not make sense, 50 bytes more required on avr
void u8g2_add_vector(u8g2_uint_t *xp, u8g2_uint_t *yp, int8_t x, int8_t y, uint8_t dir)
{
  u8g2_uint_t x_ = *xp;
  u8g2_uint_t y_ = *yp;
  switch(dir)
  {
    case 0:
      y_ += y;
      x_ += x;
      break;
    case 1:
      y_ += x;
      x_ -= y;
      break;
    case 2:
      y_ -= y;
      x_ -= x;
      break;
    default:
      y_ -= x;
      x_ += y;
      break;      
  }
  *xp = x_;
  *yp = y_;
}
*/
#endif

/*
  Description:
    Draw a run-length area of the glyph. "len" can have any size and the line
    length has to be wrapped at the glyph border.
  Args:
    len: 					Length of the line
    is_foreground			foreground/background?
    u8g2->font_decode.target_x		X position
    u8g2->font_decode.target_y		Y position
    u8g2->font_decode.is_transparent	Transparent mode
  Return:
    -
  Calls:
    u8g2_Draw90Line()
  Called by:
    u8g2_font_decode_glyph()
*/
/* optimized */
void u8g2_font_decode_len(u8g2_t* u8g2, uint8_t len, uint8_t is_foreground) {
    uint8_t cnt; /* total number of remaining pixels, which have to be drawn */
    uint8_t rem; /* remaining pixel to the right edge of the glyph */
    uint8_t current; /* number of pixels, which need to be drawn for the draw procedure */
    /* current is either equal to cnt or equal to rem */

    /* local coordinates of the glyph */
    uint8_t lx, ly;

    /* target position on the screen */
    u8g2_uint_t x, y;

    u8g2_font_decode_t* decode = &(u8g2->font_decode);

    cnt = len;

    /* get the local position */
    lx = decode->x;
    ly = decode->y;

    for(;;) {
        /* calculate the number of pixel to the right edge of the glyph */
        rem = decode->glyph_width;
        rem -= lx;

        /* calculate how many pixel to draw. This is either to the right edge */
        /* or lesser, if not enough pixel are left */
        current = rem;
        if(cnt < rem) current = cnt;

        /* now draw the line, but apply the rotation around the glyph target position */
        //u8g2_font_decode_draw_pixel(u8g2, lx,ly,current, is_foreground);

        /* get target position */
        x = decode->target_x;
        y = decode->target_y;

        /* apply rotation */
#ifdef U8G2_WITH_FONT_ROTATION

        x = u8g2_add_vector_x(x, lx, ly, decode->dir);
        y = u8g2_add_vector_y(y, lx, ly, decode->dir);

        //u8g2_add_vector(&x, &y, lx, ly, decode->dir);

#else
        x += lx;
        y += ly;
#endif

        /* draw foreground and background (if required) */
        if(is_foreground) {
            u8g2->draw_color = decode->fg_color; /* draw_color will be restored later */
            u8g2_DrawHVLine(
                u8g2,
                x,
                y,
                current,
#ifdef U8G2_WITH_FONT_ROTATION
                /* dir */ decode->dir
#else
                0
#endif
            );
        } else if(decode->is_transparent == 0) {
            u8g2->draw_color = decode->bg_color; /* draw_color will be restored later */
            u8g2_DrawHVLine(
                u8g2,
                x,
                y,
                current,
#ifdef U8G2_WITH_FONT_ROTATION
                /* dir */ decode->dir
#else
                0
#endif
            );
        }

        /* check, whether the end of the run length code has been reached */
        if(cnt < rem) break;
        cnt -= rem;
        lx = 0;
        ly++;
    }
    lx += cnt;

    decode->x = lx;
    decode->y = ly;
}

static void u8g2_font_setup_decode(u8g2_t* u8g2, const uint8_t* glyph_data) {
    u8g2_font_decode_t* decode = &(u8g2->font_decode);
    decode->decode_ptr = glyph_data;
    decode->decode_bit_pos = 0;

    /* 8 Nov 2015, this is already done in the glyph data search procedure */
    /*
  decode->decode_ptr += 1;
  decode->decode_ptr += 1;
  */

    decode->glyph_width =
        u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_char_width);
    decode->glyph_height =
        u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_char_height);

    decode->fg_color = u8g2->draw_color;
    decode->bg_color = (decode->fg_color == 0 ? 1 : 0);
}

/*
  Description:
    Decode and draw a glyph.
  Args:
    glyph_data: 					Pointer to the compressed glyph data of the font
    u8g2->font_decode.target_x		X position
    u8g2->font_decode.target_y		Y position
    u8g2->font_decode.is_transparent	Transparent mode
  Return:
    Width (delta x advance) of the glyph.
  Calls:
    u8g2_font_decode_len()
*/
/* optimized */
int8_t u8g2_font_decode_glyph(u8g2_t* u8g2, const uint8_t* glyph_data) {
    uint8_t a, b;
    int8_t x, y;
    int8_t d;
    int8_t h;
    u8g2_font_decode_t* decode = &(u8g2->font_decode);

    u8g2_font_setup_decode(u8g2, glyph_data);
    h = u8g2->font_decode.glyph_height;

    x = u8g2_font_decode_get_signed_bits(decode, u8g2->font_info.bits_per_char_x);
    y = u8g2_font_decode_get_signed_bits(decode, u8g2->font_info.bits_per_char_y);
    d = u8g2_font_decode_get_signed_bits(decode, u8g2->font_info.bits_per_delta_x);

    if(decode->glyph_width > 0) {
#ifdef U8G2_WITH_FONT_ROTATION
        decode->target_x = u8g2_add_vector_x(decode->target_x, x, -(h + y), decode->dir);
        decode->target_y = u8g2_add_vector_y(decode->target_y, x, -(h + y), decode->dir);

        //u8g2_add_vector(&(decode->target_x), &(decode->target_y), x, -(h+y), decode->dir);

#else
        decode->target_x += x;
        decode->target_y -= h + y;
#endif
        //u8g2_add_vector(&(decode->target_x), &(decode->target_y), x, -(h+y), decode->dir);

#ifdef U8G2_WITH_INTERSECTION
        {
            u8g2_uint_t x0, x1, y0, y1;
            x0 = decode->target_x;
            y0 = decode->target_y;
            x1 = x0;
            y1 = y0;

#ifdef U8G2_WITH_FONT_ROTATION
            switch(decode->dir) {
            case 0:
                x1 += decode->glyph_width;
                y1 += h;
                break;
            case 1:
                x0 -= h;
                x0++; /* shift down, because of assymetric boundaries for the interseciton test */
                x1++;
                y1 += decode->glyph_width;
                break;
            case 2:
                x0 -= decode->glyph_width;
                x0++; /* shift down, because of assymetric boundaries for the interseciton test */
                x1++;
                y0 -= h;
                y0++; /* shift down, because of assymetric boundaries for the interseciton test */
                y1++;
                break;
            case 3:
                x1 += h;
                y0 -= decode->glyph_width;
                y0++; /* shift down, because of assymetric boundaries for the interseciton test */
                y1++;
                break;
            }
#else /* U8G2_WITH_FONT_ROTATION */
            x1 += decode->glyph_width;
            y1 += h;
#endif

            if(u8g2_IsIntersection(u8g2, x0, y0, x1, y1) == 0) return d;
        }
#endif /* U8G2_WITH_INTERSECTION */

        /* reset local x/y position */
        decode->x = 0;
        decode->y = 0;

        /* decode glyph */
        for(;;) {
            a = u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_0);
            b = u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_1);
            do {
                u8g2_font_decode_len(u8g2, a, 0);
                u8g2_font_decode_len(u8g2, b, 1);
            } while(u8g2_font_decode_get_unsigned_bits(decode, 1) != 0);

            if(decode->y >= h) break;
        }

        /* restore the u8g2 draw color, because this is modified by the decode algo */
        u8g2->draw_color = decode->fg_color;
    }
    return d;
}

/*
  Description:
    Find the starting point of the glyph data.
  Args:
    encoding: Encoding (ASCII or Unicode) of the glyph
  Return:
    Address of the glyph data or NULL, if the encoding is not avialable in the font.
*/
const uint8_t* u8g2_font_get_glyph_data(u8g2_t* u8g2, uint16_t encoding) {
    const uint8_t* font = u8g2->font;
    font += U8G2_FONT_DATA_STRUCT_SIZE;

    if(encoding <= 255) {
        if(encoding >= 'a') {
            font += u8g2->font_info.start_pos_lower_a;
        } else if(encoding >= 'A') {
            font += u8g2->font_info.start_pos_upper_A;
        }

        for(;;) {
            if(u8x8_pgm_read(font + 1) == 0) break;
            if(u8x8_pgm_read(font) == encoding) {
                return font + 2; /* skip encoding and glyph size */
            }
            font += u8x8_pgm_read(font + 1);
        }
    }
#ifdef U8G2_WITH_UNICODE
    else {
        uint16_t e;
        const uint8_t* unicode_lookup_table;

        // removed, there is now the new index table
        //#ifdef  __unix__
        //    if ( u8g2->last_font_data != NULL && encoding >= u8g2->last_unicode )
        //    {
        //	font = u8g2->last_font_data;
        //    }
        //    else
        //#endif

        font += u8g2->font_info.start_pos_unicode;
        unicode_lookup_table = font;

        /* issue 596: search for the glyph start in the unicode lookup table */
        do {
            font += u8g2_font_get_word(unicode_lookup_table, 0);
            e = u8g2_font_get_word(unicode_lookup_table, 2);
            unicode_lookup_table += 4;
        } while(e < encoding);

        for(;;) {
            e = u8x8_pgm_read(font);
            e <<= 8;
            e |= u8x8_pgm_read(font + 1);

            // removed, there is now the new index table
            //#ifdef  __unix__
            //      if ( encoding < e )
            //        break;
            //#endif

            if(e == 0) break;

            if(e == encoding) {
                // removed, there is now the new index table
                //#ifdef  __unix__
                //	u8g2->last_font_data = font;
                //	u8g2->last_unicode = encoding;
                //#endif
                return font + 3; /* skip encoding and glyph size */
            }
            font += u8x8_pgm_read(font + 2);
        }
    }
#endif

    return NULL;
}

static u8g2_uint_t
    u8g2_font_draw_glyph(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding) {
    u8g2_uint_t dx = 0;
    u8g2->font_decode.target_x = x;
    u8g2->font_decode.target_y = y;
    //u8g2->font_decode.is_transparent = is_transparent; this is already set
    //u8g2->font_decode.dir = dir;
    const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, encoding);
    if(glyph_data != NULL) {
        dx = u8g2_font_decode_glyph(u8g2, glyph_data);
    }
    return dx;
}

uint8_t u8g2_IsGlyph(u8g2_t* u8g2, uint16_t requested_encoding) {
    /* updated to new code */
    if(u8g2_font_get_glyph_data(u8g2, requested_encoding) != NULL) return 1;
    return 0;
}

/* side effect: updates u8g2->font_decode and u8g2->glyph_x_offset */
int8_t u8g2_GetGlyphWidth(u8g2_t* u8g2, uint16_t requested_encoding) {
    const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, requested_encoding);
    if(glyph_data == NULL) return 0;

    u8g2_font_setup_decode(u8g2, glyph_data);
    u8g2->glyph_x_offset =
        u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_x);
    u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_y);

    /* glyph width is here: u8g2->font_decode.glyph_width */

    return u8g2_font_decode_get_signed_bits(
        &(u8g2->font_decode), u8g2->font_info.bits_per_delta_x);
}

/*
  set one of:
    U8G2_FONT_MODE_TRANSPARENT
    U8G2_FONT_MODE_SOLID
    U8G2_FONT_MODE_NONE
  This has been changed for the new font procedures  
*/
void u8g2_SetFontMode(u8g2_t* u8g2, uint8_t is_transparent) {
    u8g2->font_decode.is_transparent = is_transparent; // new font procedures
}

u8g2_uint_t u8g2_DrawGlyph(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding) {
#ifdef U8G2_WITH_FONT_ROTATION
    switch(u8g2->font_decode.dir) {
    case 0:
        y += u8g2->font_calc_vref(u8g2);
        break;
    case 1:
        x -= u8g2->font_calc_vref(u8g2);
        break;
    case 2:
        y -= u8g2->font_calc_vref(u8g2);
        break;
    case 3:
        x += u8g2->font_calc_vref(u8g2);
        break;
    }
#else
    y += u8g2->font_calc_vref(u8g2);
#endif
    return u8g2_font_draw_glyph(u8g2, x, y, encoding);
}

static u8g2_uint_t
    u8g2_draw_string(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) U8G2_NOINLINE;
static u8g2_uint_t u8g2_draw_string(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) {
    uint16_t e;
    u8g2_uint_t delta, sum;
    u8x8_utf8_init(u8g2_GetU8x8(u8g2));
    sum = 0;
    for(;;) {
        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);
        if(e == 0x0ffff) break;
        str++;
        if(e != 0x0fffe) {
            delta = u8g2_DrawGlyph(u8g2, x, y, e);

#ifdef U8G2_WITH_FONT_ROTATION
            switch(u8g2->font_decode.dir) {
            case 0:
                x += delta;
                break;
            case 1:
                y += delta;
                break;
            case 2:
                x -= delta;
                break;
            case 3:
                y -= delta;
                break;
            }

            /*
      // requires 10 bytes more on avr
      x = u8g2_add_vector_x(x, delta, 0, u8g2->font_decode.dir);
      y = u8g2_add_vector_y(y, delta, 0, u8g2->font_decode.dir);
      */

#else
            x += delta;
#endif

            sum += delta;
        }
    }
    return sum;
}

u8g2_uint_t u8g2_DrawStr(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) {
    u8g2->u8x8.next_cb = u8x8_ascii_next;
    return u8g2_draw_string(u8g2, x, y, str);
}

/*
source: https://en.wikipedia.org/wiki/UTF-8
Bits	from 		to			bytes	Byte 1 		Byte 2 		Byte 3 		Byte 4 		Byte 5 		Byte 6
  7 	U+0000 		U+007F 		1 		0xxxxxxx
11 	U+0080 		U+07FF 		2 		110xxxxx 	10xxxxxx
16 	U+0800 		U+FFFF 		3 		1110xxxx 	10xxxxxx 	10xxxxxx
21 	U+10000 	U+1FFFFF 	4 		11110xxx 	10xxxxxx 	10xxxxxx 	10xxxxxx
26 	U+200000 	U+3FFFFFF 	5 		111110xx 	10xxxxxx 	10xxxxxx 	10xxxxxx 	10xxxxxx
31 	U+4000000 	U+7FFFFFFF 	6 		1111110x 	10xxxxxx 	10xxxxxx 	10xxxxxx 	10xxxxxx 	10xxxxxx  
*/
u8g2_uint_t u8g2_DrawUTF8(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) {
    u8g2->u8x8.next_cb = u8x8_utf8_next;
    return u8g2_draw_string(u8g2, x, y, str);
}

u8g2_uint_t u8g2_DrawExtendedUTF8(
    u8g2_t* u8g2,
    u8g2_uint_t x,
    u8g2_uint_t y,
    uint8_t to_left,
    u8g2_kerning_t* kerning,
    const char* str) {
    u8g2->u8x8.next_cb = u8x8_utf8_next;
    uint16_t e_prev = 0x0ffff;
    uint16_t e;
    u8g2_uint_t delta, sum, k;
    u8x8_utf8_init(u8g2_GetU8x8(u8g2));
    sum = 0;
    for(;;) {
        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);
        if(e == 0x0ffff) break;
        str++;
        if(e != 0x0fffe) {
            delta = u8g2_GetGlyphWidth(u8g2, e);

            if(to_left) {
                k = u8g2_GetKerning(u8g2, kerning, e, e_prev);
                delta -= k;
                x -= delta;
            } else {
                k = u8g2_GetKerning(u8g2, kerning, e_prev, e);
                delta -= k;
            }
            e_prev = e;

            u8g2_DrawGlyph(u8g2, x, y, e);
            if(to_left) {
            } else {
                x += delta;
                x -= k;
            }

            sum += delta;
        }
    }
    return sum;
}

u8g2_uint_t u8g2_DrawExtUTF8(
    u8g2_t* u8g2,
    u8g2_uint_t x,
    u8g2_uint_t y,
    uint8_t to_left,
    const uint16_t* kerning_table,
    const char* str) {
    u8g2->u8x8.next_cb = u8x8_utf8_next;
    uint16_t e_prev = 0x0ffff;
    uint16_t e;
    u8g2_uint_t delta, sum, k;
    u8x8_utf8_init(u8g2_GetU8x8(u8g2));
    sum = 0;
    for(;;) {
        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);
        if(e == 0x0ffff) break;
        str++;
        if(e != 0x0fffe) {
            delta = u8g2_GetGlyphWidth(u8g2, e);

            if(to_left) {
                k = u8g2_GetKerningByTable(u8g2, kerning_table, e, e_prev);
                delta -= k;
                x -= delta;
            } else {
                k = u8g2_GetKerningByTable(u8g2, kerning_table, e_prev, e);
                delta -= k;
            }
            e_prev = e;

            if(to_left) {
            } else {
                x += delta;
            }
            u8g2_DrawGlyph(u8g2, x, y, e);
            if(to_left) {
            } else {
                //x += delta;
                //x -= k;
            }

            sum += delta;
        }
    }
    return sum;
}

/*===============================================*/

/* set ascent/descent for reference point calculation */

void u8g2_UpdateRefHeight(u8g2_t* u8g2) {
    if(u8g2->font == NULL) return;
    u8g2->font_ref_ascent = u8g2->font_info.ascent_A;
    u8g2->font_ref_descent = u8g2->font_info.descent_g;
    if(u8g2->font_height_mode == U8G2_FONT_HEIGHT_MODE_TEXT) {
    } else if(u8g2->font_height_mode == U8G2_FONT_HEIGHT_MODE_XTEXT) {
        if(u8g2->font_ref_ascent < u8g2->font_info.ascent_para)
            u8g2->font_ref_ascent = u8g2->font_info.ascent_para;
        if(u8g2->font_ref_descent > u8g2->font_info.descent_para)
            u8g2->font_ref_descent = u8g2->font_info.descent_para;
    } else {
        if(u8g2->font_ref_ascent < u8g2->font_info.max_char_height + u8g2->font_info.y_offset)
            u8g2->font_ref_ascent = u8g2->font_info.max_char_height + u8g2->font_info.y_offset;
        if(u8g2->font_ref_descent > u8g2->font_info.y_offset)
            u8g2->font_ref_descent = u8g2->font_info.y_offset;
    }
}

void u8g2_SetFontRefHeightText(u8g2_t* u8g2) {
    u8g2->font_height_mode = U8G2_FONT_HEIGHT_MODE_TEXT;
    u8g2_UpdateRefHeight(u8g2);
}

void u8g2_SetFontRefHeightExtendedText(u8g2_t* u8g2) {
    u8g2->font_height_mode = U8G2_FONT_HEIGHT_MODE_XTEXT;
    u8g2_UpdateRefHeight(u8g2);
}

void u8g2_SetFontRefHeightAll(u8g2_t* u8g2) {
    u8g2->font_height_mode = U8G2_FONT_HEIGHT_MODE_ALL;
    u8g2_UpdateRefHeight(u8g2);
}

/*===============================================*/
/* callback procedures to correct the y position */

u8g2_uint_t u8g2_font_calc_vref_font(U8X8_UNUSED u8g2_t* u8g2) {
    return 0;
}

void u8g2_SetFontPosBaseline(u8g2_t* u8g2) {
    u8g2->font_calc_vref = u8g2_font_calc_vref_font;
}

u8g2_uint_t u8g2_font_calc_vref_bottom(u8g2_t* u8g2) {
    return (u8g2_uint_t)(u8g2->font_ref_descent);
}

void u8g2_SetFontPosBottom(u8g2_t* u8g2) {
    u8g2->font_calc_vref = u8g2_font_calc_vref_bottom;
}

u8g2_uint_t u8g2_font_calc_vref_top(u8g2_t* u8g2) {
    u8g2_uint_t tmp;
    /* reference pos is one pixel above the upper edge of the reference glyph */
    tmp = (u8g2_uint_t)(u8g2->font_ref_ascent);
    tmp++;
    return tmp;
}

void u8g2_SetFontPosTop(u8g2_t* u8g2) {
    u8g2->font_calc_vref = u8g2_font_calc_vref_top;
}

u8g2_uint_t u8g2_font_calc_vref_center(u8g2_t* u8g2) {
    int8_t tmp;
    tmp = u8g2->font_ref_ascent;
    tmp -= u8g2->font_ref_descent;
    tmp /= 2;
    tmp += u8g2->font_ref_descent;
    return tmp;
}

void u8g2_SetFontPosCenter(u8g2_t* u8g2) {
    u8g2->font_calc_vref = u8g2_font_calc_vref_center;
}

/*===============================================*/

void u8g2_SetFont(u8g2_t* u8g2, const uint8_t* font) {
    if(u8g2->font != font) {
        //#ifdef  __unix__
        //	u8g2->last_font_data = NULL;
        //	u8g2->last_unicode = 0x0ffff;
        //#endif
        u8g2->font = font;
        u8g2_read_font_info(&(u8g2->font_info), font);
        u8g2_UpdateRefHeight(u8g2);
        /* u8g2_SetFontPosBaseline(u8g2); */ /* removed with issue 195 */
    }
}

/*===============================================*/

static uint8_t u8g2_is_all_valid(u8g2_t* u8g2, const char* str) U8G2_NOINLINE;
static uint8_t u8g2_is_all_valid(u8g2_t* u8g2, const char* str) {
    uint16_t e;
    u8x8_utf8_init(u8g2_GetU8x8(u8g2));
    for(;;) {
        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);
        if(e == 0x0ffff) break;
        str++;
        if(e != 0x0fffe) {
            if(u8g2_font_get_glyph_data(u8g2, e) == NULL) return 0;
        }
    }
    return 1;
}

uint8_t u8g2_IsAllValidUTF8(u8g2_t* u8g2, const char* str) {
    u8g2->u8x8.next_cb = u8x8_utf8_next;
    return u8g2_is_all_valid(u8g2, str);
}

/* string calculation is stilll not 100% perfect as it addes the initial string offset to the overall size */
static u8g2_long_t u8g2_string_width(u8g2_t* u8g2, const char* str) U8G2_NOINLINE;
static u8g2_long_t u8g2_string_width(u8g2_t* u8g2, const char* str) {
    uint16_t e;
    u8g2_uint_t dx;
    u8g2_long_t w;

    u8g2->font_decode.glyph_width = 0;
    u8x8_utf8_init(u8g2_GetU8x8(u8g2));

    /* reset the total width to zero, this will be expanded during calculation */
    w = 0;
    dx = 0;

    // printf("str=<%s>\n", str);

    for(;;) {
        e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);
        if(e == 0x0ffff) break;
        str++;
        if(e != 0x0fffe) {
            dx = u8g2_GetGlyphWidth(u8g2, e); /* delta x value of the glyph */
            w += dx;
        }
    }

    /* adjust the last glyph, check for issue #16: do not adjust if width is 0 */
    if(u8g2->font_decode.glyph_width != 0) {
        w -= dx;
        w += u8g2->font_decode
                 .glyph_width; /* the real pixel width of the glyph, sideeffect of GetGlyphWidth */
        /* issue #46: we have to add the x offset also */
        w += u8g2->glyph_x_offset; /* this value is set as a side effect of u8g2_GetGlyphWidth() */
    }
    // printf("w=%d \n", w);

    return w;
}

static void u8g2_GetGlyphHorizontalProperties(
    u8g2_t* u8g2,
    uint16_t requested_encoding,
    uint8_t* w,
    int8_t* ox,
    int8_t* dx) {
    const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, requested_encoding);
    if(glyph_data == NULL) return;

    u8g2_font_setup_decode(u8g2, glyph_data);
    *w = u8g2->font_decode.glyph_width;
    *ox = u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_x);
    u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_y);
    *dx = u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_delta_x);
}

/* u8g compatible GetStrX function */
int8_t u8g2_GetStrX(u8g2_t* u8g2, const char* s) {
    uint8_t w;
    int8_t ox, dx;
    u8g2_GetGlyphHorizontalProperties(u8g2, *s, &w, &ox, &dx);
    return ox;
}

static u8g2_uint_t u8g2_calculate_exact_string_width(u8g2_t* u8g2, const char* str) {
    u8g2_uint_t w;
    uint16_t enc;
    uint8_t gw;
    int8_t ox, dx;

    /* reset the total minimal width to zero, this will be expanded during calculation */
    w = 0;

    /* check for empty string, width is already 0 */
    do {
        enc = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);
        str++;
    } while(enc == 0x0fffe);

    if(enc == 0x0ffff) return w;

    /* get the glyph information of the first char. This must be valid, because we already checked for the empty string */
    /* if *s is not inside the font, then the cached parameters of the glyph are all zero */
    u8g2_GetGlyphHorizontalProperties(u8g2, enc, &gw, &ox, &dx);

    /* strlen(s) == 1:       width = width(s[0]) */
    /* strlen(s) == 2:       width = - offx(s[0]) + deltax(s[0]) + offx(s[1]) + width(s[1]) */
    /* strlen(s) == 3:       width = - offx(s[0]) + deltax(s[0]) + deltax(s[1]) + offx(s[2]) + width(s[2]) */

    /* assume that the string has size 2 or more, than start with negative offset-x */
    /* for string with size 1, this will be nullified after the loop */
    w = -ox;
    for(;;) {
        /* check and stop if the end of the string is reached */
        do {
            enc = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);
            str++;
        } while(enc == 0x0fffe);
        if(enc == 0x0ffff) break;

        u8g2_GetGlyphHorizontalProperties(u8g2, enc, &gw, &ox, &dx);

        /* if there are still more characters, add the delta to the next glyph */
        w += dx;
    }

    /* finally calculate the width of the last char */
    /* here is another exception, if the last char is a black, use the dx value instead */
    if(enc != ' ') {
        /* if g was not updated in the for loop (strlen() == 1), then the initial offset x gets removed */
        w += gw;
        w += ox;
    } else {
        w += dx;
    }

    return w;
}

u8g2_long_t u8g2_GetStrWidth(u8g2_t* u8g2, const char* s) {
    u8g2->u8x8.next_cb = u8x8_ascii_next;
    return u8g2_string_width(u8g2, s);
}

u8g2_uint_t u8g2_GetExactStrWidth(u8g2_t* u8g2, const char* s) {
    u8g2->u8x8.next_cb = u8x8_ascii_next;
    return u8g2_calculate_exact_string_width(u8g2, s);
}

/*
source: https://en.wikipedia.org/wiki/UTF-8
Bits	from 		to			bytes	Byte 1 		Byte 2 		Byte 3 		Byte 4 		Byte 5 		Byte 6
  7 	U+0000 		U+007F 		1 		0xxxxxxx
11 	U+0080 		U+07FF 		2 		110xxxxx 	10xxxxxx
16 	U+0800 		U+FFFF 		3 		1110xxxx 	10xxxxxx 	10xxxxxx
21 	U+10000 	U+1FFFFF 	4 		11110xxx 	10xxxxxx 	10xxxxxx 	10xxxxxx
26 	U+200000 	U+3FFFFFF 	5 		111110xx 	10xxxxxx 	10xxxxxx 	10xxxxxx 	10xxxxxx
31 	U+4000000 	U+7FFFFFFF 	6 		1111110x 	10xxxxxx 	10xxxxxx 	10xxxxxx 	10xxxxxx 	10xxxxxx  
*/
u8g2_uint_t u8g2_GetUTF8Width(u8g2_t* u8g2, const char* str) {
    u8g2->u8x8.next_cb = u8x8_utf8_next;
    return u8g2_string_width(u8g2, str);
}

void u8g2_SetFontDirection(u8g2_t* u8g2, uint8_t dir) {
#ifdef U8G2_WITH_FONT_ROTATION
    u8g2->font_decode.dir = dir;
#endif
}
