PlayablePets = {}
PlayablePets.ConfigData = PlayablePets.ConfigData or {}

local SETTING = require("pp_settings")
local MOBTYPE = require("pp_mobtype")

-- Used to call the function listed as a key in this table when the corresponding keyboard code is detected (key pressed)
local RPCKEYS =
{
	SetSleep = 122, -- Z
	SpecialAbility1 = 120, -- X
	SpecialAbility2 = 99, -- C
	--SetRage = 111, -- O - Unused because of crashes
	ChangeCharacter = 112, -- P
}

------------------------------------------------------------------
-- Table utility functions
------------------------------------------------------------------

--[[
local next = next
function table.isEmpty(t)
	if next(t) == nil then
		return true
	else
		return false
	end
end

function table.clear(t)
	for k in pairs (table) do
			t[k] = nil
	end
end
--]]

function table.contains(t, element)
   --contact Phaze, t is nil when loaded via caves. 
  if t ~= nil then	
  for _, value in pairs(t) do
		if value == element then
			return true
		end
  end
  else
	
  return false
  end
end

-- Append the contents of t2 to t1
--function table.concat(t1, t2)
function TableConcat(t1, t2)
	for i=1,#t2 do
		t1[#t1+1] = t2[i]
	end
	
	return t1
end

------------------------------------------------------------------
-- Settings code
------------------------------------------------------------------

local configuratorModName = "Pet Configuration Manager"
local configuratorModEnabled = KnownModIndex:IsModEnabled(configuratorModName)

for _, mod in ipairs(KnownModIndex:GetModsToLoad()) do
	if mod == configuratorModName then
		configuratorModEnabled = true
	end
end

-- Retrieve the global Playable Pets configuration settings, if applicable
if configuratorModEnabled then
	local modConfig = KnownModIndex:LoadModConfigurationOptions(configuratorModName, false)
	
	-- Build a more convenient global configuration table to access, indexed by option name
	if modConfig and type(modConfig) == "table" then
		for k, v in ipairs(modConfig) do
			PlayablePets.ConfigData[v.name] = {}
			PlayablePets.ConfigData[v.name].default = v.default
			PlayablePets.ConfigData[v.name].saved = v.saved
		end
	end
end

------------------------------------------------------------------
-- Configuration functions
------------------------------------------------------------------

-- The configurator mod is intended to override all individual Playable Pets mods, if an option is set to
-- This function will first consult the configurator settings for an option, only using the local
--  setting if both the saved and default options are set to not override.
PlayablePets.GetModConfigData = function(optionName)
	local configData = PlayablePets.ConfigData[optionName]
	local optionValue = nil
	
	--local dataDump = DataDumper(PlayablePets.ConfigData, nil, false)
	
	-- If the configurator option isn't set to off, use it
	-- Otherwise, use the default if it isn't off
	if configData ~= nil then
		if configData.saved ~= nil and configData.saved ~= SETTING.OFF then
			optionValue = configData.saved
		elseif configData.default  ~= SETTING.OFF then
			optionValue = configData.default
		end
	end
	
	-- If the configurator option was set to override the local mod, use it
	-- Otherwise, get the local mod's option for the setting
	-- Forced to use function GetModConfigData(optionname, SETTING.MODNAME, get_local_config) due to this not being in modmain.lua
	--return optionValue ~= nil and optionValue or GetModConfigData(optionName)
	return optionValue ~= nil and optionValue or GetModConfigData(optionName, SETTING.MODNAME, false)
end

-- Consults various Configurator preset settings in addition to the default mod's setting for an individual mob to determine
--  whether or not the mob is enabled, and if various actions (like adding to the character select or skins) should be undertaken
function PlayablePets.MobEnabled(prefab)
	local mobs = PP_MobCharacters
	local MOBPRESET = PlayablePets.GetModConfigData("MobPreset")
	
	-- Spare performing all of the table and setting checks if the mob table already has the setting defined
	if mobs[prefab].enabled ~= nil then
		return mobs[prefab].enabled
	end
	
	local mobEnabled = PlayablePets.GetModConfigData(mobs[prefab].fancyname) == SETTING.ENABLE
	local isGiant = table.contains(mobs[prefab].mobtype, MOBTYPE.GIANT)
	local isBoss = table.contains(mobs[prefab].mobtype, MOBTYPE.BOSS)
	local isCrafty = table.contains(mobs[prefab].mobtype, MOBTYPE.CRAFTY)
	
	if ( (MOBPRESET == nil or MOBPRESET == SETTING.OFF) and mobEnabled )
	or ( MOBPRESET == SETTING.ALL_MOBS )
	or ( MOBPRESET == SETTING.GIANTS_ONLY and isGiant)
	or ( MOBPRESET == SETTING.BOSSES_ONLY and isBoss )
	or ( MOBPRESET == SETTING.CRAFTY_MOBS and isCrafty ) then
		mobs[prefab].enabled = true
	end
	
	if ( MOBPRESET == SETTING.NO_GIANTS and isGiant )
	or ( MOBPRESET == SETTING.NO_BOSSES and isBoss )
	or ( MOBPRESET == SETTING.CRAFTY_MOBS and not isCrafty ) then
		mobs[prefab].enabled = false
	end
	
	return mobs[prefab].enabled
end

------------------------------------------------------------------
-- Skin function
------------------------------------------------------------------

PlayablePets.SetSkin = function(inst, name)
	if not inst:HasTag("playerghost") then
		local mobskin = inst.components.skinner.skin_name
		local getshiny = (mobskin ~= inst.prefab and mobskin ~= "") and string.gsub(mobskin, inst.prefab.."_", "" ) --will return false if no skin is equipped.
		if SKIN_RARITY_COLORS.ModMade ~= nil then
			if getshiny ~= false then
				inst.isshiny = getshiny --used for other purposes
				inst.AnimState:SetBuild(name.."_shiny_build_0"..inst.isshiny)
			end
		end
	end
end

------------------------------------------------------------------
-- RPC Keyhandler functions
------------------------------------------------------------------

--Checks to see if there is any type-able pop ups. Prevents RPCs from being activated.
local function IsDefaultScreen()
	if TheFrontEnd:GetActiveScreen() and TheFrontEnd:GetActiveScreen().name and type(TheFrontEnd:GetActiveScreen().name) == "string" and TheFrontEnd:GetActiveScreen().name == "HUD" then
		return true
	else
		return false
	end
end

-- Allows a player mob to sleep. Does what it says on the tin
PlayablePets.SetSleep = function(player)
	if player.specialsleep ~= nil and player.specialsleep == true and not player.sg:HasStateTag("busy") then
		player.sg:GoToState("special_sleep")
	elseif player.specialsleep ~= nil and player.specialsleep == false and not player.sg:HasStateTag("busy") then
		player.sg:GoToState("special_wake")
	end	
	
--ToDo: Make a variable use for this. Don't want to use up Tag space.
if player:HasTag("mobsleep") and not player.sg:HasStateTag("busy") and player.sg:HasStateTag("sleeping") then
		player.sg:GoToState("wake")	
	elseif player:HasTag("mobsleep") and not player.sg:HasStateTag("busy") and not player.sg:HasStateTag("sleeping") and not player.sg:HasStateTag("specialsleep") then
		player.sg:GoToState("sleep")	
		
	--elseif player:HasTag("mobsleep") and not player.sg:HasStateTag("busy") and not player.sg:HasStateTag("sleep") and player.sg:HasStateTag("sleeping") then-- and not player.HUD:IsChatInputScreenOpen() and not player.HUD:IsConsoleScreenOpen() then
		--player.sg:GoToState("wake")	
	end
	
	if player.wants_to_sleep ~= nil and player.wants_to_sleep == false and player.sg:HasStateTag("attacking") and not player.sg:HasStateTag("nointerrupt") then
		player.wants_to_sleep = true --This allows tentacle players to stop attacking when they want to.
	end
	
	if player.mobsleep ~= nil and player.mobsleep == true and player.sg:HasStateTag("sleeping") then
		player.sg:GoToState("wake")	
	elseif player.mobsleep ~= nil and player.mobsleep == true and not player.sg:HasStateTag("busy") and not player.sg:HasStateTag("sleeping") and not player.sg:HasStateTag("specialsleep") then
		player.sg:GoToState("sleep")	
	end
	
	if player.sg:HasStateTag("specialsleep") and not player.sg:HasStateTag("busy") then
		player.sg:GoToState("wake")
	end
end

-- Performs a special abillity, usually a taunt, when the "x" key is pressed.
PlayablePets.SpecialAbility1 = function(player)
	if not player.sg:HasStateTag("busy") then
		if player.taunt or player.israged_special or ( player:HasTag("special_atk1") and not player.sg:HasStateTag("sleeping") ) then
			player.sg:GoToState("special_atk1")
		end
		
		if player.dragonrage ~= nil then
			if player.dragonrage then
				player.sg:GoToState("transform_normal")
			else
				player.sg:GoToState("transform_fire")
			end
		end
	end
end

-- Performs a secondary special abillity when "c" is pressed.
PlayablePets.SpecialAbility2 = function(player)
	if not player.sg:HasStateTag("busy") then
		--if not player:HasTag("nospecial") then
			if player:HasTag("special_atk2") and not player.sg:HasStateTag("sleeping") then
				player.sg:GoToState("special_atk2")
			end
			
			if not player.specialatk2_nr then
				if player.specialatk2 then
					player.sg:GoToState("special_atk2")
				end
				
				if player.taunt2 then
					player.sg:GoToState("special_atk2")
				end
			end
			
			if player.israged ~= nil then
				player.sg:GoToState("rage")
			end
	end
end

--Supposed to send the player into "Rage" mode when "o" is pressed. Currently unused.
--[[
PlayablePets.SetRage = function(player) --Causes crash when it runs states with the "busy" Tag. Maybe try to add tags with ThePlayer instead.
	if inst.raged ~= nil and not player.sg:HasStateTag("busy") then
			--if inst.israged ~= nil then
				if inst.israged == true then
					inst.israged == false
				else
					inst.israged = true
				end
			--end
		end
	end
]]

-- Sends the player to the character select screen when "P" is pressed
PlayablePets.ChangeCharacter = function(player)
	if PlayablePets.GetModConfigData("Mobchange") == SETTING.ENABLE then
		if not player.sg:HasStateTag("busy") and not player.isdespawning then
			player.isdespawning = true --prevent spamming.
			player.components.inventory:DropEverything(true)
			TheWorld:PushEvent("ms_playerdespawnanddelete", player)
		end
	end
end

------------------------------------------------------------------
-- PLAYABLE PETS INIT FUNCTION (MAKES STUFF GO) (WIP)
------------------------------------------------------------------

PlayablePets.Init = function(modName)
	SETTING.MODNAME = modName

	------------------------------------------------------------------
	-- Keyhandlers
	------------------------------------------------------------------
	for fn, keycode in pairs(RPCKEYS) do
		AddModRPCHandler(SETTING.MODNAME, fn, PlayablePets[fn])
		
		TheInput:AddKeyDownHandler(keycode, function()
			if ThePlayer and not ThePlayer.HUD:IsChatInputScreenOpen() and not ThePlayer.HUD:IsConsoleScreenOpen() and IsDefaultScreen() then
				SendModRPCToServer(MOD_RPC[SETTING.MODNAME][fn])
			end
		end)
	end
end

return PlayablePets