-- https://github.com/HowcanoeWang/rime-lua-aux-code
local AuxFilter = {}

-- local log = require 'log'
-- log.outfile = "aux_code.log"

function AuxFilter.init(env)
    -- log.info("** AuxCode filter", env.name_space)

    AuxFilter.aux_code = AuxFilter.readAuxTxt(env.name_space)

    local engine = env.engine
    local config = engine.schema.config

    -- 設定預設觸發鍵為分號，並從配置中讀取自訂的觸發鍵
    env.trigger_key = config:get_string("key_binder/aux_code_trigger") or "`"
    -- 设定是否显示辅助码，默认为显示
    env.show_aux_notice = config:get_string("key_binder/show_aux_notice") or 'true'
    if env.show_aux_notice == "false" then
        env.show_aux_notice = false
    else
        env.show_aux_notice = true
    end

    ----------------------------
    -- 持續選詞上屏，保持輔助碼分隔符存在 --
    ----------------------------
    env.notifier = engine.context.select_notifier:connect(function(ctx)
        -- 含有輔助碼分隔符才處理
        if not string.find(ctx.input, env.trigger_key) then
            return
        end

        local preedit = ctx:get_preedit()
        local removeAuxInput = ctx.input:match("([^,]+)" .. env.trigger_key)
        local reeditTextFront = preedit.text:match("([^,]+)" .. env.trigger_key)

        -- ctx.text 隨著選字的進行，oaoaoa； 有如下的輸出：
        -- ---- 有輔助碼 ----
        -- >>> 啊 oaoa；au
        -- >>> 啊吖 oa；au
        -- >>> 啊吖啊；au
        -- ---- 無輔助碼 ----
        -- >>> 啊 oaoa；
        -- >>> 啊吖 oa；
        -- >>> 啊吖啊；
        -- 這邊把已經上屏的字段 (preedit:text) 進行分割；
        -- 如果已經全部選完了，分割後的結果就是 nil，否則都是 吖卡 a 這種字符串
        -- 驗證方式：
        -- log.info('select_notifier', ctx.input, removeAuxInput, preedit.text, reeditTextFront)

        -- 當最終不含有任何字母時 (候選)，就跳出分割模式，並把輔助碼分隔符刪掉
        ctx.input = removeAuxInput
        if reeditTextFront and reeditTextFront:match("[a-z]") then
            -- 給詞尾自動添加分隔符，上面的 re.match 會把分隔符刪掉
            ctx.input = ctx.input .. env.trigger_key
        else
            -- 剩下的直接上屏
            ctx:commit()
        end
    end)
end

----------------
-- 閱讀輔碼文件 --
----------------
function AuxFilter.readAuxTxt(txtpath)
    -- log.info("** AuxCode filter", 'read Aux code txt:', txtpath)
    if AuxFilter.cache then
        return AuxFilter.cache
    end
    
    local defaultFile = 'moqi_aux_code.txt'
    local userPath = rime_api.get_user_data_dir() .. "/lua/aux_code/"
    local fileAbsolutePath = userPath .. txtpath .. ".txt"

    local file = io.open(fileAbsolutePath, "r") or io.open(userPath .. defaultFile, "r")
    if not file then
        log.info("Unable to open auxiliary code file.")
        return {}
    end

    local auxCodes = {}
    for line in file:lines() do
        if line ~= nil and line ~= "" then  -- 检查 line 是否为空
            local line_ = line:match("[^\r\n]+") -- 去掉換行符，不然 value 是帶著 \n 的
            local key, value = line_:match("([^=]+)=(.+)") -- 分割 = 左右的變數
            if key and value then
                auxCodes[key] = auxCodes[key] or {}
                table.insert(auxCodes[key], value)
            end
        end
    end
    file:close()
    -- 確認 code 能打印出來
    -- for key, value in pairs(AuxFilter.aux_code) do
    --     log.info(key, table.concat(value, ','))
    -- end

    AuxFilter.cache = auxCodes
    return AuxFilter.cache
end

-- local function getUtf8CharLength(byte)
--     if byte < 128 then
--         return 1
--     elseif byte < 224 then
--         return 2
--     elseif byte < 240 then
--         return 3
--     else
--         return 4
--     end
-- end

-- 輔助函數，用於獲取表格的所有鍵
local function table_keys(t)
    local keys = {}
    for key, _ in pairs(t) do
        table.insert(keys, key)
    end
    return keys
end

-----------------------------------------------
-- 計算詞語整體的輔助碼
-- 目前定義為
--   把字或词组的所有辅码，第一个键堆到一起，第二个键堆到一起
--   例子：
--       候选(word) = 拜日
--          【拜】 的辅码有 charAuxCodes=
--             p a
--             p u
--             u a
--             u f
--             u u
--          【日】 的辅码有 charAuxCodes=
--             o r
--             r i
--             a a
--             u h
--       (竖着拍成左右两个字符串)
--   第一个辅码键的不重复列表为：fullAuxCodes[1]= urpao 
--   第二个辅码键的不重复列表为：fullAuxCodes[2]= urhafi
-- -----------------------------------------------
function AuxFilter.fullAux(env, word)
    local fullAuxCodes = {}
    -- log.info('候选词：', word)
    for _, codePoint in utf8.codes(word) do
        local char = utf8.char(codePoint)
        local charAuxCodes = AuxFilter.aux_code[char] -- 每個字的輔助碼組
        if charAuxCodes then -- 輔助碼存在
            for _, code in ipairs(charAuxCodes) do
                for i = 1, #code do
                    fullAuxCodes[i] = fullAuxCodes[i] or {}
                    fullAuxCodes[i][code:sub(i, i)] = true
                end
            end
        end
    end

    -- 將表格轉換為字符串
    for i, chars in pairs(fullAuxCodes) do
        fullAuxCodes[i] = table.concat(table_keys(chars), "")
    end

    return fullAuxCodes
end

-----------------------------------------------
-- 判斷 auxStr 是否匹配 fullAux
-----------------------------------------------
function AuxFilter.match(fullAux, auxStr)
    if #fullAux == 0 then
        return false
    end

    local firstKeyMatched = fullAux[1]:find(auxStr:sub(1, 1)) ~= nil
    -- 如果辅助码只有一个键，且第一个键匹配，则返回 true
    if #auxStr == 1 then
        return firstKeyMatched
    end

    -- 如果辅助码有两个或更多键，检查第二个键是否匹配
    local secondKeyMatched = fullAux[2] and fullAux[2]:find(auxStr:sub(2, 2)) ~= nil

    -- 只有当第一个键和第二个键都匹配时，才返回 true
    return firstKeyMatched and secondKeyMatched
end

------------------
-- filter 主函數 --
------------------
function AuxFilter.func(input, env)
    local context = env.engine.context
    local inputCode = context.input

    -- 分割部分正式開始
    local auxStr = ''

    -- 判断字符串中是否包含輔助碼分隔符
    if not string.find(inputCode, env.trigger_key) then
        -- 没有输入辅助码引导符，则直接yield所有待选项，不进入后续迭代，提升性能
        for cand in input:iter() do
            yield(cand)
        end
        return
    else
        -- 字符串中包含輔助碼分隔符
        local trigger_pattern = env.trigger_key:gsub("%W", "%%%1") -- 處理特殊字符
        local localSplit = inputCode:match(trigger_pattern .. "([^,]+)")
        if localSplit then
            auxStr = string.sub(localSplit, 1, 2)
            -- log.info('re.match ' .. local_split)
        end

        -- 更新逻辑：没有匹配上就不出现再候选框里，提升性能
        -- local insertLater = {}

        -- 遍歷每一個待選項
        for cand in input:iter() do
            local cand_ = cand
            local auxCodes = AuxFilter.aux_code[cand.text] -- 僅單字非 nil
            local fullAuxCodes = AuxFilter.fullAux(env, cand.text)

            -- 查看 auxCodes
            -- log.info(cand.text, #auxCodes)
            -- for i, cl in ipairs(auxCodes) do
            --     log.info(i, table.concat(cl, ',', 1, #cl))
            -- end

            -- 給待選項加上輔助碼提示
            if env.show_aux_notice and auxCodes and #auxCodes > 0 then
                local codeComment = table.concat(auxCodes, ',')
                -- 處理 simplifier
                if cand:get_dynamic_type() == "Shadow" then
                    local shadowText = cand.text
                    local shadowComment = cand.comment
                    local originalCand = cand:get_genuine()
                    cand_ = ShadowCandidate(originalCand, originalCand.type, shadowText,
                        originalCand.comment .. shadowComment .. '(' .. codeComment .. ')')
                else
                    cand.comment = '(' .. codeComment .. ')'
                end
            end

            -- 過濾輔助碼
            if #auxStr == 0 then
                -- 沒有輔助碼、不需篩選，直接返回待選項
                yield(cand_)
            elseif #auxStr > 0 and fullAuxCodes and (cand.type == 'user_phrase' or cand.type == 'phrase') and
                AuxFilter.match(fullAuxCodes, auxStr) then
                -- 匹配到辅助码的待选项，直接插入到候选框中( 获得靠前的位置 )
                yield(cand_)
            else
                -- 待选项字词 没有 匹配到当前的辅助码，插入到列表中，最后插入到候选框里( 获得靠后的位置 )
                -- table.insert(insertLater, cand)
                -- 更新逻辑：没有匹配上就不出现再候选框里，提升性能
            end
        end

        -- 把沒有匹配上的待選給添加上
        -- for _, cand in ipairs(insertLater) do
        --     yield(cand)
        -- end
        -- 更新逻辑：没有匹配上就不出现再候选框里，提升性能
        
    end

end

function AuxFilter.fini(env)
    env.notifier:disconnect()
end

return AuxFilter
