local roam = require 'util.roam'
local util = require 'util'
local objects = require 'objects'
local plants = require 'objects.plants'

local janitor_data = serialize.define({
    {"task", "u4"}
})

local TASK_NONE = 0
local TASK_WATERING = 1
local TASK_SWEEPING = 2

function server_handler()
    while true do
        local rooms = free_roam.rooms_for_player()
        util.table_shuffle(rooms)

        local done_task = false
        if math.random(25) ~= 1 then
            for _, room in ipairs(rooms) do
                local objs = room.objects
                local room_x, room_y = room:get_bounds()
                util.table_shuffle(objs)
                for _, obj in ipairs(objs) do
                    if objects.get_properties(obj.key).plant then
                        -- Try to water the plant
                        if not obj.properties['claimed'] and (obj.properties['water_level'] or 1200) <= 20 * 30 then
                            obj.properties['claimed'] = true
                            done_task = true
                            local ox, oy = obj.entities[1]:get_position()
                            ox = ox + room_x
                            oy = oy + room_y
                            local ow, oh = 0.7, 0.7

                            local rotation = math.random(0, math.pi * 2)
                            local target_x = ox + ow * math.cos(rotation)
                            local target_y = oy + oh * math.sin(rotation)

                            roam.walk_to_face(target_x, target_y, -rotation + math.pi * 0.5)

                            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
                                e.state = janitor_data.encode({
                                    task = TASK_WATERING,
                                })
                                roam.wait_ticks(20 * math.random(6, 13))
                                e.state = janitor_data.encode({
                                    task = TASK_NONE,
                                })
                                obj.properties['water_level'] = 20 * math.random(5 * 60, 20 * 60)
                                room:execute_command("update_water_level", plants.water_data.encode({
                                    object_idx = obj.id,
                                    water_level = obj.properties['water_level'],
                                }))
                                roam.wait_ticks(30)
                                obj.properties['claimed'] = nil
                            end
                            break
                        end
                    end
                end
                if done_task then
                    break
                end
            end
        end
        if not done_task then
            local e = free_roam.get_entity()

            if #rooms == 0 then
                local x, y = e:get_position()
                -- Idle for a bit
                roam.walk_to(x + math.random(-8, 8), y + math.random(-8, 8))
            else
                local trash_task = free_roam.get_idle_task("base:drop_trash")
                if trash_task ~= nil then
                    local e = free_roam.get_entity()
                    local x, y = e:get_position()
                    -- TODO: Use distance checks instead of doing it in order
                    if #(trash_task.trash_list or {}) > 0 then
                        local trash = table.remove(trash_task.trash_list, 1)

                        local rotation = math.random(0, math.pi * 2)
                        local target_x = trash.x + 0.5 * math.cos(rotation)
                        local target_y = trash.y + 0.5 * math.sin(rotation)

                        if roam.walk_to_face(target_x, target_y, -rotation + math.pi * 0.5) then
                            e.state = janitor_data.encode({
                                task = TASK_SWEEPING,
                            })
                            roam.wait_ticks(20 * (200/60) * 2)
                            -- Remove the trash
                            local to_remove = trash_task.to_remove or {}
                            table.insert(to_remove, trash)
                            trash_task.to_remove = to_remove

                            roam.wait_ticks(20 * (200/60) * 2)
                            e.state = janitor_data.encode({
                                task = TASK_NONE,
                            })
                            roam.wait_ticks(30)
                        else
                            table.insert(trash_task.trash_list, trash)
                            roam.wait_ticks(20)
                        end
                    else
                        roam.wait_ticks(20 * 4)
                    end
                else
                    roam.wait_ticks(20 * 4)
                end
            end
        end
        free_roam.wait()
    end
end

if is_client then
    local rad = math.rad;
    local watering_can_matrix = new_matrix()
        :rotate_x(rad(180))
        :rotate_y(rad(-60))
        :translate(0.0, -0.2, 0.0)

    local broom_matrix = new_matrix()
        :rotate_y(rad(-25))
        :rotate_z(rad(-90))
        :translate(-0.1, -4.9, -0.1)

    function client_handler()
        local current_item

        local task_handlers = {
            [TASK_NONE] = function()
            end,
            [TASK_WATERING] = function()
                local e = free_roam.get_entity()
                e:play_animation("watering")

                if current_item ~= nil then
                    current_item:remove()
                    current_item = nil
                end
                current_item = e:add_attachment("base:janitor/watercan", "LeftAttachPoint", watering_can_matrix)
            end,
            [TASK_SWEEPING] = function()
                local e = free_roam.get_entity()
                e:play_animation("sweeping")

                if current_item ~= nil then
                    current_item:remove()
                    current_item = nil
                end
                current_item = e:add_attachment("base:janitor/broom", "LeftAttachPoint", broom_matrix)
            end,
        }
        local task_cancel = {
            [TASK_NONE] = function()
            end,
            [TASK_WATERING] = function()
                local e = free_roam.get_entity()
                e:play_animation("watering_stop")

                roam.wait_ticks(20)
                if current_item ~= nil then
                    current_item:remove()
                    current_item = nil
                end
            end,
            [TASK_SWEEPING] = function()
                local e = free_roam.get_entity()
                e:play_animation("sweeping_stop")

                roam.wait_ticks(20)
            end,
        }

        while true do
            local e = free_roam.get_entity()
            if e.state ~= nil and not e.is_walking then
                local data = janitor_data.decode(e.state)
                e.properties['task'] = e.properties['task'] or TASK_NONE

                if data.task ~= e.properties['task'] then
                    task_cancel[e.properties['task']]()
                    e.properties['task'] = data.task
                    task_handlers[data.task]()
                end
            end
            free_roam.wait()
        end
    end
end