#include <utility>

#include <GLFW/glfw3.h>

#include "input/TextInput.h"

TextInput::TextInput() : limit(256), active(false) {
}

void TextInput::setLimit(int limit) {
    TextInput::limit = limit;
    while(input.getLength() > limit) {
        input.removeBySwap(limit);
    }
}

void TextInput::reset() {
    input.clear();
    cursor = 0;
}

void TextInput::setActive(bool b) {
    active = b;
}

void TextInput::onKeyEvent(int key, int scancode, int action, int mods) {
    if(!active || action == GLFW_RELEASE) {
        return;
    }
    (void)scancode;
    (void)mods;
    switch(key) {
        case GLFW_KEY_BACKSPACE:
            if(input.getLength() > cursor - 1 && cursor > 0) {
                input.remove(cursor - 1);
                cursor--;
            }
            break;
        case GLFW_KEY_LEFT:
            if(cursor > 0) {
                cursor--;
            }
            break;
        case GLFW_KEY_RIGHT:
            if(cursor < input.getLength()) {
                cursor++;
            }
            break;
    }
}

void TextInput::onCharEvent(uint32 codepoint) {
    if(!active || input.getLength() >= limit) {
        return;
    }
    input.add(codepoint);
    for(int i = input.getLength() - 1; i > cursor; i--) {
        std::swap(input[i], input[i - 1]);
    }
    cursor++;
}

static uint32 read(int& index, const char* s) {
    if(s[index] == '\0') {
        return '\0';
    }
    return s[index++];
}

static uint32 readUnicode(int& index, const char* s) {
    uint32 c = read(index, s);
    if((c & 0xE0) == 0xC0) {
        c = ((c & 0x1F) << 6) | (read(index, s) & 0x3F);
    } else if((c & 0xF0) == 0xE0) {
        c = ((c & 0xF) << 12) | ((read(index, s) & 0x3F) << 6);
        c |= read(index, s) & 0x3F;
    } else if((c & 0xF8) == 0xF0) {
        c = ((c & 0x7) << 18) | ((read(index, s) & 0x3F) << 12);
        c |= (read(index, s) & 0x3F) << 6;
        c |= read(index, s) & 0x3F;
    }
    return c;
}

void TextInput::fill(const char* s) {
    int index = 0;
    reset();
    while(true) {
        uint32 c = readUnicode(index, s);
        if(c == '\0') {
            break;
        }
        onCharEvent(c);
    }
}

int TextInput::getCursor() const {
    return cursor;
}