local SIZE_RATIO = 2/3
local SUPERSCRIPT_BASELINE = 2/3
local SUBSCRIPT_BASELINE = -1/3

local function getSize(fontSize, superscripts)
    return fontSize * SIZE_RATIO ^ #superscripts
end

local function getBaseline(fontSize, superscripts)
    local baseline = 0
    for index = 1, #superscripts do
        local superscript = superscripts[index]
        fontSize = fontSize * SIZE_RATIO
        if superscript == 1 then
            baseline = baseline + fontSize * SUPERSCRIPT_BASELINE
        else
            baseline = baseline + fontSize * SUBSCRIPT_BASELINE
        end
    end
    return baseline
end

function superscript(string, font)
    local styledString
    local fontName, fontSize = font:getName(), font:getSize()
    local scriptStack = {}
    local runStyle = { font = font, baseline = 0 }
    local runStart, runEnd = 1, #string
    local function append()
        runStyle.font = (#scriptStack == 0 and font) or Font.get{ name = fontName, size = getSize(fontSize, scriptStack) }
        runStyle.baseline = getBaseline(fontSize, scriptStack)
        local newStyledString = StyledString.new(string:sub(runStart, runEnd), runStyle)
        styledString = (styledString and (styledString .. newStyledString)) or newStyledString
    end
    for index = 1, #string do
        local char = string:sub(index, index)
        if char == '^' then
            runEnd = index - 1
            append()
            scriptStack[#scriptStack + 1] = 1
        elseif char == '_' then
            runEnd = index - 1
            append()
            scriptStack[#scriptStack + 1] = -1
        elseif char == '{' then
            runStart = index + 1
            runEnd = #string
        elseif char == '}' then
            runEnd = index - 1
            append()
            scriptStack[#scriptStack] = nil
            runStart = index + 1
            runEnd = #string
        end
    end
    if runStart <= runEnd or not styledString then
        append()
    end
    return styledString
end

return superscript
