--[[ Copyright (c) 2009 Edvin "Lego3" Linge

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. --]]

local TH = require "TH"

--! An `Object` which needs occasional repair (to prevent explosion).
class "Machine" (Object)

function Machine:Machine(world, object_type, x, y, direction, etc)
  
  self.total_usage = -1 -- Incremented in the constructor of Object.
  self:Object(world, object_type, x, y, direction, etc)
  
  -- Initialize machines with strength values. (TODO: according to current research)
  local config
  local level = world.map.level_config
  local id = self:getRoom().room_info.level_config_id
  if level and id and level.objects[id]
  and level.objects[id].StartStrength then
    config = level.objects[id]
  end
  self.strength = config and config.StartStrength or object_type.default_strength
  
  -- We actually don't want any dynamic info just yet
  self:clearDynamicInfo()
  -- TODO: Smoke, 3424
  -- Change hover cursor once the room has been finished.
  local callback
  callback = --[[persistable:machine_build_callback]] function(room)
    if room.objects[self] then
      self:finalize(room)
      self.world:unregisterRoomBuildCallback(callback)
    end
  end
  local orientation = object_type.orientations[direction]
  local handyman_position = orientation.handyman_position
  if handyman_position then
  -- If there are many possible handyman tiles, choose one that is accessible from the use_position.
    if type(handyman_position[1]) == "table" then
      for _, position in ipairs(handyman_position) do
        local hx, hy = x + position[1], y + position[2]
        local ux, uy = x + orientation.use_position[1], y + orientation.use_position[2]
        if world.pathfinder:findDistance(hx, hy, ux, uy) then
          -- Also make sure the tile is not in another room or in the corridor.
          local room = world:getRoom(hx, hy)
          if room and room == self:getRoom() then
            self.handyman_position = {position[1], position[2]}
            break
          end
        end
      end
    else
      self.handyman_position = {handyman_position[1], handyman_position[2]}
    end
  else
    self.handyman_position = {orientation.use_position[1], orientation.use_position[2]}
  end
  self.world:registerRoomBuildCallback(callback)
end

function Machine:setCrashedAnimation()
  self:setAnimation(self.object_type.crashed_animation)
end

function Machine:machineUsed(room)
  self:updateDynamicInfo()
  local threshold = self.times_used/self.strength
  if threshold > 0.85 then
    room:crashRoom()
    self:setCrashedAnimation()
    self.hover_cursor = nil
    self:clearDynamicInfo()
    local window = self.world.ui:getWindow(UIMachine)
    if window and window.machine == self then
      window:close()
    end
    if self.handyman_to_repair then
      local hand = self.handyman_to_repair
      hand:setNextAction{name = "meander"}
      hand:setDynamicInfoText("")
      self:setRepairing(false)
    end
    return true
  elseif threshold > 0.65 then
    self.world:callForStaff(room, self, true)
    -- TODO: 3428 is smoke, add it when additional objects can be made
  elseif threshold > 0.35 then
    self.world:callForStaff(room, self) 
  end
end

function Machine:getRepairTile()
  local x = self.tile_x + self.handyman_position[1]
  local y = self.tile_y + self.handyman_position[2]
  return x, y
end

function Machine:createRepairAction(handyman)
  if self.approaching_handyman then
    local fixer = self.approaching_handyman
    -- Remove the is_job flag for the other handyman to show
    -- that someone else is on it.
    fixer.action_queue[1].is_job = nil
    if fixer:getRoom() then
      fixer:setNextAction(fixer:getRoom():createLeaveAction())
      fixer:queueAction{name = "meander"}
    else
      fixer:setNextAction{name = "meander"}
    end
    fixer:setDynamicInfoText("")
  end
  self.approaching_handyman = handyman
  local --[[persistable:handyman_repair_after_use]] function after_use()
    self:machineRepaired(self:getRoom())
    handyman:setDynamicInfoText("")
    -- Let's check if any plants need watering in here.
    for object in pairs(self.world:findAllObjectsNear(self.tile_x, self.tile_y)) do
      if class.is(object, Plant) and object:needsWatering() and not object.reserved_for then
        object:createHandymanActions(handyman)
        break
      end
    end
  end
  return {
    name = "use_object",
    object = self,
    must_happen = true,
    prolonged_usage = false,
    loop_callback = --[[persistable:handyman_repair_loop_callback]] function()
      action_use.prolonged_usage = false
    end,
    after_use = after_use,
    is_job = handyman,
  }
end

function Machine:machineRepaired(room)
  room.needs_repair = nil
  self.approaching_handyman = nil
  local str = self.strength
  if self.times_used/str > 0.55 then
    self.strength = str - 1
  end
  self.times_used = 0
  self:setRepairing(false)
  self:updateDynamicInfo(true)
end

--! Tells the machine to start showing the icon that it needs repair.
--!param repairer The handyman set to do the task
function Machine:setRepairing(repairer)
  self.handyman_to_repair = repairer
  local anim = {icon = 4564} -- The only icon for machinery
  self:setMoodInfo(repairer and anim or nil)
  if repairer then
    self.ticks = true
  else
    self.ticks = self.object_type.ticks
  end
end

function Machine.slaveMixinClass(class_method_table)
  local slave_to_master, master_to_slave = Object.slaveMixinClass(class_method_table)
  master_to_slave("finalize")
  master_to_slave("setCrashedAnimation")
  slave_to_master("createRepairAction")
  slave_to_master("getRepairTile")
  return slave_to_master, master_to_slave
end

-- Currently used to make the hover cursor of the machine be special
-- only if the room is active at the moment (e.g. not being edited)
function Machine:finalize(room)
  if room.is_active then
    self.hover_cursor = TheApp.gfx:loadMainCursor("repair")
    self:updateDynamicInfo(true)
  else
    self:clearDynamicInfo()
    self.hover_cursor = nil
  end
end

function Machine:updateDynamicInfo(only_update)
  if not only_update then
    self.times_used = self.times_used + 1
    self.total_usage = self.total_usage + 1
  end
  if self.strength then
    self:setDynamicInfo("text", {
      self.object_type.name, 
      _S.dynamic_info.object.strength:format(self.strength),
      _S.dynamic_info.object.times_used:format(self.times_used),
    })
  end
end

function Machine:onClick(ui, button)
  local room = self:getRoom()
  if button == "left" and room.is_active then
    -- If the room is crashed is_active is false.
    ui:addWindow(UIMachine(ui, self, room))
  else
    Object.onClick(self, ui, button)
  end
end

function Machine:onDestroy()
  Object.onDestroy(self)
end

function Machine:afterLoad(old, new)
  if old < 15 then
    if self.object_type.id == "cardio" then
      -- Fix THOB value being wrong
      self.world.map.th:setCellFlags(self.tile_x, self.tile_y, {
        thob = self.object_type.thob
      })
    end
  end
  return Entity.afterLoad(self, old, new)
end
