local gui = require("gui")
local events = import("events", "local")

local cameras = {}

local function make_camera(x, y, w, h)
    local camera = {
        base = gui.make_camera(w, h)
    }
    camera.base.type = "perspective"
    camera.base.fov = 45
    camera.base.height = 300
    camera.base.speed = 500
    camera.base.scroll_speed = 10
    camera.rotationY = 0
    camera.angleY = 45
    camera.active = true
    if x then
        camera.base.x = x
    else
        camera.base.x = 0
    end
    if y then
        camera.base.y = y
    else
        camera.base.y = 0
    end

    function camera.update(v)
        if v then
            camera.base.target = v
        else
            v = camera.base.target
        end
        local x = v.x - camera.base.height * 0.50 * math.cos(camera.rotationY)
        local y = v.y + camera.base.height * 1.00 * math.sin(camera.angleY)
        local z = v.z - camera.base.height * 0.50 * math.sin(camera.rotationY)
        camera.base.position = {x, y, z}
    end

    function camera.move(v)
        local x = v.x * math.cos(camera.rotationY) - v.z * math.sin(camera.rotationY)
        local z = v.x * math.sin(camera.rotationY) + v.z * math.cos(camera.rotationY)
        local t = camera.base.target
        return { x = t.x + x, y = t.y, z = t.z + z }
    end

    function camera.apply()
        camera.base:apply()
    end

    cameras[#cameras + 1] = camera
    camera.update()

    return setmetatable(camera, {
        __index = function(t, k)
            return t.base[k]
        end,
        __newindex = function(t, k, v)
            t.base[k] = v
        end
    })
end

local main = make_camera()

events.update += function(dt)
    for _, camera in pairs(cameras) do
        if not camera.active then
            return
        end
        if gui.keyboard.held.W then
            camera.update(camera.move({ x = dt*camera.base.speed, z = 0 }))
        end
        if gui.keyboard.held.S then
            camera.update(camera.move({ x = -dt*camera.base.speed, z = 0 }))
        end
        if gui.keyboard.held.A then
            camera.update(camera.move({ x = 0, z = -dt*camera.base.speed }))
        end
        if gui.keyboard.held.D then
            camera.update(camera.move({ x = 0, z = dt*camera.base.speed }))
        end
        if gui.keyboard.held.E then
            camera.rotationY = camera.rotationY + dt*5
            camera.update()
        end
        if gui.keyboard.held.Q then
            camera.rotationY = camera.rotationY - dt*5
            camera.update()
        end
        if gui.mouse.ScrollWheel ~= 0 then
            camera.base.height = camera.base.height - gui.mouse.ScrollWheel / 120 * camera.base.scroll_speed
            gui.mouse:ResetScrollWheelValue()
            camera.update()
        end
        if gui.mouse.RightButton then
            if gui.mouse.mode == 0 then
                gui.mouse.mode = 1
                return
            end
            camera.angleY = camera.angleY + gui.mouse.y / 1000 * math.pi / 2
            camera.angleY = math.max(math.pi / 32, math.min(math.pi / 2, camera.angleY))
            camera.rotationY = camera.rotationY + gui.mouse.x / 1000 * math.pi / 2
            camera.update()
        else
            gui.mouse.mode = 0
        end
    end
end

return setmetatable({
    main = main,
    new = make_camera
}, {
    __index = main,
    __newindex = main
})