
bool TE_Initialized;
int64_t TE_Caret;

struct TE_Buffer {
    S8_String string;
    int64_t capacity;
};
char TE_Text[128 * 4];
TE_Buffer TE_MainBuffer = {{TE_Text}, lengthof(TE_Text)};

void TE_MemoryMove(char *dest, char *src, int64_t size) {
    if (dest == src)
        return;

    char *ends = src + size;
    bool overlap_source_behind_dest = dest >= src && dest < ends;
    if (overlap_source_behind_dest) {
        char *endd = dest + size;
        while (size--) {
            *--endd = *--ends;
        }
    }
    else {
        while (size--) {
            *dest++ = *src++;
        }
    }
}

void TE_MemoryCopy(char *dst, char *src, int64_t size) {
    while (size--) *dst++ = *src++;
}

void TE_Insert(TE_Buffer *buffer, S8_String string, int64_t pos) {
    int64_t capacity_remaining = buffer->capacity - buffer->string.len;
    pos = Clamp(pos, (int64_t)0, buffer->string.len);
    string.len = Clamp(string.len, (int64_t)0, capacity_remaining);

    char *address_to_insert_at = buffer->string.str + pos;
    char *end_of_address_to_insert_at = address_to_insert_at + string.len;
    int64_t existing_size_to_move = buffer->string.len - pos;

    TE_MemoryMove(end_of_address_to_insert_at, address_to_insert_at, existing_size_to_move);
    TE_MemoryMove(address_to_insert_at, string.str, string.len);
    buffer->string.len += string.len;

    IO_Assert(buffer->string.len <= buffer->capacity);
}

void TE_Replace(TE_Buffer *buffer, S8_String replacement, int64_t pos_region_to_replace, int64_t size_region_to_replace) {
    int64_t capacity_remaining = buffer->capacity - buffer->string.len;
    int64_t start_pos_region_to_replace = Clamp(pos_region_to_replace, (int64_t)0, buffer->string.len);
    int64_t end_pos_region_to_replace = Clamp(pos_region_to_replace + size_region_to_replace, (int64_t)0, buffer->string.len);
    int64_t actual_size_region_to_replace = end_pos_region_to_replace - start_pos_region_to_replace;

    IO_Assert(actual_size_region_to_replace >= 0);

    char *start_of_region_to_replace = buffer->string.str + start_pos_region_to_replace;
    char *end_of_region_to_replace = buffer->string.str + end_pos_region_to_replace;
    char *new_home_of_old_memory = start_of_region_to_replace + replacement.len;
    int64_t existing_size_to_move = buffer->string.len - end_pos_region_to_replace;

    TE_MemoryMove(new_home_of_old_memory, end_of_region_to_replace, existing_size_to_move);
    TE_MemoryMove(start_of_region_to_replace, replacement.str, replacement.len);
    buffer->string.len = buffer->string.len - actual_size_region_to_replace + replacement.len;

    IO_Assert(buffer->string.len <= buffer->capacity);
}

typedef int TE_SearchFlags;
enum {
    TE_SearchFlags_IgnoreCase = 1,
    TE_SearchFlags_SearchLeft = 2,
};

int64_t TE_Search(S8_String string, S8_String seek_string, int64_t start_seek_pos, TE_SearchFlags flags) {
    int64_t start_seek = Clamp(start_seek_pos, (int64_t)0, string.len - 1);
    if (flags & TE_SearchFlags_SearchLeft) {
        for (int64_t i = start_seek; i >= 0; i -= 1) {
            int64_t seek_size = Clamp(seek_string.len, (int64_t)0, string.len - i);
            S8_String to_try = S8_Make(string.str + i, seek_size);
            if (S8_AreEqual(to_try, seek_string, flags)) {
                return i;
            }
        }
    }
    else {
        for (int64_t i = start_seek; i < seek_string.len; i += 1) {
            int64_t seek_size = Clamp(seek_string.len, (int64_t)0, string.len - i);
            S8_String to_try = S8_Make(string.str + i, seek_size);
            if (S8_AreEqual(to_try, seek_string, flags)) {
                return i;
            }
        }
    }
    return -1;
}

int64_t CaretBackward() {
    int64_t original_cursor = TE_Caret;
    IO_Assert(TE_Caret <= TE_MainBuffer.string.len);
    IO_Assert(TE_Caret >= 0);
    while (TE_Caret) {
        char it = TE_MainBuffer.string.str[--TE_Caret];
        if ((it & 0b11000000) != 0b10000000) break; // We dont want to stop on continuation byte
    }
    TE_Caret = Clamp(TE_Caret, (int64_t)0, TE_MainBuffer.string.len);

    int64_t step = original_cursor - TE_Caret;
    return step;
}

void CaretForward() {
    IO_Assert(TE_Caret <= TE_MainBuffer.string.len);
    IO_Assert(TE_Caret >= 0);
    UTF32_Result decode = UTF_ConvertUTF8ToUTF32(TE_MainBuffer.string.str + TE_Caret, (int)(TE_MainBuffer.string.len - TE_Caret));
    TE_Caret += decode.advance;
    TE_Caret = Clamp(TE_Caret, (int64_t)0, TE_MainBuffer.string.len);
}

static void PROTOTYPE_TextEditor() {
    if (!TE_Initialized) {
        TE_Initialized = !TE_Initialized;
    }

    if (Mu->window->key[MU_KEY_LEFT].raw_press) {
        if (Mu->window->key[MU_KEY_CONTROL].down) {
            CaretBackward();
            int64_t pos = TE_Search(TE_MainBuffer.string, S8_Lit(" "), TE_Caret, TE_SearchFlags_SearchLeft);
            if (pos == -1) pos = 0;
            TE_Caret = pos;
        }
        else CaretBackward();
    }
    if (Mu->window->key[MU_KEY_RIGHT].raw_press) {
        if (Mu->window->key[MU_KEY_CONTROL].down) {
            // CaretForward();
            int64_t pos = TE_Search(TE_MainBuffer.string, S8_Lit(" "), TE_Caret, 0);
            if (pos == -1) pos = TE_MainBuffer.string.len;
            TE_Caret = pos;
        }
        else CaretForward();
    }

    for (UTF8_Iter iter = UTF8_IterateEx((char *)Mu->window->user_text8, Mu->window->user_text8_count); iter.item; UTF8_Advance(&iter)) {
        uint32_t it = iter.item;

        if (it == '\b') {
            int64_t step = CaretBackward();
            TE_Replace(&TE_MainBuffer, S8_Lit(""), TE_Caret, step);
        }

        else if (it == 127) {
            // @warning: If I want to implement stuff like seek over all whitespace then it needs to be on search
            // function level
            int64_t pos = TE_Search(TE_MainBuffer.string, S8_Lit(" "), TE_Caret, TE_SearchFlags_SearchLeft);
            if (pos == -1) pos = 0;
            TE_Replace(&TE_MainBuffer, S8_Lit(""), pos, TE_Caret);
            TE_Caret = pos;
        }

        else {
            TE_Insert(&TE_MainBuffer, S8_Make(iter.str + iter.i, iter.utf8_codepoint_byte_size), TE_Caret);
            CaretForward();
        }
    }
    Vec2 window_middle = Mu->window->sizef / 2.0f;
    Vec2 string_size = R2_GetStringSize(TE_MainBuffer.string);

    Vec2 text_pos = window_middle - Vec2{string_size.x / 2, 0};
    R2_StringParams params = R2_MakeStringParams(TE_MainBuffer.string, text_pos, COLOR_text, true);
    params.rects_arena = G_Frame;
    R2_StringMeasure measure = R2_BaseDrawString(params);

    // Draw the caret
    {
        Rect2 caret_rect = {};
        if (TE_Caret == 0) {
            if (TE_MainBuffer.string.len) {
                R2_RectNode *it = measure.first_rect;
                caret_rect = {it->rect.min.x - 2, it->rect.min.y, it->rect.min.x + 2, it->rect.max.y};
            }
            else {
                caret_rect = {window_middle.x - 2, window_middle.y, window_middle.x + 2, window_middle.y + R2.font->size / 2};
            }
        }
        else if (TE_Caret == TE_MainBuffer.string.len) {
            caret_rect = {measure.rect.max.x - 2, measure.rect.min.y, measure.rect.max.x + 2, measure.rect.max.y};
        }
        else {
            int glyph_i = 0;
            for (R2_RectNode *it = measure.first_rect; it; it = it->next) {
                if ((glyph_i + it->utf8_codepoint_byte_size) == TE_Caret) {
                    caret_rect = {it->rect.max.x - 2, it->rect.min.y, it->rect.max.x + 2, it->rect.max.y};
                }
                glyph_i += it->utf8_codepoint_byte_size;
            }
        }
        R2_DrawRect(caret_rect, COLOR_Caret);
    }

    int64_t text_index = 0;
    if (Mu->window->mouse.left.down) {
        for (R2_RectNode *it = measure.first_rect; it; it = it->next) {
            if (AreColliding(it->rect, Mu->window->mouse.posf)) {
                TE_Caret = text_index;

                Vec4 color = COLOR_Caret;
                color.a = 0.2f;
                R2_DrawRect(it->rect, color);
            }
            text_index += 1;
        }
    }
}