-- v1.3.0

local spyblock = {}

local redstone = require("redstone")
local npcManager = require("npcManager")

spyblock.name = "spyblock"
spyblock.id = NPC_ID
spyblock.order = 0.48

spyblock.onRedPower = function(n, c, power, dir, hitbox)
  if redstone.is.operator(c.id) and power > 0 then
    n.data.isOn = true
    redstone.setEnergy(n, power)
  end
  return true
end

spyblock.config = npcManager.setNpcSettings({
	id = spyblock.id,

  width = 32,
  height = 32,

	gfxwidth = 32,
	gfxheight = 32,
	gfxoffsetx = 0,
	gfxoffsety = 0,

	frames = 1,
	framespeed = 8,
	framestyle = 0,
  invisible = false,

  nogravity = true,
  notcointransformable = true,
	jumphurt = true,
  noblockcollision = true,
	nohurt = true,
	noyoshi = true,
  blocknpc = true,
  playerblock = true,
  playerblocktop = true,
  npcblock = true,
  disabledespawn = false,
})

local function luafy(msg)
  return "return function(object) local player, npc, block, bgo = object, object, object, object "..msg.." end"
end

function spyblock.prime(n)
  local data = n.data

  data.animFrame = data.animFrame or 0
  data.animTimer = data.animTimer or 0

  data.frameX = data._settings.dir or 0
  data.frameY = data._settings.type or 0

  data.isOn = data.isOn or false
  data.prevState = data.prevState or false
  data.type = data._settings.type or 0

  data.whitelist = data.whitelist or redstone.parseListMAP(data._settings.whitelist)
  data.filter = redstone.luaParse("SPYBLOCK", n, luafy(data._settings.filter or "return true"))

  data.redhitbox = redstone.basicDirectionalRedHitBox(n, (data.frameX + 2)%4)
end

local TYPE_PLAYER  = 0
local TYPE_NPC     = 1
local TYPE_BLOCK   = 2
local TYPE_BGO     = 3
local TYPE_POWERED = 4

local function passPower(n, power)
  local data = n.data

  redstone.updateDirectionalRedHitBox(n, (data.frameX + 2)%4)
  redstone.passDirectionEnergy{source = n, power = power, hitbox = data.redhitbox}
  if not data.prevState then
    data.observ = true
  end
end

local getFuncType = {}
local getCheckType = {}
getFuncType[TYPE_PLAYER] = Player.getIntersecting
getCheckType[TYPE_PLAYER] = "character"

getFuncType[TYPE_NPC] = NPC.iterateIntersecting
getCheckType[TYPE_NPC] = "id"

getFuncType[TYPE_BLOCK] = Block.iterateIntersecting
getCheckType[TYPE_BLOCK] = "id"

getFuncType[TYPE_BGO] = BGO.iterateIntersecting
getCheckType[TYPE_BGO] = "id"

local function scanBox(n, dir)
  if dir == 0 then
    return n.x - 2, n.y, 2, n.height
  elseif dir == 1 then
    return n.x, n.y - 2, n.width, 2
  elseif dir == 2 then
    return n.x + n.width, n.y, 2, n.height
  else
    return n.x, n.y + n.height, n.width, 2
  end
end

local function scan(n, v, id)
  local data = n.data

  local whitelistpass = true
  local filterpass = redstone.luaCall(data.filter, v)
  local hiddenpass = not v.isHidden
  local datapass = not (v.data and v.data.hasNoScan)

  if #table.unmap(data.whitelist) ~= 0 then
    whitelistpass = data.whitelist[id]
  end

  if datapass then
    if type(v) == 'npc' then
      datapass = NPC.config[id].hasnoscan
    elseif type(v) == 'block' then
      datapass = Block.config[id].hasnoscan
    elseif type(v) == 'bgo' then
      datapass = BGP.config[id].hasnoscan
    end
  end
  
  if whitelistpass and filterpass and hiddenpass and datapass then
    data.isOn = true
    passPower(n, 15)
    return true
  end
end

function spyblock.onRedTick(n)
  local data = n.data
  data.observ = false

  if data.power > 0 then
    passPower(n, data.power)
  else
    local func, check = getFuncType[data.type], getCheckType[data.type]
    local x, y, w, h = scanBox(n, data.frameX)
    if data.type == TYPE_PLAYER then
      for _, v in ipairs(func(x, y, x + w, y + h)) do
        if scan(n, v, v[check]) then break end
      end
    else
      for _, v in func(x, y, x + w, y + h) do
        if scan(n, v, v[check]) then break end
      end
    end
  end

  if not data.isOn and data.prevState then
    data.observ = true
  end

  if data.isOn  then
    data.frameY = TYPE_POWERED
  else
    data.frameY = data.type
  end

  data.prevState = data.isOn
  data.isOn = false

  redstone.resetPower(n)
end

spyblock.onRedDraw = redstone.drawNPC

redstone.register(spyblock)

return spyblock
