The Alchemist Code Wiki

READ MORE

The Alchemist Code Wiki
Register
mNo edit summary
No edit summary
(19 intermediate revisions by 2 users not shown)
Line 1: Line 1:
  +
-- <nowiki>
 
local p = {}
 
local p = {}
  +
local h = {}
 
local cargo = require('Module:CargoUtil')
 
local cargo = require('Module:CargoUtil')
  +
local yesno = require('Module:Yesno')
 
local dataModel = require("Module:Data/Model")
 
local dataModel = require("Module:Data/Model")
 
local util_text = require('Module:TextUtil')
 
local util_text = require('Module:TextUtil')
Line 7: Line 10:
 
local trim = util_text.trim
 
local trim = util_text.trim
   
  +
local langs = {'english', 'japanese'}
function getTitlePath()
 
  +
local locPrefixes = {
  +
['Ability'] = true,
  +
['Artifact'] = true,
  +
['ConceptCard'] = true,
  +
['Item'] = true,
  +
['Job'] = true,
  +
['Skill'] = true,
  +
['Unit'] = true,
  +
}
  +
function h.getEntityType(args)
  +
local name = args.name or mw.title.getCurrentTitle().prefixedText
  +
return name:match('Data:Game/MasterParam/([^/]+)/.+')
  +
end
  +
  +
function p.displayLocs(data)
  +
local locs = data.loc
  +
if not locs then return end
  +
local entityType = h.getEntityType(data)
  +
if not entityType or not locPrefixes[entityType] then return end
  +
  +
local definitions = mw.loadData('Module:CargoDeclare/'..entityType..'Loc')
  +
  +
-- HOTFIX: "otherdesc"
  +
local temp = {}
  +
for param, obj in pairs(locs) do
  +
if param == 'otherdesc' then
  +
param = 'desc_ot'
  +
end
  +
for lang, value in pairs(obj) do
  +
temp[param] = temp[param] or {}
  +
temp[param][lang] = value
  +
end
  +
end
  +
locs = temp
  +
  +
-- Build localization wikitable
  +
local wikitable = mw.html.create('table'):addClass('wikitable')
  +
local tr = wikitable:tag('tr')
  +
tr:tag('th'):wikitext('param')
  +
for _, lang in ipairs(langs) do
  +
tr:tag('th'):wikitext(lang)
  +
end
  +
for _, definition in ipairs(definitions) do
  +
local field = definition.field
  +
if field and locs[field] then
  +
tr = wikitable:tag('tr')
  +
tr:tag('th'):wikitext(field)
  +
for _, lang in ipairs(langs) do
  +
tr:tag('td'):wikitext(locs[field][lang])
  +
end
  +
end
  +
end
  +
return tostring(wikitable)
  +
end
  +
  +
function p.storeLocs(nocargo, data)
  +
local locs = data.loc
  +
if not locs then return end
  +
local entityType = h.getEntityType(data)
  +
if not entityType or not locPrefixes[entityType] then return end
  +
  +
local iname = (data.gl or data.jp or {}).iname
  +
local definitions = mw.loadData('Module:CargoDeclare/'..entityType..'Loc')
  +
  +
-- HOTFIX: "otherdesc"
  +
local temp = {}
  +
for param, obj in pairs(locs) do
  +
if param == 'otherdesc' then
  +
param = 'desc_ot'
  +
end
  +
for lang, value in pairs(obj) do
  +
temp[param] = temp[param] or {}
  +
temp[param][lang] = value
  +
end
  +
end
  +
locs = temp
  +
  +
local rows = {}
  +
for _, definition in ipairs(definitions) do
  +
local field = definition.field
  +
if field and locs[field] then
  +
for lang, value in pairs(locs[field]) do
  +
rows[lang] = rows[lang] or {
  +
_table = entityType .. 'Loc',
  +
iname = iname,
  +
lang = lang,
  +
}
  +
rows[lang][field] = value
  +
end
  +
end
  +
end
  +
  +
local result = {}
  +
-- Store localization
  +
if not yesno(nocargo) then
  +
for _, row in pairs(rows) do
  +
mw.logObject(row)
  +
result[#result+1] = cargo.store(row)
  +
end
  +
end
  +
return table.concat(result)
  +
end
  +
  +
function h.getTitlePath()
 
local curTitle = mw.title.getCurrentTitle()
 
local curTitle = mw.title.getCurrentTitle()
 
--if curTitle.prefixedText == "Module:Data" then curTitle = mw.title.new("Game/MasterParam/Unit/UN_V2_LOGI", "Data") end
 
--if curTitle.prefixedText == "Module:Data" then curTitle = mw.title.new("Game/MasterParam/Unit/UN_V2_LOGI", "Data") end
Line 17: Line 124:
 
return titleNodes
 
return titleNodes
 
end
 
end
  +
-- Generates table for data page
  +
function h.insertQueryResult(result, title, tbl)
  +
local fields = {}
  +
local found = {}
  +
local count = 0
  +
local hideCols = true
  +
for i, row in pairs(tbl) do
  +
count = count + 1
  +
for col in ipairs(row) do
  +
if found[col] == nil then
  +
table.insert(fields, col)
  +
found[col] = true
  +
end
  +
end
  +
for col in pairs(row) do
  +
if found[col] == nil then
  +
table.insert(fields, col)
  +
found[col] = true
  +
hideCols = false
  +
end
  +
end
  +
end
  +
table.sort(fields, function(a, b) return a == "server" or a == "lang" or (b ~= "server" and b ~= "lang" and (a or '') < (b or '')) end)
  +
if count == 0 then return nil end
  +
if title ~= nil then table.insert(result, "==="..title.."===\n") end
  +
table.insert(result, '{| class="wikitable"')
  +
for i, col in ipairs(fields) do
  +
table.insert(result, "\n|-")
  +
if not hideCols then
  +
table.insert(result, "\n! ")
  +
table.insert(result, col)
  +
end
  +
  +
-- Merge matching rows
  +
local allSame = true
  +
local testVal = tbl[next(tbl)][col]
  +
for j, row in pairs(tbl) do
  +
if type(testVal) == 'table' and type(row[col]) == 'table' then
  +
for k, v in pairs(testVal) do if v ~= row[col][k] then allSame = false end end
  +
for k, v in pairs(row[col]) do if v ~= testVal[k] then allSame = false end end
  +
elseif row[col] ~= testVal then allSame = false end
  +
end
  +
  +
local function writeVal(val)
  +
if type(val) == "table" then
  +
table.insert(result, "\n")
  +
h.insertQueryResult(result, nil, {val})
  +
else
  +
table.insert(result, tostring(val or ''))
  +
end
  +
end
  +
  +
if allSame then
  +
table.insert(result, '\n| colspan="'..count..'" | ')
  +
writeVal(testVal)
  +
else
  +
for j, row in pairs(tbl) do
  +
table.insert(result, "\n| ")
  +
writeVal(row[col])
  +
end
  +
end
  +
end
  +
table.insert(result, "\n|}\n")
  +
end
  +
p.insertQueryResult = h.insertQueryResult
   
 
-- Will only actually perform the declares on the CargoTable template pages.
 
-- Will only actually perform the declares on the CargoTable template pages.
 
function p.init(frame)
 
function p.init(frame)
local args = require('Module:Arguments').getArgs(frame, {parentFirst=true})
+
local args = require('Module:Arguments').getArgs(frame, {parentFirst=true})
return p._init(args)
+
return p._init(args)
 
end
 
end
 
function p._init(args)
 
function p._init(args)
Line 42: Line 214:
   
 
local curTitle = mw.title.getCurrentTitle()
 
local curTitle = mw.title.getCurrentTitle()
if args[1] then
+
if args[1] then
curTitle = mw.title.new(args[1])
+
curTitle = mw.title.new(args[1])
end
+
end
 
if curTitle.basePageTitle.prefixedText == "Template:CargoTable" then
 
if curTitle.basePageTitle.prefixedText == "Template:CargoTable" then
 
local statement = declareStatements[curTitle.subpageText]
 
local statement = declareStatements[curTitle.subpageText]
 
if statement == nil then return "Invalid table "..curTitle.subpageText end
 
if statement == nil then return "Invalid table "..curTitle.subpageText end
local declare = mw.getCurrentFrame():callParserFunction('#cargo_declare', statement)
+
local tbl = {'{{#cargo_declare:', '}}'}
if args.debug then
+
for k,v in pairs(statement) do
  +
if k ~= 1 then
return mw.getCurrentFrame():extensionTag('pre', tostring(declare))
 
  +
tbl[#tbl+1] = '| ' .. k..' = '..v
  +
end
  +
end
  +
table.sort(tbl)
  +
local declare = table.concat(tbl, '\n')
  +
if yesno(args.debug) then
  +
return tostring(mw.html.create('pre'):wikitext(declare))
 
else
 
else
return declare
+
mw.log(declare)
  +
return mw.getCurrentFrame():callParserFunction('#cargo_declare', statement)
end
 
  +
end
 
end
 
end
 
if mw.getCurrentFrame():getParent() == nil then
 
if mw.getCurrentFrame():getParent() == nil then
Line 63: Line 243:
 
-- Called from Data pages
 
-- Called from Data pages
 
function p.insert(frame)
 
function p.insert(frame)
local args = require('Module:Arguments').getArgs(frame, {parentFirst = true})
+
local args = require('Module:Arguments').getArgs(frame, {parentFirst = true})
return p._insert(args)
+
return p._insert(args)
 
end
 
end
 
function p._insert(args)
 
function p._insert(args)
  +
local nocargo = args.nocargo
 
local curNode = {nodes = {Game = dataModel}}
 
local curNode = {nodes = {Game = dataModel}}
local titlePath = getTitlePath()
+
local titlePath = h.getTitlePath()
 
for i, pathItem in ipairs(titlePath) do
 
for i, pathItem in ipairs(titlePath) do
 
if curNode == nil then return nil end
 
if curNode == nil then return nil end
Line 77: Line 258:
 
 
 
if curNode.model == nil then return nil end
 
if curNode.model == nil then return nil end
 
 
-- Now that we know this is a valid page
 
-- Now that we know this is a valid page
 
local result = {}
 
local result = {}
  +
 
  +
if next(curNode.model) ~= nil then table.insert(result, "==Tables==\nThe following tables have these rows defined by this data entry.\n") end
-- Generates table for data page
 
  +
local insertQueryResult
 
  +
-- Decode JSON
function insertQueryResult(title, tbl)
 
local fields = {}
+
local jsonSets = {}
local found = {}
+
for k, v in pairs(args) do
  +
if type(v) == 'string' then
local count = 0
 
  +
v = mw.text.decode(mw.text.unstripNoWiki(v))
local hideCols = true
 
  +
local success, result = pcall(mw.text.jsonDecode, v)
for i, row in pairs(tbl) do
 
  +
if success then
count = count + 1
 
  +
v = result
for col in ipairs(row) do
 
  +
end
if found[col] == nil then
 
  +
args[k] = v
table.insert(fields, col)
 
  +
end
found[col] = true
 
end
+
end
  +
local content_sha256s = {}
end
 
  +
local suffix = '_sha256'
for col in pairs(row) do
 
  +
for server, json in pairs(args) do
if found[col] == nil then
 
table.insert(fields, col)
+
if server == 'gl' or server == 'jp' or server == 'loc' then
found[col] = true
+
jsonSets[server] = json
  +
elseif string.sub(server, -string.len(suffix)) == suffix then
hideCols = false
 
end
+
-- TODO: Store this in a cargo table somewhere.
  +
content_sha256s[string.sub(server, 1, -string.len(suffix))] = json
end
 
 
end
 
end
table.sort(fields, function(a, b) return a == "server" or a == "lang" or (b ~= "server" and b ~= "lang" and (a or '') < (b or '')) end)
 
if count == 0 then return nil end
 
if title ~= nil then table.insert(result, "=="..title.."==\n") end
 
table.insert(result, '{| class="wikitable"')
 
for i, col in ipairs(fields) do
 
table.insert(result, "\n|-")
 
if not hideCols then
 
table.insert(result, "\n! ")
 
table.insert(result, col)
 
end
 
 
-- Merge matching rows
 
local allSame = true
 
local testVal = tbl[next(tbl)][col]
 
for j, row in pairs(tbl) do
 
if type(testVal) == 'table' and type(row[col]) == 'table' then
 
for k, v in pairs(testVal) do if v ~= row[col][k] then allSame = false end end
 
for k, v in pairs(row[col]) do if v ~= testVal[k] then allSame = false end end
 
elseif row[col] ~= testVal then allSame = false end
 
end
 
 
local function writeVal(val)
 
if type(val) == "table" then
 
table.insert(result, "\n")
 
insertQueryResult(nil, {val})
 
else table.insert(result, tostring(val or '')) end
 
end
 
 
if allSame then
 
table.insert(result, '\n| colspan="'..count..'" | ')
 
writeVal(testVal)
 
else
 
for j, row in pairs(tbl) do
 
table.insert(result, "\n| ")
 
writeVal(row[col])
 
end
 
end
 
end
 
table.insert(result, "\n|}\n")
 
 
end
 
end
 
if next(curNode.model) ~= nil then table.insert(result, "=Tables=\nThe following tables have these rows defined by this data entry.\n") end
 
 
-- Decode JSON
 
local jsonDecode = mw.text.jsonDecode
 
local jsonSets = {}
 
for server, json in pairs(args) do jsonSets[server] = jsonDecode(json) end
 
 
 
 
-- Convert Functions
 
-- Convert Functions
Line 167: Line 301:
 
 
 
-- Do loc first
 
-- Do loc first
  +
result[#result+1] = p.storeLocs(nocargo, args)
  +
result[#result+1] = p.displayLocs(args)
  +
 
local locSets = jsonSets.loc or {}
 
local locSets = jsonSets.loc or {}
 
jsonSets.loc = nil
 
jsonSets.loc = nil
 
local expected = 0
 
local expected = 0
table.insert(result, mw.getCurrentFrame():expandTemplate{ title = "CargoTable/Loc" })
+
table.insert(result, '\n'..mw.getCurrentFrame():expandTemplate{ title = "CargoTable/Loc" })
  +
 
for param, json in pairs(locSets) do
 
for param, json in pairs(locSets) do
for lang, value in pairs(json) do
+
if type(json) == "table" then
table.insert(result, cargo.store{
+
for lang, value in pairs(json) do
_table = 'Loc',
+
table.insert(result, cargo.store{
lang = lang,
+
_table = 'Loc',
param = param,
+
lang = lang,
value = value,
+
param = param,
})
+
value = value,
expected = expected + 1
+
})
  +
expected = expected + 1
  +
end
 
end
 
end
 
end
 
end
 
-- Query loc table
 
-- Query loc table
 
local rows = cargo.query{
 
local rows = cargo.query{
tables = 'Loc',
+
tables = 'Loc',
fields = 'lang, param, value',
+
fields = 'lang, param, value',
where = where,
+
where = where,
orderBy = 'lang'
+
orderBy = 'lang'
 
}
 
}
-- Check if expected row count matches actual row count.
+
-- Check if expected row count matches actual row count.
 
local actual = #rows
 
local actual = #rows
 
if actual ~= expected then
 
if actual ~= expected then
Line 199: Line 339:
 
local queryResult = {}
 
local queryResult = {}
 
for i, row in ipairs(rows) do
 
for i, row in ipairs(rows) do
  +
if row.param == 'name' then
  +
local pagename = string.gsub(row.value, "[%[%]]", {
  +
["["] = "【",
  +
["]"] = "】",
  +
})
  +
if pagename ~= row.value then
  +
row.value = '[[' .. pagename .. '|' .. row.value .. ']]'
  +
else
  +
row.value = '[[' .. row.value .. ']]'
  +
end
  +
end
 
queryResult[i] = row
 
queryResult[i] = row
 
end
 
end
insertQueryResult(tbl, queryResult)
+
h.insertQueryResult(result, tbl, queryResult)
 
 
 
local iname
 
local iname
 
for server, json in pairs(jsonSets) do
 
for server, json in pairs(jsonSets) do
if not iname and json.iname then
+
if not iname and json.iname then
iname = json.iname
+
iname = json.iname
end
+
end
end
+
end
   
local actualTitle = table.concat(titlePath, '/', 1, 3)
+
local actualTitle = table.concat(titlePath, '/', 1, 3)
mw.logObject(actualTitle, 'actualTitle')
+
mw.logObject(actualTitle, 'actualTitle')
 
if actualTitle == 'Game/MasterParam/Buff' then
 
if actualTitle == 'Game/MasterParam/Buff' then
  +
local definitions = mw.loadData('Module:CargoDeclare/BuffDetail')
 
for server, json in pairs(jsonSets) do
 
for server, json in pairs(jsonSets) do
 
for i=1,11 do
 
for i=1,11 do
local calc = tonumber(json['calc'..i])
+
local values = {
local _type = tonumber(json['type'..i]) or 0
+
_table = 'BuffDetail',
  +
}
local tktag = (json['tktag'..i] or '') ~= '' and json['tktag'..i] or nil
 
local vini = tonumber(json['vini'..i])
+
for _, definition in ipairs(definitions) do
local vmax = tonumber(json['vmax'..i])
+
local field = definition.field
local vone = tonumber(json['vone'..i])
+
if definition.type == 'Integer' then
local values = {
+
values[field] = tonumber(json[field..i])
_table = 'BuffDetail',
+
else
idx = i,
+
values[field] = json[field..i]
buff_iname = iname,
+
end
server = server,
+
end
calc = calc,
+
values.buff_iname = iname
tktag = tktag,
+
values.idx = i
['type'] = _type,
+
if (values.type or 0) <= 0 then
vini = vini,
+
mw.logObject(values, "Skipped insert into BuffDetail values")
vmax = vmax,
 
vone = vone,
 
}
 
if _type <= 0 then
 
mw.logObject(values, "Skipped insert into BuffDetail values")
 
 
else
 
else
  +
mw.logObject(values, "#cargo_store")
 
table.insert(result, cargo.store(values))
 
table.insert(result, cargo.store(values))
-- json['calc'..i] = nil
 
-- json['tktag'..i] = nil
 
-- json['type'..i] = nil
 
-- json['vini'..i] = nil
 
-- json['vmax'..i] = nil
 
-- json['vone'..i] = nil
 
 
end
 
end
 
end
 
end
Line 270: Line 412:
 
end
 
end
   
-- Query the data
+
-- Query the data
 
local query = {
 
local query = {
tables = tbl,
+
tables = tbl,
fields = {"server"},
+
fields = {"server"},
where = where,
+
where = where,
orderBy = 'server',
+
orderBy = 'server',
 
}
 
}
 
for col in pairs(model) do
 
for col in pairs(model) do
table.insert(query.fields, col)
+
table.insert(query.fields, col)
 
end
 
end
 
local rows = cargo.query(query)
 
local rows = cargo.query(query)
Line 310: Line 452:
 
queryResult[i] = row
 
queryResult[i] = row
 
end
 
end
insertQueryResult(tbl, queryResult)
+
h.insertQueryResult(result, tbl, queryResult)
 
end
 
end
   
local hasEffects = false
+
local hasEffects = false
local jsonEffects = {}
+
local jsonEffects = {}
 
for server, json in pairs(jsonSets) do
 
for server, json in pairs(jsonSets) do
if json.effects then
+
if json.effects then
hasEffects = true
+
hasEffects = true
jsonEffects[server] = {effects = {}}
+
jsonEffects[server] = {effects = {}}
for i,effect in ipairs(json.effects) do
+
for i,effect in ipairs(json.effects) do
local values = {}
+
local values = {}
values._table = 'ConceptCardEffect'
+
values._table = 'ConceptCardEffect'
values.server = server
+
values.server = server
values.cc_iname = iname
+
values.cc_iname = iname
jsonEffects[server].effects[i] = {}
+
jsonEffects[server].effects[i] = {}
for k,v in pairs(effect) do
+
for k,v in pairs(effect) do
values[k] = v
+
values[k] = v
jsonEffects[server].effects[i][k] = v
+
jsonEffects[server].effects[i][k] = v
effect[k] = nil
+
effect[k] = nil
  +
end
end
 
table.insert(result, cargo.store(values))
+
table.insert(result, cargo.store(values))
jsonEffects[server].server = server
+
jsonEffects[server].server = server
  +
end
end
 
-- A table of failed effect insertions.
+
-- A table of failed effect insertions.
local errorEffects = {}
+
local errorEffects = {}
for i, effect in ipairs(json.effects) do
+
for i, effect in ipairs(json.effects) do
local empty = true
+
local empty = true
for k, v in pairs(effect) do empty = false end
+
for k, v in pairs(effect) do empty = false end
if not empty then
+
if not empty then
errorEffects[#errorEffects+1] = effect
+
errorEffects[#errorEffects+1] = effect
  +
end
end
 
  +
end
end
 
json.effects = #errorEffects > 0 and errorEffects or nil
+
json.effects = #errorEffects > 0 and errorEffects or nil
end
+
end
end
+
end
  +
 
if next(jsonEffects) then
+
if next(jsonEffects) then
table.insert(result, '==ConceptCardEffect==\n')
+
table.insert(result, '===ConceptCardEffect===\n')
local toInsert = {}
+
local toInsert = {}
for k in pairs(jsonEffects) do table.insert(toInsert, k) end
+
for k in pairs(jsonEffects) do table.insert(toInsert, k) end
table.sort(toInsert)
+
table.sort(toInsert)
for i, k in ipairs(toInsert) do toInsert[i] = jsonEffects[k] end
+
for i, k in ipairs(toInsert) do toInsert[i] = jsonEffects[k] end
insertQueryResult(nil, toInsert)
+
h.insertQueryResult(result, nil, toInsert)
end
+
end
   
 
local buffDetailRows = cargo.query{
 
local buffDetailRows = cargo.query{
tables = 'BuffDetail',
+
tables = 'BuffDetail',
fields = 'buff_iname, server, idx, calc, type, tktag, vini, vmax, vone',
+
fields = 'buff_iname, server, idx, calc, type, tktag, vini, vmax, vone',
where = where,
+
where = where,
orderBy = 'idx'
+
orderBy = 'idx'
 
}
 
}
insertQueryResult('BuffDetail', buffDetailRows)
+
h.insertQueryResult(result, 'BuffDetail', buffDetailRows)
   
  +
 
 
-- Display unused keys here
 
-- Display unused keys here
 
local unused = false
 
local unused = false
Line 371: Line 513:
 
end end
 
end end
 
if unused then
 
if unused then
table.insert(result, "==Unused Keys==\nThe following keys were not used by inserted into any table and are thus unused.\n")
+
table.insert(result, "===Unused Keys===\nThe following keys were not used by inserted into any table and are thus unused.\n")
 
local toInsert = {}
 
local toInsert = {}
 
for k in pairs(jsonSets) do table.insert(toInsert, k) end
 
for k in pairs(jsonSets) do table.insert(toInsert, k) end
 
table.sort(toInsert)
 
table.sort(toInsert)
 
for i, k in ipairs(toInsert) do toInsert[i] = jsonSets[k] end
 
for i, k in ipairs(toInsert) do toInsert[i] = jsonSets[k] end
insertQueryResult(nil, toInsert)
+
h.insertQueryResult(result, nil, toInsert)
 
end
 
end
 
 
Line 382: Line 524:
 
-- Handle errors encountered
 
-- Handle errors encountered
 
if errors.cargoStore then
 
if errors.cargoStore then
table.insert(result, '[[Category:Data pages that failed a table insert]]' )
+
table.insert(result, '[[Category:Data pages that failed a table insert]]' )
end
+
end
if hasEffects then
+
if hasEffects then
table.insert(result, '[[Category:Data pages with effects]]')
+
table.insert(result, '[[Category:Data pages with effects]]')
end
+
end
   
 
return table.concat(result)
 
return table.concat(result)
Line 405: Line 547:
 
-----------
 
-----------
 
dataModel.query = function(tables, fields, args)
 
dataModel.query = function(tables, fields, args)
local query = args
+
local query = args
query.tables = tables
+
query.tables = tables
query.fields = fields
+
query.fields = fields
 
local result = cargo.query(query)
 
local result = cargo.query(query)
 
local toCast = {}
 
local toCast = {}
 
if type(query.tables) == 'string' then
 
if type(query.tables) == 'string' then
query.tables = split(query.tables, ',', true)
+
query.tables = split(query.tables, ',', true)
 
end
 
end
 
if type(query.fields) == 'string' then
 
if type(query.fields) == 'string' then
query.fields = split(query.fields, ',', true)
+
query.fields = split(query.fields, ',', true)
 
end
 
end
 
for _, field in ipairs(query.fields) do
 
for _, field in ipairs(query.fields) do

Revision as of 17:55, 8 January 2021

Documentation for this module may be created at Module:Data/doc

-- <nowiki>
local p = {}
local h = {}
local cargo = require('Module:CargoUtil')
local yesno = require('Module:Yesno')
local dataModel = require("Module:Data/Model")
local util_text = require('Module:TextUtil')
local split = util_text.split
local gsplit = util_text.gsplit
local trim = util_text.trim

local langs = {'english', 'japanese'}
local locPrefixes = {
	['Ability'] = true,
	['Artifact'] = true,
	['ConceptCard'] = true,
	['Item'] = true,
	['Job'] = true,
	['Skill'] = true,
	['Unit'] = true,
}
function h.getEntityType(args)
	local name = args.name or mw.title.getCurrentTitle().prefixedText
	return name:match('Data:Game/MasterParam/([^/]+)/.+')
end

function p.displayLocs(data)
	local locs = data.loc
	if not locs then return end
	local entityType = h.getEntityType(data)
	if not entityType or not locPrefixes[entityType] then return end

	local definitions = mw.loadData('Module:CargoDeclare/'..entityType..'Loc')

	-- HOTFIX: "otherdesc"
	local temp = {}
	for param, obj in pairs(locs) do
		if param == 'otherdesc' then
			param = 'desc_ot'
		end
		for lang, value in pairs(obj) do
			temp[param] = temp[param] or {}
			temp[param][lang] = value
		end
	end
	locs = temp

	-- Build localization wikitable
	local wikitable = mw.html.create('table'):addClass('wikitable')
	local tr = wikitable:tag('tr')
	tr:tag('th'):wikitext('param')
	for _, lang in ipairs(langs) do
		tr:tag('th'):wikitext(lang)
	end
	for _, definition in ipairs(definitions) do
		local field = definition.field
		if field and locs[field] then
			tr = wikitable:tag('tr')
			tr:tag('th'):wikitext(field)
			for _, lang in ipairs(langs) do
				tr:tag('td'):wikitext(locs[field][lang])
			end
		end
	end
	return tostring(wikitable)
end

function p.storeLocs(nocargo, data)
	local locs = data.loc
	if not locs then return end
	local entityType = h.getEntityType(data)
	if not entityType or not locPrefixes[entityType] then return end
	
	local iname = (data.gl or data.jp or {}).iname
	local definitions = mw.loadData('Module:CargoDeclare/'..entityType..'Loc')

	-- HOTFIX: "otherdesc"
	local temp = {}
	for param, obj in pairs(locs) do
		if param == 'otherdesc' then
			param = 'desc_ot'
		end
		for lang, value in pairs(obj) do
			temp[param] = temp[param] or {}
			temp[param][lang] = value
		end
	end
	locs = temp

	local rows = {}
	for _, definition in ipairs(definitions) do
		local field = definition.field
		if field and locs[field] then
			for lang, value in pairs(locs[field]) do
				rows[lang] = rows[lang] or {
					_table = entityType .. 'Loc',
					iname = iname,
					lang = lang,
				}
				rows[lang][field] = value
			end
		end
	end

	local result = {}
	-- Store localization
	if not yesno(nocargo) then
		for _, row in pairs(rows) do
			mw.logObject(row)
			result[#result+1] = cargo.store(row)
		end
	end
	return table.concat(result)
end

function h.getTitlePath()
    local curTitle = mw.title.getCurrentTitle()
    --if curTitle.prefixedText == "Module:Data" then curTitle = mw.title.new("Game/MasterParam/Unit/UN_V2_LOGI", "Data") end
    local titleNodes = {curTitle.subpageText}
    while curTitle.baseText ~= curTitle.text do
        curTitle = curTitle.basePageTitle
        table.insert(titleNodes, 1, curTitle.subpageText)
    end
    return titleNodes
end
-- Generates table for data page
function h.insertQueryResult(result, title, tbl)
    local fields = {}
    local found = {}
    local count = 0
    local hideCols = true
    for i, row in pairs(tbl) do
        count = count + 1
        for col in ipairs(row) do 
            if found[col] == nil then
                table.insert(fields, col)
                found[col] = true
            end
        end
        for col in pairs(row) do 
            if found[col] == nil then
                table.insert(fields, col)
                found[col] = true
                hideCols = false
            end
        end
    end
    table.sort(fields, function(a, b) return a == "server" or a == "lang" or (b ~= "server" and b ~= "lang" and (a or '') < (b or '')) end)
    if count == 0 then return nil end
    if title ~= nil then table.insert(result, "==="..title.."===\n") end
    table.insert(result, '{| class="wikitable"')
    for i, col in ipairs(fields) do
        table.insert(result, "\n|-")
        if not hideCols then
            table.insert(result, "\n! ")
            table.insert(result, col)
        end
        
        -- Merge matching rows
        local allSame = true
        local testVal = tbl[next(tbl)][col]
        for j, row in pairs(tbl) do
            if type(testVal) == 'table' and type(row[col]) == 'table' then
                for k, v in pairs(testVal) do if v ~= row[col][k] then allSame = false end end
                for k, v in pairs(row[col]) do if v ~= testVal[k] then allSame = false end end
            elseif row[col] ~= testVal then allSame = false end
        end
        
        local function writeVal(val)
            if type(val) == "table" then
                table.insert(result, "\n")
                h.insertQueryResult(result, nil, {val})
            else
                table.insert(result, tostring(val or ''))
            end
        end
        
        if allSame then
            table.insert(result, '\n| colspan="'..count..'" | ')
            writeVal(testVal)
        else
            for j, row in pairs(tbl) do
                table.insert(result, "\n| ")
                writeVal(row[col])
            end
        end
    end
    table.insert(result, "\n|}\n")
end
p.insertQueryResult = h.insertQueryResult

-- Will only actually perform the declares on the CargoTable template pages.
function p.init(frame)
    local args = require('Module:Arguments').getArgs(frame, {parentFirst=true})
    return p._init(args)
end
function p._init(args)
    local declareStatements = {}
    local recFunc
    function recFunc(node)
        if node.nodes ~= nil then for k, child in pairs(node.nodes) do recFunc(child) end end
        if node.model ~= nil then for tbl, model in pairs(node.model) do
            local statement = {"", _table = tbl, server = "String"}
            for col, colModel in pairs(model) do
                if type(colModel) == "string" then colModel = {colModel} end
                local modelType = colModel[1]
                if colModel.list ~= nil then modelType = "List ("..colModel.list..") of "..modelType end
                statement[col] = modelType
            end
            declareStatements[tbl] = statement
        end end
    end
    recFunc(dataModel)

    local curTitle = mw.title.getCurrentTitle()
    if args[1] then
        curTitle = mw.title.new(args[1])
    end
    if curTitle.basePageTitle.prefixedText == "Template:CargoTable" then
        local statement = declareStatements[curTitle.subpageText]
        if statement == nil then return "Invalid table "..curTitle.subpageText end
        local tbl = {'{{#cargo_declare:', '}}'}
        for k,v in pairs(statement) do
            if k ~= 1 then
                tbl[#tbl+1] = '| ' .. k..' = '..v
            end
        end
        table.sort(tbl)
        local declare = table.concat(tbl, '\n')
        if yesno(args.debug) then
            return tostring(mw.html.create('pre'):wikitext(declare))
        else
            mw.log(declare)
            return mw.getCurrentFrame():callParserFunction('#cargo_declare', statement)
        end
    end
    if mw.getCurrentFrame():getParent() == nil then
        return nil
    end
    return "asdf"
end

-- Called from Data pages
function p.insert(frame)
    local args = require('Module:Arguments').getArgs(frame, {parentFirst = true})
    return p._insert(args)
end
function p._insert(args)
    local nocargo = args.nocargo
    local curNode = {nodes = {Game = dataModel}}
    local titlePath = h.getTitlePath()
    for i, pathItem in ipairs(titlePath) do
        if curNode == nil then return nil end
        if i == #titlePath then break end
        if curNode.nodes == nil then return nil end
        curNode = curNode.nodes[pathItem]
    end
    
    if curNode.model == nil then return nil end
    -- Now that we know this is a valid page
    local result = {}

    if next(curNode.model) ~= nil then table.insert(result, "==Tables==\nThe following tables have these rows defined by this data entry.\n") end

    -- Decode JSON
    local jsonSets = {}
    for k, v in pairs(args) do
    	if type(v) == 'string' then
	    	v = mw.text.decode(mw.text.unstripNoWiki(v))
	    	local success, result = pcall(mw.text.jsonDecode, v)
	    	if success then
	    		v = result
	    	end
	    	args[k] = v
	    end
    end
    local content_sha256s = {}
    local suffix = '_sha256'
    for server, json in pairs(args) do
        if server == 'gl' or server == 'jp' or server == 'loc' then
            jsonSets[server] = json
        elseif string.sub(server, -string.len(suffix)) == suffix then
            -- TODO: Store this in a cargo table somewhere.
            content_sha256s[string.sub(server, 1, -string.len(suffix))] = json
        end
    end
    
    -- Convert Functions
    local convertFrom = {
        table = function(val, model)
            if model.list ~= nil then
                return table.concat(val, model.list)
            end
            error("JSON data for "..model[1].." is a table but is not modeled to handle it.")
        end,
    }
    
    -- Insert Rows
    local where = '_pageId='..mw.title.getCurrentTitle().id
    local errors = {}
    
    -- Do loc first
    result[#result+1] = p.storeLocs(nocargo, args)
    result[#result+1] = p.displayLocs(args)

    local locSets = jsonSets.loc or {}
    jsonSets.loc = nil
    local expected = 0
    table.insert(result, '\n'..mw.getCurrentFrame():expandTemplate{ title = "CargoTable/Loc" })

    for param, json in pairs(locSets) do
        if type(json) == "table" then
            for lang, value in pairs(json) do
                table.insert(result, cargo.store{
                    _table = 'Loc',
                    lang = lang,
                    param = param,
                    value = value,
                })
                expected = expected + 1
            end
        end
    end
    -- Query loc table
    local rows = cargo.query{
        tables = 'Loc',
        fields = 'lang, param, value',
        where = where,
        orderBy = 'lang'
    }
    -- Check if expected row count matches actual row count.
    local actual = #rows
    if actual ~= expected then
        table.insert(result, string.format('Entry count mismatch in table %q (%s expected, got %s)\n', 'Loc', expected, actual))
        errors.cargoStore = true
    end
    
    -- Generate loc tables from queried data
    local queryResult = {}
    for i, row in ipairs(rows) do
        if row.param == 'name' then
            local pagename = string.gsub(row.value, "[%[%]]", {
                ["["] = "【",
                ["]"] = "】",
            })
            if pagename ~= row.value then
                row.value = '[[' .. pagename .. '|' .. row.value .. ']]'
            else
                row.value = '[[' .. row.value .. ']]'
            end
        end
        queryResult[i] = row
    end
    h.insertQueryResult(result, tbl, queryResult)
    
    local iname
    for server, json in pairs(jsonSets) do
        if not iname and json.iname then
            iname = json.iname
        end
    end

    local actualTitle = table.concat(titlePath, '/', 1, 3)
    mw.logObject(actualTitle, 'actualTitle')
    if actualTitle == 'Game/MasterParam/Buff' then
    	local definitions = mw.loadData('Module:CargoDeclare/BuffDetail')
        for server, json in pairs(jsonSets) do
            for i=1,11 do
            	local values = {
            		_table = 'BuffDetail',
            	}
                for _, definition in ipairs(definitions) do
                	local field = definition.field
                	if definition.type == 'Integer' then
	                	values[field] = tonumber(json[field..i])
	                else
	                	values[field] = json[field..i]
	                end
                end
            	values.buff_iname = iname
            	values.idx = i
                if (values.type or 0) <= 0 then
                    mw.logObject(values, "Skipped insert into BuffDetail values")
                else
                	mw.logObject(values, "#cargo_store")
                    table.insert(result, cargo.store(values))
                end
            end
        end
    end

    -- Now get the data
    for tbl, model in pairs(curNode.model) do
        table.insert(result, mw.getCurrentFrame():expandTemplate{ title = "CargoTable/"..tbl })
        expected = 0
        for server, json in pairs(jsonSets) do
            local statement = {_table = tbl, server = server}
            for col, colModel in pairs(model) do
                if type(colModel) == "string" then colModel = {colModel} end
                -- Extract value
                local jsonVal = json[colModel.from or col]
                json[colModel.from or col] = nil
                -- Convert datatypes here
                local converter = convertFrom[type(jsonVal)]
                if converter ~= nil then jsonVal = converter(jsonVal, colModel) end
                -- Append to statement
                statement[col] = jsonVal
            end
            mw.logObject(statement, 'statement')
            table.insert(result, cargo.store(statement))
            expected = expected + 1
        end

        -- Query the data
        local query = {
            tables = tbl,
            fields = {"server"},
            where = where,
            orderBy = 'server',
        }
        for col in pairs(model) do
            table.insert(query.fields, col)
        end
        local rows = cargo.query(query)

        local actual = #rows
        if actual ~= expected then
            table.insert(result, string.format('Entry count mismatch in table %q (%s expected, got %s)\n', tbl, expected, actual))
            errors.cargoStore = true
        end
        
        -- Generate tables from queried data
        queryResult = {}
        for i, row in ipairs(rows) do
            for col, val in pairs(row) do
                if type(model[col]) == "table" then
                    if model[col].list ~= nil then
                        val = #val == 0 and {} or split(val, model[col].list, true)
                    end
                    if model[col].dataRef ~= nil then
                        if type(val) == "table" then
                            for k, v in pairs(val) do
                                val[k] = "[[Data:Game/"..model[col].dataRef.."/"..v.."|"..v.."]]"
                            end
                        elseif #val > 0 then
                            val = "[[Data:Game/"..model[col].dataRef.."/"..val.."|"..val.."]]"
                        end
                    end
                end
                row[col] = val
            end
            queryResult[i] = row
        end
        h.insertQueryResult(result, tbl, queryResult)
    end

    local hasEffects = false
    local jsonEffects = {}
    for server, json in pairs(jsonSets) do
        if json.effects then
            hasEffects = true
            jsonEffects[server] = {effects = {}}
            for i,effect in ipairs(json.effects) do
                local values = {}
                values._table = 'ConceptCardEffect'
                values.server = server
                values.cc_iname = iname
                jsonEffects[server].effects[i] = {}
                for k,v in pairs(effect) do
                    values[k] = v
                    jsonEffects[server].effects[i][k] = v
                    effect[k] = nil
                end
                table.insert(result, cargo.store(values))
                jsonEffects[server].server = server
            end
            -- A table of failed effect insertions.
            local errorEffects = {}
            for i, effect in ipairs(json.effects) do
                local empty = true
                for k, v in pairs(effect) do empty = false end
                if not empty then
                    errorEffects[#errorEffects+1] = effect
                end
            end
            json.effects = #errorEffects > 0 and errorEffects or nil
        end
    end
    
    if next(jsonEffects) then
        table.insert(result, '===ConceptCardEffect===\n')
        local toInsert = {}
        for k in pairs(jsonEffects) do table.insert(toInsert, k) end
        table.sort(toInsert)
        for i, k in ipairs(toInsert) do toInsert[i] = jsonEffects[k] end
        h.insertQueryResult(result, nil, toInsert)
    end

    local buffDetailRows = cargo.query{
        tables = 'BuffDetail',
        fields = 'buff_iname, server, idx, calc, type, tktag, vini, vmax, vone',
        where = where,
        orderBy = 'idx'
    }
    h.insertQueryResult(result, 'BuffDetail', buffDetailRows)

    
    -- Display unused keys here
    local unused = false
    for server, json in pairs(jsonSets) do if next(json) ~= nil then
        unused = true
        json.server = server
    end end
    if unused then
        table.insert(result, "===Unused Keys===\nThe following keys were not used by inserted into any table and are thus unused.\n")
        local toInsert = {}
        for k in pairs(jsonSets) do table.insert(toInsert, k) end
        table.sort(toInsert)
        for i, k in ipairs(toInsert) do toInsert[i] = jsonSets[k] end
        h.insertQueryResult(result, nil, toInsert)
    end
    
    table.insert(result, '[[Category:Data pages]]') -- Tracking category.
    -- Handle errors encountered
    if errors.cargoStore then
        table.insert(result, '[[Category:Data pages that failed a table insert]]' )
    end
    if hasEffects then
        table.insert(result, '[[Category:Data pages with effects]]')
    end

    return table.concat(result)
end

-- Add functions to model
p.model = (function()
    dataModel.tables = {}
    local recFunc
    function recFunc(node)
        if node.nodes ~= nil then for k, child in pairs(node.nodes) do recFunc(child) end end
        if node.model ~= nil then for tbl, model in pairs(node.model) do dataModel.tables[tbl] = model end end
    end
    recFunc(dataModel)
    
    -----------
    -- Query --
    -----------
    dataModel.query = function(tables, fields, args)
        local query = args
        query.tables = tables
        query.fields = fields
        local result = cargo.query(query)
        local toCast = {}
        if type(query.tables) == 'string' then
            query.tables = split(query.tables, ',', true)
        end
        if type(query.fields) == 'string' then
            query.fields = split(query.fields, ',', true)
        end
        for _, field in ipairs(query.fields) do
            -- Extract alias
            local t = split(field, '=', true)
            local alias = trim(t[2] or t[1])
            
            local fnMatch = string.match(t[1], "([^()]*)[(](.*)[)](.*)")
            if fnMatch ~= nil then
                mw.log(fnMatch)
                toCast[alias] = {"String"}
            else
                -- Extract table
                local t2 = split(t[1], '.', true)
                local tbl = #t2 > 1 and trim(t2[1]) or nil
                local key = trim(t2[#t2])
                if tbl == nil then
                    for _, tblName in ipairs(query.tables) do
                        tblName = trim(tblName)
                        if (dataModel.tables[tblName] or {})[key] ~= nil then
                            tbl = tblName
                            break
                        end
                    end
                end
                local model = (dataModel.tables[tbl] or {})[key]
                toCast[alias] = type(model) == "string" and {model} or model
            end
        end
        local castFuncs = {
            Integer = tonumber
        }
        for i, row in ipairs(result) do
            for k, castTo in pairs(toCast) do
                local castFunc = castFuncs[castTo[1]] or function(v) return v end
                if castTo.list ~= nil then
                    local list = {}
                    if row[k] and row[k] ~= '' then
                        for s in gsplit(row[k], castTo.list, true) do
                            list[#list+1] = castFunc(s)
                        end
                    end
                    row[k] = list
                else
                    row[k] = castFunc(row[k])
                end
            end
        end
        return result
    end
    
    ------------
    -- getLoc --
    ------------
    dataModel.getLoc = function(_pageName, param, lang)
        if _pageName == nil then return '???' end
        local where = "_pageName='".._pageName.."' and param='"..param.."' and value<>'' and lang='"
        local thisLang = cargo.query{tables='Loc',fields='value',where=where..(lang or "english'")}
        if #thisLang == 0 then thisLang = cargo.query{tables='Loc',fields='value',where=where.."english'"} end
        if #thisLang == 0 then thisLang = cargo.query{tables='Loc',fields='value',where=where.."japanese'"} end
        if #thisLang == 0 then thisLang = {{value = ''}} end
        return thisLang[1].value
    end

    -------------
    -- Sources --
    -------------
    dataModel.sources = require("Module:Data/Sources")(dataModel)
    
    return dataModel
end)()

return p