scene_nodes = {}
local scene_nodes_mt = {}


local function GetView(viewportName)
    local view

    if viewportName then
        local viewport = MystViewportManager:GetInstance():GetViewport(viewportName)

        if viewport then
            view = viewport:GetView()
        end
    end

    if not view then
        viewportName = "MainViewport"
        view = MystViewportManager:GetInstance():GetViewport(viewportName):GetView()
    end

    return view, viewportName
end


function scene_nodes_mt.__index(t, key)
    local viewportName = rawget(t, "viewportName")
    local view, viewportName = GetView(viewportName)

    local n = view:GetNode(key)
    if not n then
        return nil
    end
    local moduleName = view:GetName() .. "." .. key
    if package.loaded[moduleName] == nil then
        n:CreateModule(viewportName)
    end
    local module = package.loaded[moduleName]

    module.As = function(typename) return tolua.cast(n, typename) end

    module.BringToFront = function(nameFilter)
        local maxord = 0
        local nord = n:GetZOrder()
        for k, v in scene_nodes() do
            if v:GetNodeType() == EMystNodeSprite2D then
                if (not nameFilter) or (nameFilter and string.find(k, nameFilter)) then
                    local vord = v:GetZOrder()
                    if vord > maxord then
                        maxord = vord
                    end
                    if vord > nord then
                        v:SetZOrder(vord - 1)
                    end
                end
            end
        end
        n:SetZOrder(maxord)
    end

    module.ActivateEffect = function(scale, offsetX, offsetY)
        tolua.cast(n, "MystRegion2D")
        local numPoints = n:GetPointCount()

        if numPoints == 0 then
            return
        end

        local left, right, top, bottom
        local center = Ogre.Vector2()
        local name = n:GetName()
        local effectWidth = 50
        local gameWidth = Game:GetWindowWidth()
        local gameHeight = Game:GetWindowHeight()

        for i = 0, numPoints - 1 do
            p = n:GetPoint(i)

            if not left or p.x < left then
                left = p.x
            end

            if not right or p.x > right then
                right = p.x
            end

            if not top or p.y < top then
                top = p.y
            end

            if not bottom or p.y > bottom then
                bottom = p.y
            end
        end

        center.x = left + (right - left) / 2
        center.y = top + (bottom - top) / 2

        if offsetX then
            center.x = center.x + offsetX / gameWidth
        end

        if offsetY then
            center.y = center.y + offsetY / gameHeight
        end

        if not scale then
            scale = (gameWidth * right - gameWidth * left) / effectWidth
        end

        if type(scale) == "number" then
            scale = Ogre.Vector3(scale, scale, scale)
        end
        
        Particle:Add("ra_"..name, "region_activate", nil, Ogre.Vector3(0, 0, 0), Ogre.Quaternion(1, 0, 0, 0), scale)
        Particle:SetPosition("ra_"..name, center, 6000)
    end

    tolua.setpeer(n, module)

    local ntype = n:GetNodeType()
    if ntype == EMystNodeSprite2D then
        n.As("MystSprite2D")
    elseif ntype == EMystNodeRegion2D then
        n.As("MystRegion2D")
    end

    return n
end


setmetatable(scene_nodes, scene_nodes_mt)


viewports = {}
local viewports_mt = {}


function viewports_mt.__index(table, key)
    local t = {scene_nodes = {viewportName = key}}
    setmetatable(t.scene_nodes, scene_nodes_mt)
    return t
end


setmetatable(viewports, viewports_mt)


local function spair(it, key)
    local n = table.remove(it.nodes)

    if n then
        local name = n:GetName()
        return name, viewports[it.viewportName].scene_nodes[name]
    end
end


function scene_nodes_mt.__call(t)
    local viewportName = rawget(t, "viewportName")
    local view
    view, viewportName = GetView(viewportName)
    local n
    local it = {}
    it.nodes = {}
    it.viewportName = viewportName

    table.insert(it.nodes, view:GetFirstNode())

    repeat
        n = view:GetNextNode()
        table.insert(it.nodes, n)
    until not n

    return spair, it, 0
end
