local mt = {}
mt.__index = mt

local function switch(value)
    return function (mapping)
        if mapping[value] then
            mapping[value]()
        end
    end
end

local function str_trim(str, p)
    return str:gsub('^'..p..'*(.-)$', '%1'):gsub('^(.-)'..p..'*$', '%1')
end

local function str_split(str, p)
    local rt = {}
    local l = 1
    local state = 0
    for i = 1, #str do
        local c = str:sub(i,i)
        if state == 0 then
            if c == '"' then
                state = 1
            elseif c == p then
                table.insert(rt, str:sub(l, i - 1))
                l = i + 1
            end
        elseif state == 1 then
            if c == '"' then
                state = 0
            end
        end
    end
    if l <= #str then
        table.insert(rt, str:sub(l, #str))
    end
    return rt
end

function mt:accept_ui(type, name, key, value)
    if not self.ui[type][name] then
        self.ui[type][name] = { name = name }
    end
    local ui = self.ui[type][name]
    switch(key) {
        args = function ()
            local r = str_split(value, ',')
            if type == 'call' then
                ui.use_in_event = r[2]
                ui.returns = r[3]
                table.remove(r, 1)
                table.remove(r, 1)
                table.remove(r, 1)
            else
                table.remove(r, 1)
            end
            ui.args = {}
            for _, v in ipairs(r) do
                table.insert(ui.args, { type = v })
            end
        end,
        default = function ()
            local r = str_split(value, ',')
            if not ui.args then
                return
            end
            for i, v in ipairs(ui.args) do
                if r[i] and r[i] ~= '_' then
                    v.default = r[i]
                end
            end
        end,
        limit = function ()
            local r = str_split(value, ',')
            if not ui.args then
                return
            end
            for i, v in ipairs(ui.args) do
                if r[2*i-1] and r[2*i-1] ~= '_' then
                    v.min = r[2*i-1]
                end
                if r[2*i] and r[2*i] ~= '_' then
                    v.max = r[2*i]
                end
            end
        end,
        category = function ()
            ui.category = value
            if not self.categories[type][ui.category] then
                self.categories[type][ui.category] = {}
                table.insert(self.categories[type], ui.category)
            end
            table.insert(self.categories[type][ui.category], ui)
        end,
        script_name = function ()
            ui.script_name = value
        end,
        description = function ()
            if not ui.title then
                ui.title = str_trim(value, '"')
                return
            end
            if ui.description then
                return
            end
            local str = ''
            local r = str_split(value, ',')
            for _, s in ipairs(r) do
                s = str_trim(s, '%s')
                if s:sub(1,1) == '~' then
                    str = str .. '${' .. s:sub(2) .. '}'
                else
                    str = str .. str_trim(s, '"')
                end
            end
            ui.description = str
        end,
        comment = function ()
            ui.comment = str_trim(value, '"')
        end,
    }
end

function mt:parse_ui(type, subtype, key, value)
    if subtype == 'data' then
        if key:sub(1,1) ~= '_' then
            self:accept_ui(type, key, 'args', value)
        elseif key:sub(- #'_Defaults', -1) == '_Defaults' then
            self:accept_ui(type, key:sub(2, -1 - #'_Defaults'), 'default', value)
        elseif key:sub(- #'_Limits', -1) == '_Limits' then
            self:accept_ui(type, key:sub(2, -1 - #'_Limits'), 'limit', value)
        elseif key:sub(- #'_Category', -1) == '_Category' then
            self:accept_ui(type, key:sub(2, -1 - #'_Category'), 'category', value)
        elseif key:sub(- #'_ScriptName', -1) == '_ScriptName' then
            self:accept_ui(type, key:sub(2, -1 - #'_ScriptName'), 'script_name', value)
        elseif key:sub(- #'_UseWithAI', -1) == '_UseWithAI' then
        elseif key:sub(- #'_AIDefaults', -1) == '_AIDefaults' then
        else
            --error('无法解析: ' .. key)
        end
    elseif subtype == 'string' then
        if key:sub(- #'Hint', -1) == 'Hint' then
            self:accept_ui(type, key:sub(1, -1 - #'Hint'), 'comment', value)
        else
            self:accept_ui(type, key, 'description', value)
        end
    else
        error('未知类型: ' .. subtype)
    end
end

function mt:parse_define(section, key, value)
    if not self.ui.define[section] then
        self.ui.define[section] = {}
    end
    table.insert(self.ui.define[section], {key, value})
end

function mt:parse(section, key, value)
    switch(section) {
        TriggerEvents = function ()
            self:parse_ui('event', 'data', key, value)
        end,
        TriggerEventStrings = function ()
            self:parse_ui('event', 'string', key, value)
        end,
        TriggerConditions = function ()
            self:parse_ui('condition', 'data', key, value)
        end,
        TriggerConditionStrings = function ()
            self:parse_ui('condition', 'string', key, value)
        end,
        TriggerActions = function ()
            self:parse_ui('action', 'data', key, value)
        end,
        TriggerActionStrings = function ()
            self:parse_ui('action', 'string', key, value)
        end,
        TriggerCalls = function ()
            self:parse_ui('call', 'data', key, value)
        end,
        TriggerCallStrings = function ()
            self:parse_ui('call', 'string', key, value)
        end,
        TriggerCategories = function ()
            self:parse_define(section, key, value)
        end,
        TriggerTypes = function ()
            self:parse_define(section, key, value)
        end,
        TriggerTypeDefaults = function ()
            self:parse_define(section, key, value)
        end,
        TriggerParams = function ()
            self:parse_define(section, key, value)
        end,
        DefaultTriggerCategories = function ()
            self:parse_define(section, key, value)
        end,
        DefaultTriggers = function ()
            self:parse_define(section, key, value)
        end,
        AIFunctionStrings = function ()
            self:parse_define(section, key, value)
        end,
    }
end

function mt:readfile(buf)
	local f = assert(buf)
	local section = nil
	for line in buf:gmatch '[^\r\n]+' do
		if line:sub(1,1) == "[" then
			section = line:sub(2, #line - 1)
		elseif line:sub(1,2) == "//" then
		elseif line ~= "" then
			local first = line:find("=")
			if first then
				local key = line:sub(1, first - 1)
				local value = line:sub(first + 1) or ""
				self:parse(section, key, value)
			end
		end
	end
end

function mt:reset()
    self.ui = {
        define = {},
        event = {},
        condition = {},
        action = {},
        call = {},
    }
    self.categories = {
        event = {},
        condition = {},
        action = {},
        call = {},
    }
end

function mt:read(loader)
    self:reset()
    self:readfile(loader [[ui\TriggerData.txt]])
    self:readfile(loader [[ui\TriggerStrings.txt]])
end

return function(loader)
    local obj = setmetatable({}, mt)
    obj:read(loader)
    return obj
end
