--NPCManager is required for setting basic NPC properties
local npcManager = require("npcManager")
local effectconfig = require("game/effectconfig")
local npcutils = require("npcs/npcutils")

local onlinePlayNPC = require("scripts/onlinePlay_npc")

--Create the library table
local sampleNPC = {}
--NPC_ID is dynamic based on the name of the library file
local npcID = NPC_ID

--Defines NPC config for our NPC. You can remove superfluous definitions.
local sampleNPCSettings = {
	id = npcID,
	--Sprite size
	gfxheight = 64,
	gfxwidth = 48,
	--Hitbox size. Bottom-center-bound to sprite size.
	width = 40,
	height = 44,
	--Sprite offset from hitbox for adjusting hitbox anchor on sprite.
	gfxoffsetx = 0,
	gfxoffsety = 0,
	--Frameloop-related
	frames = 4,
	framestyle = 1,
	framespeed = 8, --# frames between frame change
	--Movement speed. Only affects speedX by default.
	speed = 1,
	--Collision-related
	npcblock = false,
	npcblocktop = false, --Misnomer, affects whether thrown NPCs bounce off the NPC.
	playerblock = false,
	playerblocktop = false, --Also handles other NPCs walking atop this NPC.

	nohurt=false,
	nogravity = false,
	noblockcollision = false,
	nofireball = false,
	noiceball = true,
	noyoshi= true,
	nowaterphysics = true,
	--Various interactions
	jumphurt = true, --If true, spiny-like
	spinjumpsafe = false, --If true, prevents player hurt when spinjumping
	harmlessgrab = false, --Held NPC hurts other NPCs if false
	harmlessthrown = false, --Thrown NPC hurts other NPCs if false

	grabside=false,
	grabtop=false,
}

--Applies NPC settings
npcManager.setNpcSettings(sampleNPCSettings)

--Register the vulnerable harm types for this NPC. The first table defines the harm types the NPC should be affected by, while the second maps an effect to each, if desired.
npcManager.registerHarmTypes(npcID,
	{
		--HARM_TYPE_JUMP,
		--HARM_TYPE_FROMBELOW,
		HARM_TYPE_NPC,
		HARM_TYPE_PROJECTILE_USED,
		HARM_TYPE_LAVA,
		HARM_TYPE_HELD,
		--HARM_TYPE_TAIL,
		--HARM_TYPE_SPINJUMP,
		HARM_TYPE_OFFSCREEN,
		HARM_TYPE_SWORD
	}, 
	{
		--[HARM_TYPE_JUMP]=10,
		--[HARM_TYPE_FROMBELOW]=10,
		[HARM_TYPE_NPC]=230,
		[HARM_TYPE_PROJECTILE_USED]=230,
		[HARM_TYPE_LAVA]={id=13, xoffset=0.5, xoffsetBack = 0, yoffset=1, yoffsetBack = 1.5},
		[HARM_TYPE_HELD]=230,
		--[HARM_TYPE_TAIL]=10,
		--[HARM_TYPE_SPINJUMP]=10,
		[HARM_TYPE_OFFSCREEN]=10,
		[HARM_TYPE_SWORD]=230,
	}
);

local STATE_HIDE = 0
local STATE_ATTACK = 1
local STATE_WALK = 2
local STATE_DEAD = 3

local effectSpeed = 0
local fallTimer = 0

local spawnOffset = {}
spawnOffset[-1] = -6
spawnOffset[1] = 24

local spawnOffset2 = {}
spawnOffset2[-1] = 22
spawnOffset2[1] = 16

--Death effect code
function effectconfig.onTick.TICK_SNOWMAN(v)
	v.rotation = v.speedX * 2
	fallTimer = fallTimer + 1
	if fallTimer >= 32 and fallTimer <= 42 then
		effectSpeed = effectSpeed + 0.5
		v.speedY = effectSpeed
		v.speedX = 1.5 * -v.direction
	else
		v.speedX = 0
		v.speedY = 0
	end
end

--Register events
function sampleNPC.onInitAPI()
	npcManager.registerEvent(npcID, sampleNPC, "onTickEndNPC")
	npcManager.registerEvent(npcID, sampleNPC, "onDrawNPC")
	npcManager.registerEvent(npcID, sampleNPC, "onStartNPC")
end

--Set its spawn point underneath the ground if settings.Hop is set to false
function sampleNPC.onStartNPC(v)
	local settings = v.data._settings
	if settings.spawnHide then
		v.spawnY = v.spawnY + NPC.config[v.id].height
	end
end

function sampleNPC.onTickEndNPC(v)
	--Don't act during time freeze
	if Defines.levelFreeze then return end
	
	local data = v.data
	local settings = v.data._settings
	local plr = Player.getNearest(v.x + v.width/2, v.y + v.height)
	local trigger = Colliders.Circle(v.x - (v.width * 5), v.y - (v.height * 5), v.width * 7, v.height * 7)
	trigger.x = v.x
	trigger.y = v.y
	
	--If despawned
	if v.despawnTimer <= 0 then
		--Reset our properties, if necessary
		data.initialized = false
		if settings.spawnHide then
			v.noblockcollision = true
			v.speedY = -Defines.npc_grav
			v.y = v.spawnY
			data.timer2 = 0
			data.turnTimer = 0
		end
		data.timer = 0
		data.hopTimer = settings.hopTimer
		return
	end

	--Initialize
	if not data.initialized then
		--Initialize necessary data.
		data.initialized = true
		if not settings.spawnHide then
			if settings.Hop then
				data.state = STATE_WALK
				data.hopTimer = settings.hopTimer
			else
				data.state = STATE_ATTACK
				data.timer2 = data.timer2 or 0
				data.turnTimer = data.turnTimer or 0
			end
		else
			data.state = STATE_HIDE
		end
		data.timer = data.timer or 0
		
		if settings.spawnHide == nil then
			settings.spawnHide = false
			settings.hop = false
			settings.throwTimer = 128
			
		end
	end

	--Depending on the NPC, these checks must be handled differently
	if v:mem(0x12C, FIELD_WORD) > 0    --Grabbed
	or v:mem(0x136, FIELD_BOOL)        --Thrown
	or v:mem(0x138, FIELD_WORD) > 0    --Contained within
	then
		data.state = STATE_ATTACK
		data.timer = 0
	end
	
	--Make it hop around
	if data.state == STATE_WALK then
		v.animationFrame = 3
		v.noblockcollision = false
		if v.collidesBlockBottom then
			data.timer = data.timer + 1
			if data.timer >= settings.waitTimer then
				data.hopTimer = data.hopTimer - 1
				SFX.play(1)
				if data.hopTimer >= 0 then
					v.speedY = -settings.jumpSpeed
					v.speedX = 1.2 * v.direction
				else
					v.direction = -v.direction
					v.speedY = -5.5
					data.hopTimer = settings.hopTimer
				end
				data.timer = 0
			else
				v.speedX = 0
			end
		end
		
	--Sit in place underground
	elseif data.state == STATE_HIDE then
		v.animationFrame = 3
		if v.ai2 == 0 then
			v.speedY = -Defines.npc_grav
			v.friendly = true
		elseif v.ai2 > 0 then
			v.friendly = false
			v.ai2 = v.ai2 + 1
			if v.ai2 > 8 then
				v.noblockcollision = false
				if v.collidesBlockBottom then
					if settings.Hop then
						data.state = STATE_WALK
					else
						data.state = STATE_ATTACK
					end
				end
			end
		end
		if Colliders.collide(plr,trigger) then
			if v.ai2 == 0 then
				v.speedY = -8
				Effect.spawn(230, v.x + 16, v.y, plr.section, true)
				SFX.play(24)
				v.ai2 = v.ai2 + 1
			end
		end
		
	--Attacking code
	elseif data.state == STATE_ATTACK then
		npcutils.faceNearestPlayer(v)
		data.timer = data.timer + 1
		--Code to control its animation to display a throwing sprite
		data.timer2 = data.timer2 + 1
		if data.timer2 <= 0 then
			v.animationFrame = 1
		else
			v.animationFrame = 0
		end
		
		--Spawn the snowball
		if data.timer > settings.throwTimer then
			local n = NPC.spawn(npcID + 1, v.x + spawnOffset[v.direction], v.y - 8)
			n.direction = v.direction
			n.speedX = ((plr.x+(200 * v.direction)) - (v.x))/100
			n.speedY = -6
			data.timer = 0
			data.timer2 = -32
		end
		
		if data.turnTimer > 0 then
			data.turnTimer = data.turnTimer - 1
		end
		if data.turnTimer >= 128 then
			data.state = STATE_DEAD
			data.timer = 0
		end
		
		--Start a timer if the player makes it turn a lot
		if data.lastDirection == -v.direction then
			data.turnTimer = data.turnTimer + 72
		end
		data.lastDirection = v.direction
	else
		v.friendly = true
		v.animationFrame = 2
		data.timer = data.timer + 1
		if data.timer == 1 then
			local a = Animation.spawn(npcID, v.x + spawnOffset2[v.direction], v.y + 8)
			a.direction = v.direction
		end
		if data.timer >= 160 then
			v:kill(HARM_TYPE_NPC)
			SFX.play(64)
		end
	end
	
	-- animation controlling
	v.animationFrame = npcutils.getFrameByFramestyle(v, {
		frame = data.frame,
		frames = sampleNPCSettings.frames
	});
	
end

function sampleNPC.onDrawNPC(v)
	local data = v.data
	local settings = v.data._settings
	--Set it behind stuff
	if data.state == STATE_HIDE then
		npcutils.drawNPC(v,{priority = -80})
		npcutils.hideNPC(v)
	end
end


onlinePlayNPC.onlineHandlingConfig[npcID] = {
	getExtraData = function(v)
		local data = v.data
		if not data.initialized then
			return nil
		end

		return {
			state = data.state,
			timer = data.timer,
			timer2 = data.timer2,
			turnTimer = data.turnTimer,
			hopTimer = data.hopTimer,
			lastDirection = data.lastDirection,
		}
	end,
	setExtraData = function(v, receivedData)
		local data = v.data
		if not data.initialized then
			return nil
		end

		data.state = receivedData.state
		data.timer = receivedData.timer
		data.timer2 = receivedData.timer2
		data.turnTimer = receivedData.turnTimer
		data.hopTimer = receivedData.hopTimer
		data.lastDirection = receivedData.lastDirection
	end,
}


--Gotta return the library table!
return sampleNPC