local _M = {}

local function getfile(ctx, f)
    return string.format("%s/%s.%s", ctx.dir, ctx.name, f)
end

local function writefile(name, str)
    local file = assert(io.open(name, "w"))
    file:write(str)
    file:close()
end

local function readfile(name)
    local file = assert(io.open(name, "r"))
    local str = file:read("*a")
    file:close()
    return str
end

local function readfilelines(name)
    local file = assert(io.open(name, "r"))
    local ret = {}
    while true do
        local line = file:read("*l")
        if not line then
            break
        end
        table.insert(ret, line)
    end
    return ret
end

local function encode_table_column(ret, attrs)
    local function encode_column(lines)
        local ret = {}
        for _, k in ipairs(attrs) do
            local v = lines[k]
            if v then
                table.insert(ret, string.format("%s='%s'", k, v))
            end
        end
        return (table.concat(ret, " "))
    end
    local tbl = {}
    for _, v in ipairs(ret) do
        table.insert(tbl, encode_column(v))
    end
    return (table.concat(tbl, "\n"))
end

local function decode_table_column(tbls)
    local function decode_column(linestr)
        local ret = {}
        for k, v in string.gmatch(linestr, "(%S+)='(.-)'") do
            ret[k] = v
        end
        return ret
    end
    local ret = {}
    for _, v in pairs(tbls) do
        local r = decode_column(v)
        table.insert(ret, r)
    end
    return ret
end

local function decode_table_index(tbls)
    local function decode_column(linestr)
        local ret = {}
        for k, v in string.gmatch(linestr, "(%S+)='(.-)'") do
            ret[k] = v
        end
        return ret
    end
    local ret = {}
    for _, v in pairs(tbls) do
        local r = decode_column(v)
        local old = ret[r.key_name]
        if old then
            old.column_name = old.column_name .. ',' .. r.column_name
        else
            ret[r.key_name] = r
        end
    end
    return ret
end

function _M.load(ctx)
    ctx.table_list = readfilelines(getfile(ctx, "table.list"))
    ctx.table = setmetatable({}, {
        __index = function(t, k)
            local str = readfilelines(getfile(ctx, "table." .. k))
            local r = decode_table_column(str)
            t[k] = r
            return r
        end,
    })
    ctx.tablecreate = setmetatable({}, {
        __index = function(t, k)
            local r = readfile(getfile(ctx, "tablecreate." .. k))
            t[k] = r
            return r
        end,
    })
    ctx.index = setmetatable({}, {
        __index = function(t, k)
            local str = readfilelines(getfile(ctx, "index." .. k))
            local r = decode_table_index(str)
            t[k] = r
            return r
        end,
    })
    ctx.proc_list = readfilelines(getfile(ctx, "proc.list"))
    ctx.proc = setmetatable({}, {
        __index = function(t, k)
            local r = readfile(getfile(ctx, "proc." .. k))
            t[k] = r
            return r
        end,
    })
    ctx.func_list = readfilelines(getfile(ctx, "func.list"))
    ctx.func = setmetatable({}, {
        __index = function(t, k)
            local r = readfile(getfile(ctx, "func." .. k))
            t[k] = r
            return r
        end,
    })
    return ctx
end

function _M.newctx(ret, opt)
    ret.name = assert(opt.name)
    ret.dir = assert(opt.dir)
    return ret
end

function _M.compare(ctx, new)
    return new
end

local index_field_attrs = {"table", "non_unique", "key_name", "seq_in_index", "column_name", "collation", "sub_part",
                           "packed", "null", "index_type", "the_index", "comment"}
local field_attrs = {"field", "type", "collation", "null", "key", "default", "extra", "comment", "the_index"}
function _M.save(ctx)
    writefile(getfile(ctx, "table.list"), table.concat(ctx.table_list, "\n"))
    for _, k in ipairs(ctx.table_list) do
        writefile(getfile(ctx, "table." .. k), encode_table_column(ctx.table[k], field_attrs))
        writefile(getfile(ctx, "index." .. k), encode_table_column(ctx.index[k], index_field_attrs))
        writefile(getfile(ctx, "tablecreate." .. k), ctx.tablecreate[k])
    end
    writefile(getfile(ctx, "proc.list"), table.concat(ctx.proc_list, "\n"))
    for _, k in ipairs(ctx.proc_list) do
        writefile(getfile(ctx, "proc." .. k), ctx.proc[k])
    end
    writefile(getfile(ctx, "func.list"), table.concat(ctx.func_list, "\n"))
    for _, k in ipairs(ctx.func_list) do
        writefile(getfile(ctx, "func." .. k), ctx.func[k])
    end
end

return _M
