local hook = hook
local player = player
local pairs = pairs
local Msg = Msg
local ErrorNoHalt = ErrorNoHalt
local baseclass = baseclass
local setmetatable = setmetatable
local SERVER = SERVER
local string = string
local util = util

module( "player_manager" )


-- Stores a table of valid player models
local ModelList = {}
local ModelListRev = {}
local HandNames = {}

--[[---------------------------------------------------------
   Utility to add models to the acceptable model list
-----------------------------------------------------------]]
function AddValidModel( name, model )

	ModelList[ name ] = model
	ModelListRev[ string.lower( model ) ] = name

end

--
-- Valid hands
--
function AddValidHands( name, model, skin, body )

	HandNames[ name ] = { model = model, skin = skin, body = body }

end

--[[---------------------------------------------------------
   Return list of all valid player models
-----------------------------------------------------------]]
function AllValidModels( )
	return ModelList
end


--[[---------------------------------------------------------
   Translate the simple name of a model
   into the full model name
-----------------------------------------------------------]]
function TranslatePlayerModel( name )

	if ( ModelList[ name ] != nil ) then
		return ModelList[ name ]
	end

	return "models/player/kleiner.mdl"
end

-- Translate from the full model name to simple model name
function TranslateToPlayerModelName( model )

	model = string.lower( model )

	if ( ModelListRev[ model ] != nil ) then
		return ModelListRev[ model ]
	end

	return "kleiner"
end

--
-- Translate hands based on model
--
function TranslatePlayerHands( name )

	if ( HandNames[ name ] != nil ) then
		return HandNames[ name ]
	end

	return { model = "models/weapons/c_arms_citizen.mdl", skin = 0, body = "100000000" }
end

--[[---------------------------------------------------------
   Compile a list of valid player models
-----------------------------------------------------------]]

AddValidModel( "alyx",			"models/player/alyx.mdl" )
AddValidHands( "alyx",			"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "barney",		"models/player/barney.mdl" )
AddValidHands( "barney",		"models/weapons/c_arms_combine.mdl",		0,		"0000000" )

AddValidModel( "breen",			"models/player/breen.mdl" )
AddValidHands( "breen",			"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "charple",		"models/player/charple.mdl" )
AddValidHands( "charple",		"models/weapons/c_arms_citizen.mdl",		2,		"0000000" )

AddValidModel( "chell",			"models/player/p2_chell.mdl" )
AddValidHands( "chell",			"models/weapons/c_arms_chell.mdl",			0,		"0000000" )

AddValidModel( "corpse",		"models/player/corpse1.mdl" )
AddValidHands( "corpse",		"models/weapons/c_arms_citizen.mdl",		2,		"0000000" )

AddValidModel( "combine",		"models/player/combine_soldier.mdl" )
AddValidHands( "combine",		"models/weapons/c_arms_combine.mdl",		0,		"0000000" )

AddValidModel( "combineprison",	"models/player/combine_soldier_prisonguard.mdl" )
AddValidHands( "combineprison",	"models/weapons/c_arms_combine.mdl",		0,		"0000000" )

AddValidModel( "combineelite",	"models/player/combine_super_soldier.mdl" )
AddValidHands( "combineelite",	"models/weapons/c_arms_combine.mdl",		0,		"0000000" )

AddValidModel( "eli",			"models/player/eli.mdl" )
AddValidHands( "eli",			"models/weapons/c_arms_citizen.mdl",		1,		"0000000" )

AddValidModel( "gman",			"models/player/gman_high.mdl" )
AddValidHands( "gman",			"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "kleiner",		"models/player/kleiner.mdl" )
AddValidHands( "kleiner",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "monk",			"models/player/monk.mdl" )
AddValidHands( "monk",		"models/weapons/c_arms_citizen.mdl",			0,		"0000000" )

AddValidModel( "mossman",		"models/player/mossman.mdl" )
AddValidHands( "mossman",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "mossmanarctic",	"models/player/mossman_arctic.mdl" )
AddValidHands( "mossmanarctic",	"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "odessa",		"models/player/odessa.mdl" )
AddValidHands( "odessa",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "police",		"models/player/police.mdl" )
AddValidHands( "police",		"models/weapons/c_arms_combine.mdl",		0,		"0000000" )

AddValidModel( "policefem",		"models/player/police_fem.mdl" )
AddValidHands( "policefem",		"models/weapons/c_arms_combine.mdl",		0,		"0000000" )

AddValidModel( "magnusson",		"models/player/magnusson.mdl" )
AddValidHands( "magnusson",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "stripped",		"models/player/soldier_stripped.mdl" )
AddValidHands( "stripped",		"models/weapons/c_arms_hev.mdl",			2,		"0000000" )

AddValidModel( "zombie",		"models/player/zombie_classic.mdl" )
AddValidHands( "zombie",		"models/weapons/c_arms_citizen.mdl",		2,		"0000000" )

AddValidModel( "zombiefast",	"models/player/zombie_fast.mdl" )
AddValidHands( "zombiefast",	"models/weapons/c_arms_citizen.mdl",		2,		"0000000" )

AddValidModel( "female01",		"models/player/Group01/female_01.mdl" )
AddValidHands( "female01",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "female02",		"models/player/Group01/female_02.mdl" )
AddValidHands( "female02",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "female03",		"models/player/Group01/female_03.mdl" )
AddValidHands( "female03",		"models/weapons/c_arms_citizen.mdl",		1,		"0000000" )
AddValidModel( "female04",		"models/player/Group01/female_04.mdl" )
AddValidHands( "female04",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "female05",		"models/player/Group01/female_05.mdl" )
AddValidHands( "female05",		"models/weapons/c_arms_citizen.mdl",		1,		"0000000" )
AddValidModel( "female06",		"models/player/Group01/female_06.mdl" )
AddValidHands( "female06",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "female07",		"models/player/Group03/female_01.mdl" )
AddValidHands( "female07",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "female08",		"models/player/Group03/female_02.mdl" )
AddValidHands( "female08",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "female09",		"models/player/Group03/female_03.mdl" )
AddValidHands( "female09",		"models/weapons/c_arms_refugee.mdl",		1,		"0100000" )
AddValidModel( "female10",		"models/player/Group03/female_04.mdl" )
AddValidHands( "female10",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "female11",		"models/player/Group03/female_05.mdl" )
AddValidHands( "female11",		"models/weapons/c_arms_refugee.mdl",		1,		"0100000" )
AddValidModel( "female12",		"models/player/Group03/female_06.mdl" )
AddValidHands( "female12",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )

AddValidModel( "male01",		"models/player/Group01/male_01.mdl" )
AddValidHands( "male01",		"models/weapons/c_arms_citizen.mdl",		1,		"0000000" )
AddValidModel( "male02",		"models/player/Group01/male_02.mdl" )
AddValidHands( "male02",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "male03",		"models/player/Group01/male_03.mdl" )
AddValidHands( "male03",		"models/weapons/c_arms_citizen.mdl",		1,		"0000000" )
AddValidModel( "male04",		"models/player/Group01/male_04.mdl" )
AddValidHands( "male04",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "male05",		"models/player/Group01/male_05.mdl" )
AddValidHands( "male05",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "male06",		"models/player/Group01/male_06.mdl" )
AddValidHands( "male06",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "male07",		"models/player/Group01/male_07.mdl" )
AddValidHands( "male07",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "male08",		"models/player/Group01/male_08.mdl" )
AddValidHands( "male08",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "male09",		"models/player/Group01/male_09.mdl" )
AddValidHands( "male09",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

AddValidModel( "male10",		"models/player/Group03/male_01.mdl" )
AddValidHands( "male10",		"models/weapons/c_arms_refugee.mdl",		1,		"0100000" )
AddValidModel( "male11",		"models/player/Group03/male_02.mdl" )
AddValidHands( "male11",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "male12",		"models/player/Group03/male_03.mdl" )
AddValidHands( "male12",		"models/weapons/c_arms_refugee.mdl",		1,		"0100000" )
AddValidModel( "male13",		"models/player/Group03/male_04.mdl" )
AddValidHands( "male13",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "male14",		"models/player/Group03/male_05.mdl" )
AddValidHands( "male14",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "male15",		"models/player/Group03/male_06.mdl" )
AddValidHands( "male15",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "male16",		"models/player/Group03/male_07.mdl" )
AddValidHands( "male16",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "male17",		"models/player/Group03/male_08.mdl" )
AddValidHands( "male17",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "male18",		"models/player/Group03/male_09.mdl" )
AddValidHands( "male18",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )

AddValidModel( "medic01",		"models/player/Group03m/male_01.mdl" )
AddValidHands( "medic01",		"models/weapons/c_arms_refugee.mdl",		1,		"0100000" )
AddValidModel( "medic02",		"models/player/Group03m/male_02.mdl" )
AddValidHands( "medic02",		"models/weapons/c_arms_refugee.mdl",		0,		"0000000" )
AddValidModel( "medic03",		"models/player/Group03m/male_03.mdl" )
AddValidHands( "medic03",		"models/weapons/c_arms_refugee.mdl",		1,		"0100000" )
AddValidModel( "medic04",		"models/player/Group03m/male_04.mdl" )
AddValidHands( "medic04",		"models/weapons/c_arms_refugee.mdl",		0,		"0000000" )
AddValidModel( "medic05",		"models/player/Group03m/male_05.mdl" )
AddValidHands( "medic05",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "medic06",		"models/player/Group03m/male_06.mdl" )
AddValidHands( "medic06",		"models/weapons/c_arms_refugee.mdl",		0,		"0000000" )
AddValidModel( "medic07",		"models/player/Group03m/male_07.mdl" )
AddValidHands( "medic07",		"models/weapons/c_arms_refugee.mdl",		0,		"0000000" )
AddValidModel( "medic08",		"models/player/Group03m/male_08.mdl" )
AddValidHands( "medic08",		"models/weapons/c_arms_refugee.mdl",		0,		"0000000" )
AddValidModel( "medic09",		"models/player/Group03m/male_09.mdl" )
AddValidHands( "medic09",		"models/weapons/c_arms_refugee.mdl",		0,		"0000000" )
AddValidModel( "medic10",		"models/player/Group03m/female_01.mdl" )
AddValidHands( "medic10",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "medic11",		"models/player/Group03m/female_02.mdl" )
AddValidHands( "medic11",		"models/weapons/c_arms_refugee.mdl",		0,		"0000000" )
AddValidModel( "medic12",		"models/player/Group03m/female_03.mdl" )
AddValidHands( "medic12",		"models/weapons/c_arms_refugee.mdl",		1,		"0000000" )
AddValidModel( "medic13",		"models/player/Group03m/female_04.mdl" )
AddValidHands( "medic13",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "medic14",		"models/player/Group03m/female_05.mdl" )
AddValidHands( "medic14",		"models/weapons/c_arms_refugee.mdl",		0,		"0100000" )
AddValidModel( "medic15",		"models/player/Group03m/female_06.mdl" )
AddValidHands( "medic15",		"models/weapons/c_arms_refugee.mdl",		1,		"0100000" )

AddValidModel( "refugee01",		"models/player/Group02/male_02.mdl" )
AddValidHands( "refugee01",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "refugee02",		"models/player/Group02/male_04.mdl" )
AddValidHands( "refugee02",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "refugee03",		"models/player/Group02/male_06.mdl" )
AddValidHands( "refugee03",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "refugee04",		"models/player/Group02/male_08.mdl" )
AddValidHands( "refugee04",		"models/weapons/c_arms_citizen.mdl",		0,		"0000000" )

--
-- Game specific player models! (EP2, CSS, DOD)
-- Moving them to here since we're now shipping all required files / fallbacks

AddValidModel( "magnusson", "models/player/magnusson.mdl" )
AddValidHands( "magnusson","models/weapons/c_arms_citizen.mdl",		0,		"0000000" )
AddValidModel( "skeleton",	"models/player/skeleton.mdl" )
AddValidHands( "skeleton",	"models/weapons/c_arms_citizen.mdl",			2,		"0000000" )
AddValidModel( "zombine",	"models/player/zombie_soldier.mdl" )
AddValidHands( "zombine",	"models/weapons/c_arms_combine.mdl",			0,		"0000000" )

AddValidModel( "hostage01", "models/player/hostage/hostage_01.mdl" )
AddValidModel( "hostage02", "models/player/hostage/hostage_02.mdl" )
AddValidModel( "hostage03", "models/player/hostage/hostage_03.mdl" )
AddValidModel( "hostage04", "models/player/hostage/hostage_04.mdl" )

AddValidModel( "css_arctic",		"models/player/arctic.mdl" )
AddValidHands( "css_arctic",		"models/weapons/c_arms_cstrike.mdl",		0,		"10000000" )
AddValidModel( "css_gasmask",		"models/player/gasmask.mdl" )
AddValidHands( "css_gasmask",		"models/weapons/c_arms_cstrike.mdl",		0,		"10000000" )
AddValidModel( "css_guerilla",		"models/player/guerilla.mdl" )
AddValidHands( "css_guerilla",		"models/weapons/c_arms_cstrike.mdl",		0,		"10000000" )
AddValidModel( "css_leet",			"models/player/leet.mdl" )
AddValidHands( "css_leet",			"models/weapons/c_arms_cstrike.mdl",		0,		"10000000" )
AddValidModel( "css_phoenix",		"models/player/phoenix.mdl" )
AddValidHands( "css_phoenix",		"models/weapons/c_arms_cstrike.mdl",		0,		"10000000" )
AddValidModel( "css_riot",			"models/player/riot.mdl" )
AddValidHands( "css_riot",			"models/weapons/c_arms_cstrike.mdl",		0,		"10000000" )
AddValidModel( "css_swat",			"models/player/swat.mdl" )
AddValidHands( "css_swat",			"models/weapons/c_arms_cstrike.mdl",		0,		"10000000" )
AddValidModel( "css_urban",		    "models/player/urban.mdl" )
AddValidHands( "css_urban",			"models/weapons/c_arms_cstrike.mdl",		7,		"10000000" )

AddValidModel( "dod_american", "models/player/dod_american.mdl" )
AddValidHands( "dod_american", "models/weapons/c_arms_dod.mdl",		0,		"10000000" )
AddValidModel( "dod_german", "models/player/dod_german.mdl" )
AddValidHands( "dod_german", "models/weapons/c_arms_dod.mdl",		1,		"10000000" )



--
-- Player Class Stuff
--

local Type = {}

function RegisterClass( name, table, base )

	Type[ name ] = table;

	--
	-- If we have a base method then hook
	-- it up in the meta table
	--
	if ( base ) then

		if ( !Type[ name ] ) then ErrorNoHalt( "RegisterClass - deriving "..name.." from unknown class "..base.."!\n" ) end
		setmetatable( Type[ name ], { __index = Type[ base ] } )

	end

	if ( SERVER ) then
		util.AddNetworkString( name )
	end

	--
	-- drive methods cooperate with the baseclass system
	-- /lua/includes/modules/baseclass.lua
	--
	baseclass.Set( name, Type[ name ] )

end

function SetPlayerClass( ply, classname )

	local t = Type[ classname ]
	if ( !Type[ classname ] ) then ErrorNoHalt( "SetPlayerClass - attempt to use unknown player class "..classname .. "!\n" ) end

	local id = util.NetworkStringToID( classname )
	ply:SetClassID( id )

end

function GetPlayerClass( ply )

	local id = ply:GetClassID()
	if ( id == 0 ) then return end

	return util.NetworkIDToString( id )

end

function ClearPlayerClass( ply )

	ply:SetClassID( 0 )

end

local function LookupPlayerClass( ply )

	local id = ply:GetClassID()
	if ( id == 0 ) then return end

	--
	-- Check the cache
	--
	local method = ply.m_CurrentPlayerClass;
	if ( method && method.Player == ply && method.ClassID == id && method.Func ) then return method end

	--
	-- No class, lets create one
	--
	local classname = util.NetworkIDToString( id )
	if ( !classname ) then return end

	--
	-- Get that type. Fail if we don't have the type.
	--
	local t = Type[ classname ]
	if ( !t ) then return end

	local method = {}
		method.Player	= ply
		method.ClassID	= id
		method.Func		= function() end

	setmetatable( method, { __index = t } )

	ply.m_CurrentPlayerClass	= method

	method.Player:InstallDataTable();
	method:SetupDataTables()
	method:Init()
	return method

end

function RunClass( ply, funcname, ... )

	local class = LookupPlayerClass( ply )
	if ( !class ) then return end

	local func = class[funcname]
	if ( !func ) then ErrorNoHalt( "Function "..funcname.." not found on player class!\n" ) return end

	return func( class, ... )

end

--
-- Should be called on spawn automatically to set the variables below
-- This is called in the base gamemode :PlayerSpawn function
--
function OnPlayerSpawn( ply )

	local class = LookupPlayerClass( ply )
	if ( !class ) then return end

	ply:SetWalkSpeed( class.WalkSpeed )
	ply:SetRunSpeed( class.RunSpeed )
	ply:SetCrouchedWalkSpeed( class.CrouchedWalkSpeed )
	ply:SetDuckSpeed( class.DuckSpeed )
	ply:SetUnDuckSpeed( class.UnDuckSpeed )
	ply:SetJumpPower( class.JumpPower )
	ply:AllowFlashlight( class.CanUseFlashlight )
	ply:SetMaxHealth( class.MaxHealth )
	ply:SetHealth( class.StartHealth )
	ply:SetArmor( class.StartArmor )
	ply:ShouldDropWeapon( class.DropWeaponOnDie )
	ply:SetNoCollideWithTeammates( class.TeammateNoCollide )
	ply:SetAvoidPlayers( class.AvoidPlayers )

end
