/**
 * @file lv_bidi.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include <stddef.h>
#include "lv_bidi.h"
#include "lv_txt.h"
#include "../misc/lv_mem.h"

#if LV_USE_BIDI

/*********************
 *      DEFINES
 *********************/
#define LV_BIDI_BRACKLET_DEPTH   4

// Highest bit of the 16-bit pos_conv value specifies whether this pos is RTL or not
#define GET_POS(x) ((x) & 0x7FFF)
#define IS_RTL_POS(x) (((x) & 0x8000) != 0)
#define SET_RTL_POS(x, is_rtl) (GET_POS(x) | ((is_rtl)? 0x8000: 0))

/**********************
 *      TYPEDEFS
 **********************/
typedef struct {
    uint32_t bracklet_pos;
    lv_base_dir_t dir;
} bracket_stack_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/

static uint32_t lv_bidi_get_next_paragraph(const char *txt);
static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter);
static bool lv_bidi_letter_is_weak(uint32_t letter);
static bool lv_bidi_letter_is_rtl(uint32_t letter);
static bool lv_bidi_letter_is_neutral(uint32_t letter);

static lv_base_dir_t get_next_run(const char *txt, lv_base_dir_t base_dir, uint32_t max_len, uint32_t *len,
                                  uint16_t   *pos_conv_len);
static void rtl_reverse(char *dest, const char *src, uint32_t len, uint16_t *pos_conv_out, uint16_t pos_conv_rd_base,
                        uint16_t pos_conv_len);
static uint32_t char_change_to_pair(uint32_t letter);
static lv_base_dir_t bracket_process(const char *txt, uint32_t next_pos, uint32_t len, uint32_t letter,
                                     lv_base_dir_t base_dir);
static void fill_pos_conv(uint16_t *out, uint16_t len, uint16_t index);
static uint32_t get_txt_len(const char *txt, uint32_t max_len);

/**********************
 *  STATIC VARIABLES
 **********************/
static const uint8_t bracket_left[] = {"<({["};
static const uint8_t bracket_right[] = {">)}]"};
static bracket_stack_t br_stack[LV_BIDI_BRACKLET_DEPTH];
static uint8_t br_stack_p;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/**
 * Convert a text to get the characters in the correct visual order according to
 * Unicode Bidirectional Algorithm
 * @param str_in the text to process
 * @param str_out store the result here. Has the be `strlen(str_in)` length
 * @param base_dir `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
 */
void _lv_bidi_process(const char *str_in, char *str_out, lv_base_dir_t base_dir)
{
    if (base_dir == LV_BASE_DIR_AUTO) {
        base_dir = _lv_bidi_detect_base_dir(str_in);
    }

    uint32_t par_start = 0;
    uint32_t par_len;

    while (str_in[par_start] == '\n' || str_in[par_start] == '\r') {
        str_out[par_start] = str_in[par_start];
        par_start ++;
    }

    while (str_in[par_start] != '\0') {
        par_len = lv_bidi_get_next_paragraph(&str_in[par_start]);
        _lv_bidi_process_paragraph(&str_in[par_start], &str_out[par_start], par_len, base_dir, NULL, 0);
        par_start += par_len;

        while (str_in[par_start] == '\n' || str_in[par_start] == '\r') {
            str_out[par_start] = str_in[par_start];
            par_start ++;
        }
    }

    str_out[par_start] = '\0';
}

/**
 * Auto-detect the direction of a text based on the first strong character
 * @param txt the text to process
 * @return `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
 */
lv_base_dir_t _lv_bidi_detect_base_dir(const char *txt)
{
    uint32_t i = 0;
    uint32_t letter;
    while (txt[i] != '\0') {
        letter = _lv_txt_encoded_next(txt, &i);

        lv_base_dir_t dir;
        dir = lv_bidi_get_letter_dir(letter);
        if (dir == LV_BASE_DIR_RTL || dir == LV_BASE_DIR_LTR) {
            return dir;
        }
    }

    /*If there were no strong char earlier return with the default base dir*/
    if (LV_BIDI_BASE_DIR_DEF == LV_BASE_DIR_AUTO) {
        return LV_BASE_DIR_LTR;
    } else {
        return LV_BIDI_BASE_DIR_DEF;
    }
}

/**
 * Get the logical position of a character in a line
 * @param str_in the input string. Can be only one line.
 * @param bidi_txt internally the text is bidi processed which buffer can be get here.
 * If not required anymore has to freed with `lv_mem_free()`
 * Can be `NULL` is unused
 * @param len length of the line in character count
 * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
 * @param visual_pos the visual character position which logical position should be get
 * @param is_rtl tell the char at `visual_pos` is RTL or LTR context
 * @return the logical character position
 */
uint16_t _lv_bidi_get_logical_pos(const char *str_in, char **bidi_txt, uint32_t len, lv_base_dir_t base_dir,
                                  uint32_t visual_pos, bool *is_rtl)
{
    uint32_t pos_conv_len = get_txt_len(str_in, len);
    char *buf = lv_mem_buf_get(len + 1);
    if (buf == NULL) {
        return (uint16_t) - 1;
    }

    uint16_t *pos_conv_buf = lv_mem_buf_get(pos_conv_len * sizeof(uint16_t));
    if (pos_conv_buf == NULL) {
        lv_mem_buf_release(buf);
        return (uint16_t) - 1;
    }

    if (bidi_txt) {
        *bidi_txt = buf;
    }

    _lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len);

    if (is_rtl) {
        *is_rtl = IS_RTL_POS(pos_conv_buf[visual_pos]);
    }

    if (bidi_txt == NULL) {
        lv_mem_buf_release(buf);
    }
    uint16_t res = GET_POS(pos_conv_buf[visual_pos]);
    lv_mem_buf_release(pos_conv_buf);
    return res;
}

/**
 * Get the visual position of a character in a line
 * @param str_in the input string. Can be only one line.
 * @param bidi_txt internally the text is bidi processed which buffer can be get here.
 * If not required anymore has to freed with `lv_mem_free()`
 * Can be `NULL` is unused
 * @param len length of the line in character count
 * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
 * @param logical_pos the logical character position which visual position should be get
 * @param is_rtl tell the char at `logical_pos` is RTL or LTR context
 * @return the visual character position
 */
uint16_t _lv_bidi_get_visual_pos(const char *str_in, char **bidi_txt, uint16_t len, lv_base_dir_t base_dir,
                                 uint32_t logical_pos, bool *is_rtl)
{
    uint32_t pos_conv_len = get_txt_len(str_in, len);
    char *buf = lv_mem_buf_get(len + 1);
    if (buf == NULL) {
        return (uint16_t) - 1;
    }

    uint16_t *pos_conv_buf = lv_mem_buf_get(pos_conv_len * sizeof(uint16_t));
    if (pos_conv_buf == NULL) {
        lv_mem_buf_release(buf);
        return (uint16_t) - 1;
    }

    if (bidi_txt) {
        *bidi_txt = buf;
    }

    _lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len);

    for (uint16_t i = 0; i < pos_conv_len; i++) {
        if (GET_POS(pos_conv_buf[i]) == logical_pos) {

            if (is_rtl) {
                *is_rtl = IS_RTL_POS(pos_conv_buf[i]);
            }
            lv_mem_buf_release(pos_conv_buf);

            if (bidi_txt == NULL) {
                lv_mem_buf_release(buf);
            }
            return i;
        }
    }
    lv_mem_buf_release(pos_conv_buf);
    if (bidi_txt == NULL) {
        lv_mem_buf_release(buf);
    }
    return (uint16_t) - 1;
}

/**
 * Bidi process a paragraph of text
 * @param str_in the string to process
 * @param str_out store the result here
 * @param len length of the text
 * @param base_dir base dir of the text
 * @param pos_conv_out an `uint16_t` array to store the related logical position of the character.
 * Can be `NULL` is unused
 * @param pos_conv_len length of `pos_conv_out` in element count
 */
void _lv_bidi_process_paragraph(const char *str_in, char *str_out, uint32_t len, lv_base_dir_t base_dir,
                                uint16_t *pos_conv_out, uint16_t pos_conv_len)
{
    uint32_t run_len = 0;
    lv_base_dir_t run_dir;
    uint32_t rd = 0;
    uint32_t wr;
    uint16_t pos_conv_run_len = 0;
    uint16_t pos_conv_rd = 0;
    uint16_t pos_conv_wr;

    if (base_dir == LV_BASE_DIR_AUTO) {
        base_dir = _lv_bidi_detect_base_dir(str_in);
    }
    if (base_dir == LV_BASE_DIR_RTL) {
        wr = len;
        pos_conv_wr = pos_conv_len;
    } else {
        wr = 0;
        pos_conv_wr = 0;
    }

    if (str_out) {
        str_out[len] = '\0';
    }

    lv_base_dir_t dir = base_dir;

    /*Empty the bracket stack*/
    br_stack_p = 0;

    /*Process neutral chars in the beginning*/
    while (rd < len) {
        uint32_t letter = _lv_txt_encoded_next(str_in, &rd);
        pos_conv_rd++;
        dir = lv_bidi_get_letter_dir(letter);
        if (dir == LV_BASE_DIR_NEUTRAL) {
            dir = bracket_process(str_in, rd, len, letter, base_dir);
        }
        if (dir != LV_BASE_DIR_NEUTRAL && dir != LV_BASE_DIR_WEAK) {
            break;
        }
    }

    if (rd && str_in[rd] != '\0') {
        _lv_txt_encoded_prev(str_in, &rd);
        pos_conv_rd--;
    }

    if (rd) {
        if (base_dir == LV_BASE_DIR_LTR) {
            if (str_out) {
                lv_memcpy(&str_out[wr], str_in, rd);
                wr += rd;
            }
            if (pos_conv_out) {
                fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_rd, 0);
                pos_conv_wr += pos_conv_rd;
            }
        } else {
            wr -= rd;
            pos_conv_wr -= pos_conv_rd;
            rtl_reverse(str_out ? &str_out[wr] : NULL, str_in, rd, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL, 0,
                        pos_conv_rd);
        }
    }

    /*Get and process the runs*/

    while (rd < len && str_in[rd]) {
        run_dir = get_next_run(&str_in[rd], base_dir, len - rd, &run_len, &pos_conv_run_len);

        if (base_dir == LV_BASE_DIR_LTR) {
            if (run_dir == LV_BASE_DIR_LTR) {
                if (str_out) {
                    lv_memcpy(&str_out[wr], &str_in[rd], run_len);
                }
                if (pos_conv_out) {
                    fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd);
                }
            } else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL,
                                   pos_conv_rd, pos_conv_run_len);
            wr += run_len;
            pos_conv_wr += pos_conv_run_len;
        } else {
            wr -= run_len;
            pos_conv_wr -= pos_conv_run_len;
            if (run_dir == LV_BASE_DIR_LTR) {
                if (str_out) {
                    lv_memcpy(&str_out[wr], &str_in[rd], run_len);
                }
                if (pos_conv_out) {
                    fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd);
                }
            } else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL,
                                   pos_conv_rd, pos_conv_run_len);
        }

        rd += run_len;
        pos_conv_rd += pos_conv_run_len;
    }
}

void lv_bidi_calculate_align(lv_text_align_t *align, lv_base_dir_t *base_dir, const char *txt)
{
    if (*base_dir == LV_BASE_DIR_AUTO) {
        *base_dir = _lv_bidi_detect_base_dir(txt);
    }

    if (*align == LV_TEXT_ALIGN_AUTO) {
        if (*base_dir == LV_BASE_DIR_RTL) {
            *align = LV_TEXT_ALIGN_RIGHT;
        } else {
            *align = LV_TEXT_ALIGN_LEFT;
        }
    }
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/**
 * Get the next paragraph from a text
 * @param txt the text to process
 * @return the length of the current paragraph in byte count
 */
static uint32_t lv_bidi_get_next_paragraph(const char *txt)
{
    uint32_t i = 0;

    _lv_txt_encoded_next(txt, &i);

    while (txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
        _lv_txt_encoded_next(txt, &i);
    }

    return i;
}

/**
 * Get the direction of a character
 * @param letter an Unicode character
 * @return `LV_BASE_DIR_RTL/LTR/WEAK/NEUTRAL`
 */
static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter)
{
    if (lv_bidi_letter_is_rtl(letter)) {
        return LV_BASE_DIR_RTL;
    }
    if (lv_bidi_letter_is_neutral(letter)) {
        return LV_BASE_DIR_NEUTRAL;
    }
    if (lv_bidi_letter_is_weak(letter)) {
        return LV_BASE_DIR_WEAK;
    }

    return LV_BASE_DIR_LTR;
}
/**
 * Tell whether a character is weak or not
 * @param letter an Unicode character
 * @return true/false
 */
static bool lv_bidi_letter_is_weak(uint32_t letter)
{
    uint32_t i = 0;
    static const char weaks[] = "0123456789";

    do {
        uint32_t x = _lv_txt_encoded_next(weaks, &i);
        if (letter == x) {
            return true;
        }
    } while (weaks[i] != '\0');

    return false;
}
/**
 * Tell whether a character is RTL or not
 * @param letter an Unicode character
 * @return true/false
 */
static bool lv_bidi_letter_is_rtl(uint32_t letter)
{
    if (letter >= 0x5d0 && letter <= 0x5ea) {
        return true;
    }
    if (letter == 0x202E) {
        return true;    /*Unicode of LV_BIDI_RLO*/
    }

    /*Check for Persian and Arabic characters [https://en.wikipedia.org/wiki/Arabic_script_in_Unicode]*/
    if (letter >= 0x600 && letter <= 0x6FF) {
        return true;
    }
    if (letter >= 0xFB50 && letter <= 0xFDFF) {
        return true;
    }
    if (letter >= 0xFE70 && letter <= 0xFEFF) {
        return true;
    }

    return false;
}

/**
 * Tell whether a character is neutral or not
 * @param letter an Unicode character
 * @return true/false
 */
static bool lv_bidi_letter_is_neutral(uint32_t letter)
{
    uint16_t i;
    static const char neutrals[] = " \t\n\r.,:;'\"`!?%/\\-=()[]{}<>@#&$|";
    for (i = 0; neutrals[i] != '\0'; i++) {
        if (letter == (uint32_t)neutrals[i]) {
            return true;
        }
    }

    return false;
}

static uint32_t get_txt_len(const char *txt, uint32_t max_len)
{
    uint32_t len = 0;
    uint32_t i   = 0;

    while (i < max_len && txt[i] != '\0') {
        _lv_txt_encoded_next(txt, &i);
        len++;
    }

    return len;
}

static void fill_pos_conv(uint16_t *out, uint16_t len, uint16_t index)
{
    uint16_t i;
    for (i = 0; i < len; i++) {
        out[i] = SET_RTL_POS(index, false);
        index++;
    }
}

static lv_base_dir_t get_next_run(const char *txt, lv_base_dir_t base_dir, uint32_t max_len, uint32_t *len,
                                  uint16_t   *pos_conv_len)
{
    uint32_t i = 0;
    uint32_t letter;

    uint16_t pos_conv_i = 0;

    letter = _lv_txt_encoded_next(txt, NULL);
    lv_base_dir_t dir = lv_bidi_get_letter_dir(letter);
    if (dir == LV_BASE_DIR_NEUTRAL) {
        dir = bracket_process(txt, 0, max_len, letter, base_dir);
    }

    /*Find the first strong char. Skip the neutrals*/
    while (dir == LV_BASE_DIR_NEUTRAL || dir == LV_BASE_DIR_WEAK) {
        letter = _lv_txt_encoded_next(txt, &i);
        pos_conv_i++;
        dir = lv_bidi_get_letter_dir(letter);
        if (dir == LV_BASE_DIR_NEUTRAL) {
            dir = bracket_process(txt, i, max_len, letter, base_dir);
        }

        if (i >= max_len || txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') {
            *len = i;
            *pos_conv_len = pos_conv_i;
            return base_dir;
        }
    }

    lv_base_dir_t run_dir = dir;

    uint32_t i_prev = i;
    uint32_t i_last_strong = i;
    uint16_t pos_conv_i_prev = pos_conv_i;
    uint16_t pos_conv_i_last_strong = pos_conv_i;

    /*Find the next char which has different direction*/
    lv_base_dir_t next_dir = base_dir;
    while (i_prev < max_len && txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
        letter = _lv_txt_encoded_next(txt, &i);
        pos_conv_i++;
        next_dir  = lv_bidi_get_letter_dir(letter);
        if (next_dir == LV_BASE_DIR_NEUTRAL) {
            next_dir = bracket_process(txt, i, max_len, letter, base_dir);
        }

        /*New dir found?*/
        if ((next_dir == LV_BASE_DIR_RTL || next_dir == LV_BASE_DIR_LTR) && next_dir != run_dir) {
            /*Include neutrals if `run_dir == base_dir`*/
            if (run_dir == base_dir) {
                *len = i_prev;
                *pos_conv_len = pos_conv_i_prev;
            }
            /*Exclude neutrals if `run_dir != base_dir`*/
            else {
                *len = i_last_strong;
                *pos_conv_len = pos_conv_i_last_strong;
            }

            return run_dir;
        }

        if (next_dir != LV_BASE_DIR_NEUTRAL) {
            i_last_strong = i;
            pos_conv_i_last_strong = pos_conv_i;
        }

        i_prev = i;
        pos_conv_i_prev = pos_conv_i;
    }

    /*Handle end of of string. Apply `base_dir` on trailing neutrals*/

    /*Include neutrals if `run_dir == base_dir`*/
    if (run_dir == base_dir) {
        *len = i_prev;
        *pos_conv_len = pos_conv_i_prev;
    }
    /*Exclude neutrals if `run_dir != base_dir`*/
    else {
        *len = i_last_strong;
        *pos_conv_len = pos_conv_i_last_strong;
    }

    return run_dir;
}

static void rtl_reverse(char *dest, const char *src, uint32_t len, uint16_t *pos_conv_out, uint16_t pos_conv_rd_base,
                        uint16_t pos_conv_len)
{
    uint32_t i = len;
    uint32_t wr = 0;
    uint16_t pos_conv_i = pos_conv_len;
    uint16_t pos_conv_wr = 0;

    while (i) {
        uint32_t letter = _lv_txt_encoded_prev(src, &i);
        uint16_t pos_conv_letter = --pos_conv_i;

        /*Keep weak letters (numbers) as LTR*/
        if (lv_bidi_letter_is_weak(letter)) {
            uint32_t last_weak = i;
            uint32_t first_weak = i;
            uint16_t pos_conv_last_weak = pos_conv_i;
            uint16_t pos_conv_first_weak = pos_conv_i;
            while (i) {
                letter = _lv_txt_encoded_prev(src, &i);
                pos_conv_letter = --pos_conv_i;

                /*No need to call `char_change_to_pair` because there not such chars here*/

                /*Finish on non-weak char*/
                /*but treat number and currency related chars as weak*/
                if (lv_bidi_letter_is_weak(letter) == false && letter != '.' && letter != ',' && letter != '$' && letter != '%') {
                    _lv_txt_encoded_next(src, &i);   /*Rewind one letter*/
                    pos_conv_i++;
                    first_weak = i;
                    pos_conv_first_weak = pos_conv_i;
                    break;
                }
            }
            if (i == 0) {
                first_weak = 0;
                pos_conv_first_weak = 0;
            }

            if (dest) {
                lv_memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1);
            }
            if (pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_last_weak - pos_conv_first_weak + 1,
                                                pos_conv_rd_base + pos_conv_first_weak);
            wr += last_weak - first_weak + 1;
            pos_conv_wr += pos_conv_last_weak - pos_conv_first_weak + 1;
        }

        /*Simply store in reversed order*/
        else {
            uint32_t letter_size = _lv_txt_encoded_size((const char *)&src[i]);
            /*Swap arithmetical symbols*/
            if (letter_size == 1) {
                uint32_t new_letter = letter = char_change_to_pair(letter);
                if (dest) {
                    dest[wr] = (uint8_t)new_letter;
                }
                if (pos_conv_out) {
                    pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_letter, true);
                }
                wr++;
                pos_conv_wr++;
            }
            /*Just store the letter*/
            else {
                if (dest) {
                    lv_memcpy(&dest[wr], &src[i], letter_size);
                }
                if (pos_conv_out) {
                    pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_i, true);
                }
                wr += letter_size;
                pos_conv_wr++;
            }
        }
    }
}

static uint32_t char_change_to_pair(uint32_t letter)
{

    uint8_t i;
    for (i = 0; bracket_left[i] != '\0'; i++) {
        if (letter == bracket_left[i]) {
            return bracket_right[i];
        }
    }

    for (i = 0; bracket_right[i] != '\0'; i++) {
        if (letter == bracket_right[i]) {
            return bracket_left[i];
        }
    }

    return letter;
}

static lv_base_dir_t bracket_process(const char *txt, uint32_t next_pos, uint32_t len, uint32_t letter,
                                     lv_base_dir_t base_dir)
{
    lv_base_dir_t bracket_dir = LV_BASE_DIR_NEUTRAL;

    uint8_t i;
    /*Is the letter an opening bracket?*/
    for (i = 0; bracket_left[i] != '\0'; i++) {
        if (bracket_left[i] == letter) {
            /*If so find its matching closing bracket.
             *If a char with base dir. direction is found then the brackets will have `base_dir` direction*/
            uint32_t txt_i = next_pos;
            while (txt_i < len) {
                uint32_t letter_next = _lv_txt_encoded_next(txt, &txt_i);
                if (letter_next == bracket_right[i]) {
                    /*Closing bracket found*/
                    break;
                } else {
                    /*Save the dir*/
                    lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
                    if (letter_dir == base_dir) {
                        bracket_dir = base_dir;
                    }
                }
            }

            /*There were no matching closing bracket*/
            if (txt_i > len) {
                return LV_BASE_DIR_NEUTRAL;
            }

            /*There where a strong char with base dir in the bracket so the dir is found.*/
            if (bracket_dir != LV_BASE_DIR_NEUTRAL && bracket_dir != LV_BASE_DIR_WEAK) {
                break;
            }

            /*If there were no matching strong chars in the brackets then check the previous chars*/
            txt_i = next_pos;
            if (txt_i) {
                _lv_txt_encoded_prev(txt, &txt_i);
            }
            while (txt_i > 0) {
                uint32_t letter_next = _lv_txt_encoded_prev(txt, &txt_i);
                lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
                if (letter_dir == LV_BASE_DIR_LTR || letter_dir == LV_BASE_DIR_RTL) {
                    bracket_dir = letter_dir;
                    break;
                }
            }

            /*There where a previous strong char which can be used*/
            if (bracket_dir != LV_BASE_DIR_NEUTRAL) {
                break;
            }

            /*There were no strong chars before the bracket, so use the base dir.*/
            if (txt_i == 0) {
                bracket_dir = base_dir;
            }

            break;
        }
    }

    /*The letter was an opening bracket*/
    if (bracket_left[i] != '\0') {

        if (bracket_dir == LV_BASE_DIR_NEUTRAL || br_stack_p == LV_BIDI_BRACKLET_DEPTH) {
            return LV_BASE_DIR_NEUTRAL;
        }

        br_stack[br_stack_p].bracklet_pos = i;
        br_stack[br_stack_p].dir = bracket_dir;

        br_stack_p++;
        return bracket_dir;
    } else if (br_stack_p > 0) {
        /*Is the letter a closing bracket of the last opening?*/
        if (letter == bracket_right[br_stack[br_stack_p - 1].bracklet_pos]) {
            bracket_dir = br_stack[br_stack_p - 1].dir;
            br_stack_p--;
            return bracket_dir;
        }
    }

    return LV_BASE_DIR_NEUTRAL;
}

#endif /*LV_USE_BIDI*/
