--load all module
if not cjson then cjson = require "cjson" end
if not JSONPath then JSONPath = require 'JSONPath' end

if not JSONPath then 
  error("exception: module require.")
end

local walk,_M

local function arraylen(tab)
  if type(tab) ~= 'table' then return 0 end  
  for i,v in pairs(tab) do
    if type(i) ~= 'number' then return 0 end
    if i > #tab then return 0 end
  end
  return #tab
end

local function seekSingle(data, pathStr, result, key)
  local seek = JSONPath.query(data, pathStr) or {}
  if #seek > 0 then 
    result[key] = seek[1]
  end
end

local function seekArray(data, pathArr, result, key)
  -- support pyhton list [[]] produces  
  if arraylen(pathArr) == 1 and arraylen(pathArr[1]) > 0  then 
    if key then
      result[key]={}
      result =result[key]
    end  
    for index,item in ipairs(pathArr[1]) do  
	  walk(data, item, result, index)
    end
	return  -- [[]] array only support once
  end

  local subpath = pathArr[2]
  local path = pathArr[1]
   
  local seek = JSONPath.query(data, path) or {}
  if #seek > 0 and subpath then 
    result[key]={}
    result =result[key]
-- distinguish in $.array return [[{},{}]]
-- distinguish in $..some return [{},{}]
    if arraylen(seek) > 0 and arraylen(seek[1]) > 0 then seek=seek[1] end
    for index,item in ipairs(seek) do  
      walk(item, subpath, result, index)
    end
  else
    result[key] = seek
  end
end

local function seekObject(data, pathObj, result, key)
  if key then
    result[key]={}
    result =result[key]
  end
  for name in pairs(pathObj) do 
    walk(data, pathObj[name], result, name)
  end
end

-- local call
function walk(data, path, result, key)
  local func = type(path)
  
  if func == 'string' then 
    if string.find(path,' *$[.[]') then return seekSingle(data, path, result, key) end
    result[key] = path --directly transfrom value
	return 
  elseif arraylen(path) > 0 then 
    return seekArray(data, path, result, key)
  elseif func == 'table' then 
    return seekObject(data, path, result, key)
  end
end

--main function 
function _M(data, template)
  local result = {}
  walk(data, template, result)
  return result
end

return _M

--[[
testing code 
local data11 = {
  some={
    {
      example= 'A'
    }
  }
}

local data = {
  some={
    crazy={
      {
        example= 'A'
      },
      {
        example= 'B'
      }
    }
  }
}

local path = {
  foo= {'$.some.crazy', { bar ='$.example'} }
}

local path11 = {
  foo= {'$.some',{bar='$.example'}}
}

local result = transform(data, path)

print(result)
print(JSONPath:encode(result))

]]--