local separators = " ()[],-+*/=<>"

local function split_by_last_separator(s)
    for i = #s, 1, -1 do
        local c = s:sub(i, i)
        if separators:find(c, 1, true) then
            if i == #s then
                -- Separador al final
                return s, ""
            end
            -- Separador en medio
            return s:sub(1, i), s:sub(i + 1)
        end
    end
    -- Sin separadores
    return "", s
end

local function split_by_last_dot(s)
    local last = s:match(".*()%.")  -- devuelve la posición DESPUÉS del último punto

    if not last then
        -- No hay puntos: primera parte = cadena completa, segunda = ""
        return "", s
    end

    -- Si el punto está al final, la segunda parte es ""
    if last > #s then
        return s, ""
    end

    -- Primera parte incluye el punto
    local first = s:sub(1, last - 1)
    local second = s:sub(last+1)

    return first, second
end

function get_by_path(path)
    local current = _G

    for segment in path:gmatch("[^%.]+") do
        if type(current) ~= "table" then
            return nil
        end
        current = current[segment]
        if current == nil then
            --print("get_by_path: " ..segment.." es nil")
            return nil
        end
    end

    --print("get_by_path: ok")
    return current
end

function entries_starting_with(s, t)
    if not t then return nil end
    local result = {}
    for k, v in pairs(t) do
        if type(k) == "string" and k:sub(1, #s) == s then
            result[#result+1] = k
        end
    end
    return result
end

console = {
    command = "",
    history = {},
    history_pos = 0,
    cursor = 50,
    autocomplete_list = {},
    autocomplete_prefix = "",
    autocomplete_index = 1,
} local me = console

function me.enable()
    app.push(me.update)
    me.command = ""
end

function me.update()
    surf.target(0)
    draw.rrectf(0, 92, 160, 108, 4, 1)
    draw.rrect(0, 92, 160, 108, 4, 13)
    draw.text(">", 2, 96, 13)
    draw.text(me.command, 6, 96, 13)

    if me.autocomplete_list and #me.autocomplete_list>0 then
        --local num = math.min(5, #me.autocomplete_list)
        --local base_y = 92-num*7
        --draw.rectf(3+#me.command*4, base_y, 80, num*7, 1)
        --draw.rect(3+#me.command*4, base_y, 80, num*7, 13)
        --for i=1,num do
        --    draw.text(me.autocomplete_list[i], 6+#me.command*4, base_y+i*7, 13)
        --end

        local sufix = me.autocomplete_list[me.autocomplete_index]:sub(#me.autocomplete_prefix+1)
        draw.text(sufix, 6+#me.command*4, 96, 6)
    end

    if me.cursor > 24 then
        draw.text("_", 6+#me.command*4, 96, 13)
    end
    me.cursor = me.cursor - 1
    if me.cursor == 0 then me.cursor = 50 end

    if key.press(key.ESCAPE) then
        app.pop()
        return
    end

    local should_update = false

    if #me.history>0 then
        if key.press(key.UP) and me.history_pos > 1 then
            me.history_pos = me.history_pos - 1
            me.command = me.history[me.history_pos]
            should_update = true
        elseif key.press(key.DOWN) and me.history_pos < #me.history then
            me.history_pos = me.history_pos + 1
            me.command = me.history[me.history_pos]
            should_update = true
        end
    end

    local k = key.press()
    if k ~= key.UNKNOWN then
        should_update = true
        if k >= key.A and k <= key.Z then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. string.char(k+61)
            else
                me.command = me.command .. string.char(k+93)
            end
        elseif k == key.N0 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '='
            elseif key.down(key.RALT) then
                me.command = me.command .. '}'
            else
                me.command = me.command .. '0'
            end
        elseif k == key.N1 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '!'
            elseif key.down(key.RALT) then
                me.command = me.command .. '|'
            else
                me.command = me.command .. '1'
            end
        elseif k == key.N2 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '"'
            elseif key.down(key.RALT) then
                me.command = me.command .. '@'
            else
                me.command = me.command .. '2'
            end
        elseif k == key.N3 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '·'
            elseif key.down(key.RALT) then
                me.command = me.command .. '#'
            else
                me.command = me.command .. '3'
            end
        elseif k == key.N4 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '$'
            elseif key.down(key.RALT) then
                me.command = me.command .. '~'
            else
                me.command = me.command .. '4'
            end
        elseif k == key.N5 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '%'
            elseif key.down(key.RALT) then
                me.command = me.command .. ' '
            else
                me.command = me.command .. '5'
            end
        elseif k == key.N6 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '&'
            elseif key.down(key.RALT) then
                me.command = me.command .. ' '
            else
                me.command = me.command .. '6'
            end
        elseif k == key.N7 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '/'
            elseif key.down(key.RALT) then
                me.command = me.command .. ' '
            else
                me.command = me.command .. '7'
            end
        elseif k == key.N8 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '('
            elseif key.down(key.RALT) then
                me.command = me.command .. ' '
            else
                me.command = me.command .. '8'
            end
        elseif k == key.N9 then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. ')'
            elseif key.down(key.RALT) then
                me.command = me.command .. ' '
            else
                me.command = me.command .. '9'
            end

        elseif k == key.PERIOD then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. ':'
            else
                me.command = me.command .. '.'
            end

        elseif k == key.COMMA then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. ';'
            else
                me.command = me.command .. ','
            end

        elseif k == key.GRAVE then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. ' '
            elseif key.down(key.RALT) then
                me.command = me.command .. '\\'
            else
                me.command = me.command .. ' '
            end

        elseif k == key.EQUALS then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '¿'
            else
                me.command = me.command .. '¡'
            end

        elseif k == key.SLASH then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '_'
            elseif key.down(key.RALT) then
                me.command = me.command .. ' '
            else
                me.command = me.command .. '-'
            end

        elseif k == key.APOSTROPHE then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '"'
            elseif key.down(key.RALT) then
                me.command = me.command .. '{'
            else
                me.command = me.command .. '\''
            end

        elseif k == key.SEMICOLON then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. 'Ñ'
            elseif key.down(key.RALT) then
                me.command = me.command .. ' '
            else
                me.command = me.command .. 'ñ'
            end

        elseif k == key.BACKSLASH then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. 'Ç'
            elseif key.down(key.RALT) then
                me.command = me.command .. '}'
            else
                me.command = me.command .. 'ç'
            end

        elseif k == key.RIGHTBRACKET then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '*'
            elseif key.down(key.RALT) then
                me.command = me.command .. ']'
            else
                me.command = me.command .. '+'
            end

        elseif k == key.LEFTBRACKET then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '^'
            elseif key.down(key.RALT) then
                me.command = me.command .. '['
            else
                me.command = me.command .. '`'
            end

        elseif k == key.NONUSBACKSLASH then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '>'
            elseif key.down(key.RALT) then
                me.command = me.command .. ' '
            else
                me.command = me.command .. '<'
            end

        elseif k == key.MINUS then
            if key.down(key.LSHIFT) or key.down(key.RSHIFT) then
                me.command = me.command .. '?'
            else
                me.command = me.command .. '\''
            end

        elseif k == key.SPACE then
            me.command = me.command .. ' '
        elseif k == key.BACKSPACE then
            me.command = me.command:sub(1, #me.command - 1)
        
        elseif k == key.TAB then
            local sufix = me.autocomplete_list[me.autocomplete_index]:sub(#me.autocomplete_prefix+1)
            me.command = me.command .. sufix

        elseif k == key.RETURN then
            table.insert(me.history, me.command)
            me.history_pos = #me.history+1
            local f,err = load(me.command)
            if not f then
                print("Error al compilar:", err)
            else
                print(f())
                app.pop()
            end
        end

    end

    if should_update then
        local a, b = split_by_last_separator(me.command)
        if b ~= "" then
            local ba, bb = split_by_last_dot(b)
            me.autocomplete_prefix = bb
            if ba ~= "" then
                me.autocomplete_list = entries_starting_with(bb, get_by_path(ba))
                --print(bb..":"..ba)
                --print(#me.autocomplete_list)
            else
                me.autocomplete_list = entries_starting_with(bb, _G)
                --print(bb..":".."golbal")
                --print(#me.autocomplete_list)
            end
        else
            me.autocomplete_prefix = ""
            me.autocomplete_list = entries_starting_with("", _G)
            --print("<buit>"..":".."golbal")
            --print(#me.autocomplete_list)
        end
    end
end
