local roam = require 'util.roam'
local util = require 'util'

local vip_data = serialize.define({
    {"notes", "bool"}
})
local notify_data = serialize.define({
    {"money", "u32"},
    {"rating", "u32"}
})

function find_door(objs)
    for idx, obj in ipairs(objs) do
        for _, act in ipairs(obj.actions) do
            if act.type == "wall" and act.flag == "door" then
                table.remove(objs, idx)
                return act
            end
        end
    end
    -- Can't find anything
    return nil
end

function server_handler()
    local rooms = free_roam.rooms_for_player()

    -- Visit rooms in a random order.
    -- The vip only cares about rooms placed when
    -- they started the visit.
    util.table_shuffle(rooms)

    local visited_rooms = 0
    local active_rooms = 0
    local stuck_rooms = 0
    local seen_students = 0

    for _, v in ipairs(rooms) do
        -- Only visit classes
        if v.used_for_teaching and v.valid then
            local objs = v.objects
            util.table_shuffle(objs)

            local done_visit = false

            function do_visit()
                if done_visit then
                    return
                end
                local e = free_roam.get_entity()
                done_visit = true
                visited_rooms = visited_rooms + 1
                if #v.visitors > 0 then
                    active_rooms = active_rooms + 1
                    seen_students = seen_students + #v.visitors
                elseif v.has_waiting then
                    stuck_rooms = stuck_rooms + 1
                end
                e.state = vip_data.encode({
                    notes = true,
                });
                roam.wait_ticks(20 * math.random(10, 20))
                local e = free_roam.get_entity()
                e.state = vip_data.encode({
                    notes = false,
                });
                roam.wait_ticks(60)
            end

            while true do
                local obj = find_door(objs)
                if obj == nil then
                    break
                end

                local ox, oy = direction.offset(obj.direction)
                local target_x = obj.location.x + 0.5 + ox
                local target_y = obj.location.y + 0.5 + oy

                if level.get_room_type_at(target_x, target_y) == "base:building" then
                    target_x = obj.location.x + 0.5
                    target_y = obj.location.y + 0.5
                    -- Look into the room through the door
                    local rotation = 0.0
                    if obj.direction == "north" then
                        rotation = math.pi
                    elseif obj.direction == "east" then
                        rotation = -math.pi * 1.5
                    elseif obj.direction == "west" then
                        rotation = -math.pi / 2.0
                    end

                    roam.walk_to_face(target_x, target_y, -rotation)

                    local e = free_roam.get_entity()
                    local ex, ey = e:get_position()

                    if math.abs(ex - target_x) < 1.5 and math.abs(ey - target_y) < 1.5 then
                        do_visit()
                        break
                    end
                end
            end

            if not done_visit then
                local ox, oy = v:get_position()
                roam.walk_to_face(ox + 1, oy + 1, 0)

                local e = free_roam.get_entity()
                local ex, ey = e:get_position()

                if math.abs(ex - (ox + 1)) < 1.5 and math.abs(ey - (oy + 1)) < 1.5 then
                    do_visit()
                end
            end
        end
    end

    local score = visited_rooms * 100
        + active_rooms * 400
        - stuck_rooms * 500
        + seen_students * 20

    local money = score * 5.0
    local rating = score * 0.75
    free_roam.notify_player("other.vip#notify", notify_data.encode({
        money = money,
        rating = rating,
    }))
    local e = free_roam.get_entity()
    e:give_money("vip", money)
    e:give_rating("vip", rating)

    e:quit()
end

function notify(data)
    local data = notify_data.decode(data)
    local desc = ui.new("description")
    desc:add_child(ui.new_text("Results: $" .. tostring(data.money) .. " and " .. tostring(data.rating) .. " rating"))
    return {
        icon = "ui/icons/inspection",
        title = "Inspection Results",
        description = desc,
    }
end

function client_handler()
    while true do
        local e = free_roam.get_entity()
        if e.state ~= nil then
            local data = vip_data.decode(e.state)
            e.properties['taking_notes'] = e.properties['taking_notes'] or false
            if e.properties['taking_notes'] and not data.notes then
                e:play_animation("take_notes_stop")
                e.properties['taking_notes'] =  false
            elseif (not e.properties['taking_notes'] or e.animation ~= "take_notes") and data.notes then
                e:play_animation("take_notes")
                e.properties['taking_notes'] = true
            end
        end
        free_roam.wait()
    end
end