local tr = aegisub.gettext

script_name = tr"Duetto Meika"
script_description = tr"The ultimate tool for karaoke duets"
script_author = "amoethyst"

include("utils.lua")


function replace_style(line, style_name, style_string)
    before_style, after_style = line.text:match("^(.-{[^}]-)\\?s:".. style_name .."(.*)$")
    return before_style .. style_string .. after_style
end


function duetto(subs, sel)
    styles = {}

    -- create the style map
    for _, line in ipairs(subs) do
        if line.class == "style" then
            styles[line.name] = line
        end
    end

    -- duetto~
    for _, i in ipairs(sel) do
        line = subs[i]

        current_style = styles[line.style]
        -- match every `s:` marker
        for style_name in line.text:gmatch("{[^}]*s:([^}\\]*)[^}]*}") do
            if style_name ~= current_style.name then

                style = styles[style_name]
                -- build the tags to use the new style
                style_string = ""
                if current_style.color1 ~= style.color1 then
                    style_string = style_string .. "\\c" .. style.color1
                end
                if current_style.color2 ~= style.color2 then
                    style_string = style_string .. "\\2c" .. style.color2
                end
                if current_style.color3 ~= style.color3 then
                    style_string = style_string .. "\\3c" .. style.color3
                end
                if current_style.color4 ~= style.color4 then
                    style_string = style_string .. "\\4c" .. style.color4
                end

                -- set style
                line.text = replace_style(line, style_name, style_string)
                current_style = style
            else
                -- remove marker to not break everything
                line.text = replace_style(line, style_name, "")
            end
        end
        subs[i] = line
    end

    aegisub.set_undo_point(script_name)
end


function test_colors(c1, c2)
    return color_from_style(c1) == color_from_style(c2)
end


function get_script_style(style, styles)
    for key, script_style in pairs(styles) do
        if (test_colors(style.color1, script_style.color1)
                and test_colors(style.color2, script_style.color2)
                and test_colors(style.color3, script_style.color3)
                and test_colors(style.color4, script_style.color4)
                and tonumber(style.fontsize) == tonumber(script_style.fontsize)
                and style.fontname == script_style.fontname) then
            return script_style
        end
    end
    return nil
end


function deduetto_meika(subs, sel)
    local styles = {}
    local last_style = -1

    -- create the style map
    for i, line in ipairs(subs) do
        if line.class == "style" then
            styles[line.name] = line
            last_style = i
        end
    end

    local new_styles = {}

    for _, i in ipairs(sel) do
        local line = subs[i]
        local current_style = table.copy(styles[line.style])

        local search_index = 1
        while search_index < #line.text do
            local match_start, match_end = line.text:find("{[^}]*}", search_index)
            if match_start == nil then
                break
            end

            local bracketed = line.text:sub(match_start, match_end)
            local new_style = false

            -- change style's colors
            for tag, value in bracketed:gmatch("\\([1-4]?c)([^}\\]*)") do
                new_style = true
                if tag == "c" or tag == "1c" then
                    current_style.color1 = value
                elseif tag == "2c" then
                    current_style.color2 = value
                elseif tag == "3c" then
                    current_style.color3 = value
                elseif tag == "4c" then
                    current_style.color4 = value
                end
            end

            -- change style's font
            for tag, value in bracketed:gmatch("\\(f[sn])([^}\\]*)") do
                new_style = true
                if tag == "fs" then
                    current_style.fontsize = value
                elseif tag == "fn" then
                    current_style.fontname = value
                end
            end

            if new_style then
                aegisub.log("oof\n")
                local script_style = get_script_style(current_style, styles)
                if script_style == nil then
                    aegisub.log("foo\n")
                    if get_script_style(current_style, new_styles) == nil then
                        new_styles[#new_styles+1] = table.copy(current_style)
                    end
                else
                    aegisub.log("ofo\n")
                    -- remove inline colors
                    bracketed = bracketed:gsub("\\[1-4]?c[^\\}]*", "")
                    bracketed = bracketed:gsub("\\[1-4]?a[^\\}]*", "")
                    -- remove inline fonts
                    bracketed = bracketed:gsub("\\f[sn][^\\}]*", "")

                    -- add style marker
                    bracketed = "{s:" .. script_style.name .. bracketed:sub(2, #bracketed)
                    line.text = line.text:sub(1, match_start-1) .. bracketed ..  line.text:sub(match_end + 1, #line.text)
                end
            end

            search_index = match_start + 1
        end

        subs[i] = line
    end

    if #new_styles > 0 then
        for i, new_style in ipairs(new_styles) do
            new_style.name = "Deduetto style " .. i
            subs.insert(last_style, new_style)
            last_style = last_style + 1
            aegisub.log("Created new style: " .. new_style.name .. "\n")
        end
    end

end

aegisub.register_macro(script_name, script_description, duetto)
aegisub.register_macro(tr"Deduetto Meika", tr"Create styles from inline color tags", deduetto_meika)
