
local file = file
local GAMEMODE = GAMEMODE
local type = type
local utilx = utilx
local tablex = tablex
local pairs = pairs
local string = string
local setmetatable = setmetatable
local tostring = tostring

local Msg = Msg
local PrintTable = PrintTable

/*------------------------------------------------------
  
  // This is the user authorization module. It has
  // been designed to be as flexible as possible,
  // allowing for Lua coders to use it as a base and
  // keeping admin lists simple for the end user.
  
  // There is a naming convention for the config and
  // access options.
  
  - GLOBAL settings should be prefixed with a colon
  	EX: ":CanKick"
  	
  - GAMEMODE settings should be prefixed with
    the gamemode folder followed by a colon
  	EX: "sandbox:CanSpawnProps"
  	
  - If you leave off the gamemode folder and colon
    it defaults to the current gamemode.
  	EX: "MaxHealth"
  		is the same thing as
  	EX: "mygamemode:MaxHealth"
  	
  // The access settings are just true or false,
  // like CanKick or CanBan, or CanSpawnProps
  
  player:AuthAccess( flag )
  player:SetAuthAccess( flag, bool )
  
  // Use the config functions for non-boolean
  // settings, like prop limits or money.
  
  player:AuthConfig( setting )
  player:SetAuthConfig( setting, value )
  
  // The info settings should be used for things
  // like full name, e-mail address, etc. These do not
  // follow the naming conventions outlined earlier.
  
  player:AuthInfo( setting )
  player:SetAuthInfo( setting, value )
  
  // You can also set up groups in the groups file.
  // Groups work the same as a user when it comes to
  // settings, it just provides an easier way to manage
  // admins and the like.
  
  player:AuthGroup()
  player:SetAuthGroup( groupname )
  
------------------------------------------------------*/

module("user")

UserAuthFile = "settings/users/users.txt"
GroupAuthFile = "settings/users/groups.txt"
DefaultGroupDir = "settings/users/default/"

UserObj = {}
UserObj.Ident = nil
GroupObj = {}
GroupObj.Ident = nil

local function BoolToNumber( _bool )
	if ( _bool == true || _bool == 1 ) then return 1 end
	return 0
end

local function NumberToBool( _bool )
	if ( _bool == 0 || _bool == "0" || _bool == "false" || _bool == nil ) then return false end
	return true
end

local function GetKeyValues( kvfile )
	local _file = kvfile or UserAuthFile
	
	if !file.Exists( _file ) then return end
	
	local fp = file.Read( _file )
	local kv = utilx.KeyValuesToTable( fp )
	//local kv = {}
	
	return kv
end

local function SetKeyValues( kv, kvfile )
	local _file = kvfile or UserAuthFile
	
	if !file.Exists( _file ) then return false end
	
	local str = utilx.TableToKeyValues( kv )
	file.Write( _file, str )
	
	return true
	
end

function GetUserFromIdent( ident )
	local NewUser = UserObj:Create()
	NewUser.Ident = ident
	return NewUser
end

function GetGroupFromIdent( ident )
	local NewGroup = GroupObj:Create()
	NewGroup.Ident = ident
	return NewGroup
end

function GetUserFromPlayer( pl )
	local kv = GetKeyValues()
	if ( !kv ) then return end
		
	for k,v in pairs( kv ) do
		local _break = false
		if ( type(v) != "table" ) then _break = true end
		if ( v.SteamID != nil && v.SteamID != pl:SteamID() ) then _break = true end
		if ( v.Name != nil && v.Name != pl:Nick() ) then _break = true end
		if ( v.Password != nil && v.Password != pl:GetInfo( "authpass" ) ) then _break = true end
//		if ( v.Address != nil && v.Address != pl:Address() ) then _break = true end
		if ( !_break ) then
			return GetUserFromIdent( k )
		end
	end
	
	local NewUser = UserObj:Create()
	NewUser.Ident = nil
	return NewUser
	
end

function UserObj:Create()
	local o = {}
	
	setmetatable(o, self)
	
	self.__index = self

	return o
end

function UserObj:GetGroup()
	local kv = GetKeyValues()
	local ident = self.Ident
	if ( !kv[ident] ) then return end
	if ( !kv[ident]["Group"] ) then return end
	return kv[ident]["Group"]
end

function UserObj:GetGroupObject()
	local kv = GetKeyValues()
	local ident = self.Ident
	if ( !kv[ident] ) then return end
	if ( !kv[ident]["Group"] ) then return end
	return GetGroupFromIdent( kv[ident]["Group"] ) or nil
end

function UserObj:SetGroup( group )
	local kv = GetKeyValues()
	local ident = self.Ident
	kv[ident]["Group"] = group or nil
	return SetKeyValues( kv )
end

function UserObj:GetName()
	local kv = GetKeyValues()
	local ident = self.Ident
	return kv[ident]["Name"] or nil
end

function UserObj:SetName( name )
	local kv = GetKeyValues()
	local ident = self.Ident
	kv[ident]["Name"] = name or nil
	return SetKeyValues( kv )
end

function UserObj:GetSteamID()
	local kv = GetKeyValues()
	local ident = self.Ident
	return kv[ident]["SteamID"] or nil
end

function UserObj:SetSteamID( steamid )
	local kv = GetKeyValues()
	local ident = self.Ident
	kv[ident]["SteamID"] = steamid or nil
	return SetKeyValues( kv )
end

function UserObj:GetAddress()
	local kv = GetKeyValues()
	local ident = self.Ident
	return kv[ident]["Address"] or nil
end

function UserObj:SetAddress( address )
	local kv = GetKeyValues()
	local ident = self.Ident
	kv[ident]["Address"] = address or nil
	return SetKeyValues( kv )
end

function UserObj:GetPassword()
	local kv = GetKeyValues()
	local ident = self.Ident
	return kv[ident]["Password"] or nil
end

function UserObj:SetPassword( password )
	local kv = GetKeyValues()
	local ident = self.Ident
	kv[ident]["Password"] = password or nil
	return SetKeyValues( kv )
end

function UserObj:GetInfo( key, value )
	local kv = GetKeyValues()
	local ident = self.Ident
	return kv[ident]["Info"][key] or nil
end

function UserObj:SetInfo( key, value )
	local kv = GetKeyValues()
	local ident = self.Ident
	
	if ( kv[ident] == nil ) then return end	
	if ( kv[ident]["Info"] == nil ) then kv[ident]["Info"] = {} end
	kv[ident]["Info"][key] = value
	
	return SetKeyValues(kv)
end

function UserObj:GetFlag( key, value )
	local kv = GetKeyValues()
	local ident = self.Ident
	local strstart,strend,gamemode,flag = string.find(key, "(%w*):(%w*)")
	
	if ( gamemode == "" ) then gamemode = ":GLOBAL" end
	if ( gamemode == nil ) then
		flag = key
		strstart,strend,gamemode = string.find( GAMEMODE.Folder, ".*/(%a*)" )
	end
	
	local _retval = nil

	// Get the default gamemode setting
	local _file = DefaultGroupDir .. gamemode .. ".txt"
	if ( gamemode != ":GLOBAL" && file.Exists(_file) ) then
		local _kv = GetKeyValues(_file)
		if ( _kv.Flags != nil && _kv.Flags[flag] != nil ) then
			_retval = NumberToBool( _kv.Flags[flag] )
		end
	end
	
	local groupval
	
	// Get the user's group's setting
	if ( self:GetGroupObject() != nil ) then groupval = self:GetGroupObject():GetFlag( key, value ) end
	
	Msg( groupval, "\n" )
	
	// Don't overwrite the default gamemode setting with nil!
	if ( groupval != nil ) then _retval = groupval end

	if ( kv[ident] == nil ) then return _retval end	
	if ( kv[ident].Settings == nil ) then return _retval end
	if ( kv[ident].Settings[gamemode] == nil ) then return _retval end
	if ( kv[ident].Settings[gamemode].Flags == nil ) then return _retval end
	if ( kv[ident].Settings[gamemode].Flags[flag] == nil ) then return _retval end
	
	return NumberToBool( kv[ident].Settings[gamemode].Flags[flag] )
end

UserObj.Authorize = UserObj.GetFlag

function UserObj:SetFlag( key, value )
	local kv = GetKeyValues()
	local ident = self.Ident
	local strstart,strend,gamemode,flag = string.find(key, "(%w*):(%w*)")
	
	if ( gamemode == "" ) then gamemode = ":GLOBAL" end
	if ( gamemode == nil ) then
		flag = key
		strstart,strend,gamemode = string.find( GAMEMODE.Folder, ".*/(%a*)" )
	end
	
	local _setval = nil
	
	if ( value != nil ) then
		_setval = value
	end

	// Create necessary tables	
	if ( kv[ident] == nil ) then return end
	if ( kv[ident].Settings == nil ) then kv[ident].Settings = {} end
	if ( kv[ident].Settings[gamemode] == nil ) then kv[ident].Settings[gamemode] = {} end
	if ( kv[ident].Settings[gamemode].Flags == nil ) then kv[ident].Settings[gamemode].Flags = {} end
	
	if ( _setval == nil ) then
		kv[ident].Settings[gamemode].Flags[flag] = nil
	else
		kv[ident].Settings[gamemode].Flags[flag] = BoolToNumber( _setval )
	end
	
	// Remove empty tables
	if ( tablex.Count( kv[ident].Settings[gamemode].Flags ) < 1 ) then kv[ident].Settings[gamemode].Flags = nil end
	if ( tablex.Count( kv[ident].Settings[gamemode] ) < 1 ) then kv[ident].Settings[gamemode] = nil end
	if ( tablex.Count( kv[ident].Settings ) < 1 ) then kv[ident].Settings = nil end
	
	SetKeyValues(kv)
end

function UserObj:GetConfig( key, value )
	local kv = GetKeyValues()
	local ident = self.Ident
	local strstart,strend,gamemode,config = string.find(key, "(%w*):(%w*)")
	
	if ( gamemode == "" ) then gamemode = ":GLOBAL" end
	if ( gamemode == nil ) then
		config = key
		strstart,strend,gamemode = string.find( GAMEMODE.Folder, ".*/(%a*)" )
	end
	
	local _retval

	// Get the default gamemode setting
	local _file = DefaultGroupDir .. gamemode .. ".txt"
	if ( gamemode != ":GLOBAL" && file.Exists(_file) ) then
		local _kv = GetKeyValues(_file)
		
		if ( _kv.Config != nil && _kv.Config[config] != nil ) then
			_retval = tostring( _kv.Config[config] )
		end
	end
	
	local groupval
	
	// Get the user's group's setting
	if ( self:GetGroupObject() != nil ) then groupval = self:GetGroupObject():GetConfig( key, value ) end
	
	// Don't overwrite the default gamemode setting with nil!
	if ( groupval != nil ) then _retval = groupval end
	
	if ( kv[ident] == nil ) then return _retval end	
	if ( kv[ident].Settings == nil ) then return _retval end
	if ( kv[ident].Settings[gamemode] == nil ) then return _retval end
	if ( kv[ident].Settings[gamemode].Config == nil ) then return _retval end
	if ( kv[ident].Settings[gamemode].Config[config] == nil ) then return _retval end
	
	return tostring( kv[ident].Settings[gamemode].Config[config] )
end

function UserObj:SetConfig( key, value )
	local kv = GetKeyValues()
	local ident = self.Ident
	local strstart,strend,gamemode,config = string.find(key, "(%w*):(%w*)")
	
	if ( gamemode == "" ) then gamemode = ":GLOBAL" end
	if ( gamemode == nil ) then
		config = key
		strstart,strend,gamemode = string.find( GAMEMODE.Folder, ".*/(%a*)" )
	end
	
	local _setval
	
	if ( value != nil ) then
		_setval = value
	end
	
	// Create necessary tables
	if ( kv[ident] == nil ) then return end
	if ( kv[ident].Settings == nil ) then kv[ident].Settings = {} end
	if ( kv[ident].Settings[gamemode] == nil ) then kv[ident].Settings[gamemode] = {} end
	if ( kv[ident].Settings[gamemode].Config == nil ) then kv[ident].Settings[gamemode].Config = {} end
	
	if ( _setval == nil ) then
		kv[ident].Settings[gamemode].Config[config] = nil
	else
		kv[ident].Settings[gamemode].Config[config] = _setval
	end
	
	// Remove empty tables
	if ( tablex.Count( kv[ident].Settings[gamemode].Config ) < 1 ) then kv[ident].Settings[gamemode].Config = nil end
	if ( tablex.Count( kv[ident].Settings[gamemode] ) < 1 ) then kv[ident].Settings[gamemode] = nil end
	if ( tablex.Count( kv[ident].Settings ) < 1 ) then kv[ident].Settings = nil end
	
	SetKeyValues(kv)
end

function GroupObj:Create()
	local o = {}
	
	setmetatable(o, self)
	
	self.__index = self

	return o
end

function GroupObj:GetFlag( key, value )
	local kv = GetKeyValues(GroupAuthFile)
	
	local ident = self.Ident
	Msg( ident, "\n" )
	local strstart,strend,gamemode,flag = string.find(key, "(%w*):(%w*)")
	
	if ( gamemode == "" ) then gamemode = ":GLOBAL" end
	if ( gamemode == nil ) then
		flag = key
		strstart,strend,gamemode = string.find( GAMEMODE.Folder, ".*/(%a*)" )
	end
	
	if ( kv[ident][gamemode] == nil ) then return end
	if ( kv[ident][gamemode].Flags == nil ) then return end
	if ( kv[ident][gamemode].Flags[flag] == nil ) then return end
	
	return NumberToBool( kv[ident][gamemode].Flags[flag] )
end

function GroupObj:SetFlag( key, value )
	local kv = GetKeyValues(GroupAuthFile)
	local ident = self.Ident
	local strstart,strend,gamemode,flag = string.find(key, "(%w*):(%w*)")
	
	if ( gamemode == "" ) then gamemode = ":GLOBAL" end
	if ( gamemode == nil ) then
		flag = key
		strstart,strend,gamemode = string.find( GAMEMODE.Folder, ".*/(%a*)" )
	end
	
	local _setval
	
	if ( value != nil ) then
		_setval = value
	end
	
	if ( kv[ident][gamemode] == nil ) then kv[ident][gamemode] = {} end
	if ( kv[ident][gamemode].Flags == nil ) then kv[ident][gamemode].Flags = {} end
	
	if ( _setval == nil ) then
		kv[ident][gamemode].Flags[flag] = nil
	else
		kv[ident][gamemode].Flags[flag] = BoolToNumber( _setval )
	end
	
	if ( tablex.Count( kv[ident][gamemode].Flags ) < 1 ) then kv[ident][gamemode].Flags = nil end
	if ( tablex.Count( kv[ident][gamemode] ) < 1 ) then kv[ident][gamemode] = nil end
	
	SetKeyValues(kv, GroupAuthFile)
end

function GroupObj:GetConfig( key, value )
	local kv = GetKeyValues(GroupAuthFile)
	local ident = self.Ident
	local strstart,strend,gamemode,config = string.find(key, "(%w*):(%w*)")
	
	if ( gamemode == "" ) then gamemode = ":GLOBAL" end
	if ( gamemode == nil ) then
		config = key
		strstart,strend,gamemode = string.find( GAMEMODE.Folder, ".*/(%a*)" )
	end
	
	if ( kv[ident][gamemode] == nil ) then return end
	if ( kv[ident][gamemode].Config == nil ) then return end
	if ( kv[ident][gamemode].Config[config] == nil ) then return end
	
	return tostring( kv[ident][gamemode].Config[config] )
end

function GroupObj:SetConfig( key, value )
	local kv = GetKeyValues(GroupAuthFile)
	local ident = self.Ident
	local strstart,strend,gamemode,config = string.find(key, "(%w*):(%w*)")
	
	if ( gamemode == "" ) then gamemode = ":GLOBAL" end
	if ( gamemode == nil ) then
		config = key
		strstart,strend,gamemode = string.find( GAMEMODE.Folder, ".*/(%a*)" )
	end
	
	local _setval
	
	if ( value != nil ) then
		_setval = value
	end
	
	if ( kv[ident][gamemode] == nil ) then kv[ident][gamemode] = {} end
	if ( kv[ident][gamemode].Config == nil ) then kv[ident][gamemode].Config = {} end
	
	if ( _setval == nil ) then
		kv[ident][gamemode].Config[config] = nil
	else
		kv[ident][gamemode].Config[config] = _setval
	end
	
	if ( tablex.Count( kv[ident][gamemode].Config ) < 1 ) then kv[ident][gamemode].Config = nil end
	if ( tablex.Count( kv[ident][gamemode] ) < 1 ) then kv[ident][gamemode] = nil end	
	
	SetKeyValues(kv, GroupAuthFile)
end