-- enables outside only and inside only buildings
--author expwnent
local usage = [====[

modtools/outside-only
=====================
This allows you to specify certain custom buildings as outside only, or inside
only. If the player attempts to build a building in an inappropriate location,
the building will be destroyed.

Arguments::

    -clear
        clears the list of registered buildings
    -checkEvery n
        set how often existing buildings are checked for whether they
        are in the appropriate location to n ticks
    -type [EITHER, OUTSIDE_ONLY, INSIDE_ONLY]
        specify what sort of restriction to put on the building
    -building name
        specify the id of the building

]====]
local eventful = require 'plugins.eventful'
local utils = require 'utils'

--luacheck: global
buildingType = utils.invert({'EITHER','OUTSIDE_ONLY','INSIDE_ONLY'})
registeredBuildings = registeredBuildings or {} --as:number[]
checkEvery = checkEvery or 100
timeoutId = timeoutId or nil --as:number

eventful.enableEvent(eventful.eventType.UNLOAD,1)
eventful.onUnload.outsideOnly = function()
 registeredBuildings = {}
 checkEvery = 100
 timeoutId = nil
end

local function destroy(building)
 if #building.jobs > 0 and building.jobs[0] and building.jobs[0].job_type == df.job_type.DestroyBuilding then
  return
 end
 local b = dfhack.buildings.deconstruct(building)
 if b then
  --TODO: print an error message to the user so they know
  return
 end
-- building.flags.almost_deleted = 1
end

local function checkBuildings()
 local toDestroy = {} --as:df.building[]
 local function forEach(building)
  if building:getCustomType() < 0 then
   --TODO: support builtin building types if someone wants
   return
  end
  local pos = df.coord:new()
  pos.x = building.centerx
  pos.y = building.centery
  pos.z = building.z
  local outside = dfhack.maps.getTileBlock(pos).designation[pos.x%16][pos.y%16].outside
  local def = df.global.world.raws.buildings.all[building:getCustomType()]
  local btype = registeredBuildings[def.code]
  if btype then
--   print('outside: ' .. outside==true .. ', type: ' .. btype)
  end

  if not btype or btype == buildingType.EITHER then
   registeredBuildings[def.code] = nil
   return
  elseif btype == buildingType.OUTSIDE_ONLY then
   if outside then
    return
   end
  else
   if not outside then
    return
   end
  end
  table.insert(toDestroy,building)
 end
 for _,building in ipairs(df.global.world.buildings.all) do
  forEach(building)
 end
 for _,building in ipairs(toDestroy) do
  destroy(building)
 end
 if timeoutId then
  dfhack.timeout_active(timeoutId,nil)
  timeoutId = nil
 end
 timeoutId = dfhack.timeout(checkEvery, 'ticks', checkBuildings)
end

eventful.enableEvent(eventful.eventType.BUILDING, 100)
eventful.onBuildingCreatedDestroyed.outsideOnly = function(buildingId)
 checkBuildings()
end

local validArgs = utils.invert({
 'help',
 'clear',
 'checkEvery',
 'building',
 'type'
})
local args = utils.processArgs({...}, validArgs)
if args.help then
 print(usage)
 return
end

if args.clear then
 registeredBuildings = {}
end

if args.checkEvery then
 if not tonumber(args.checkEvery) then
  error('Invalid checkEvery.')
 end
 checkEvery = tonumber(args.checkEvery)
end

if not args.building then
 return
end

if not args['type'] then
 print 'outside-only: please specify type'
 return
end

if not buildingType[args['type']] then
 error('Invalid building type: ' .. args['type'])
end

registeredBuildings[args.building] = buildingType[args['type']]

checkBuildings()
