require "luascape.reflect"

reflect.loadReflectors("objects")
reflect.makeNamespaceGlobal("scape::objects")

objects = {}
  function objects.getVisuals(root, visuals)
    if not visuals then visuals = {} end
    if root.visual then
      table.insert(visuals, root.visual)
    end
    for i,child in pairs(root.children) do
      objects.getVisuals(child, visuals)
    end
    return visuals
  end
  
  function objects.getVisualMaterials(visual, materials)
    if not materials then materials = {} end
    if visual.material then table.insert(materials, visual.material) end
    for i,subMesh in pairs(visual.subMeshes) do
      local material = subMesh.material
      if material then table.insert(materials, material) end
    end
    return materials
  end
  
  function objects.findVisualMaterial(visual)
    local materials = objects.getVisualMaterials(visual)
    if #materials then return materials[1] end
  end
  
  function objects.getNodeMaterials(root, materials)
    if not materials then materials = {} end
    local visuals = objects.getVisuals(root)
    for i,visual in pairs(visuals) do
      objects.getVisualMaterials(visual, materials)
    end
    return materials
  end
  
  function objects.findNodeMaterial(root)
    local materials = objects.getNodeMaterials(root)
    if #materials then return materials[1] end
  end
  
  function objects.findNodes(rootNode, testFunc, nodes)
    if not nodes then nodes = {} end
    if rootNode then
      if testFunc(rootNode) then table.insert(nodes, rootNode) end
      for i,child in pairs(rootNode.children) do
        objects.findNodes(child, testFunc, nodes)
      end
    end
    return nodes
  end
  
  function objects.findNodeByName(node, name)
    for i,child in pairs(node.children) do
      if child.name == name then
        return child
      end
    end
    return nil
  end
  
  function objects.getMaterials(root)
    local rootType = TypeRegistry.getType(root)
    if rootType:isDerivedFrom(Node) then
      return objects.getNodeMaterials(root)
    elseif rootType:isDerivedFrom(Visual) then
      return objects.getVisualMaterials(root)
    end
  end
  
  function objects.findMaterial(root)
    local materials = objects.getMaterials(root)
    if #materials then return materials[1] end
  end
  
  function objects.findRenderState(root, id)
    material = objects.findMaterial(root)
    if material then return material.shader:getRenderState(id) end
  end

  function objects.applyRenderState(root, renderState1, renderState2)
    local materials = objects.getMaterials(root)
    
    if materials then
      for i,material in pairs(materials) do
        local shader = material:shader()
        if (shader) then
          shader:setRenderStateByID(renderState1)
          if renderState2 then
            shader:setRenderStateByID(renderState2)
          end
        end
      end
    end
  end
  
  function objects.setSceneProperty(root, propertyName, value)
    root[propertyName] = value
    for i,child in pairs(root.children) do
      objects.setSceneProperty(child, propertyName, value)
    end
  end

  function objects.createSkyBox(textureFilename, extension, size)
    local sides = { "front", "back", "left", "right", "top", "bottom" }
    local textureNames = {}
    for i, side in pairs(sides) do
      textureNames[i] = textureFilename .. side .. "." .. extension
    end
  
    if not size then size = 1 end
    local skybox = SkyBox()
    skybox:loadTextures(textureNames[1], textureNames[2], textureNames[3], textureNames[4], textureNames[5], textureNames[6])
    local depthBufferState = skybox.material:getOrCreateDepthBufferState()
    depthBufferState.enabled = true
    return skybox
  end
  
  function objects.applyModifier(root, modifier)
    local visuals = objects.getVisuals(root)
    for i,visual in pairs(visuals) do
      modifier:apply(visual)
    end
  end
  
  function objects.applyFilter(root, filter)
    if root.visual then
      local mesh = Mesh()
      mesh:loadVisual(root.visual)
      filter:filter(mesh.vertexBuffer)
      root.visual = mesh
    end
    for i,child in pairs(root.children) do
      objects.applyFilter(child, filter)
    end
  end
  
  function objects.applyMaterial(root, material)
    local visuals = objects.getVisuals(root)
    for i,visual in pairs(visuals) do
      visual.material = material
      for j,subMesh in pairs(visual.subMeshes) do
        subMesh.material = nil
      end
    end
  end

