--------------------- Polite Notice ---------------------
-- YaLB was created by Daniel Astbury (jnz)            --
-- The source is avaliable to anyone, however          --
-- please do not take it can claim it yours.           --
-- Please follow my example and release your own.      --
-----------------------Thank you-------------------------


IRC_Address = ""
IRC_Port = 0
IRC_Nick = ""
IRC_Channels = {}
IRC_NickServPass = ""
IRC_Client = -1
Packet_Construct = ""
IRC_Connected = false
IRC_Last = 0
IRC_PingSent = false
IRC_Started = false
IRC_Reconnect = 0

IRC_Channel = {Name = "", Restricted = false, Password = nil}

Channels = {}
Channel = {Users = {}, Name = ""}
User = {Status = "", Nick = ""}
    
function Channel:new (o)
	o = o or {}
	setmetatable(o, self)	
	self.__index = self
	return o
end

function User:new (o)
	o = o or {}
	setmetatable(o, self)	
	self.__index = self
	return o
end

function IRC_Channel:new(o)
	o = o or {}
	setmetatable(o, self)	
	self.__index = self
	return o
end

Main_Think_CoRoutine = nil
function Load_IRC()
	IRC_Address = Find_Block("YaLB", "IRC").Keys['Host']
	IRC_Port = tonumber(Find_Block("YaLB", "IRC").Keys['Port'])
	IRC_Nick = Find_Block("YaLB", "IRC").Keys['Nick']
	IRC_NickServPass = Find_Block("YaLB", "IRC").Keys['NickServ']

	local chan_block = Find_Block("YaLB", "IRC", "Channels")
	for i,v in pairs(chan_block.Next_Level) do
		local c = IRC_Channel:new({Name = i, Restricted = (string.lower(v.Keys["Output"] or "") ~= "public"), Password = v.Keys["Output"]})
		table.insert(IRC_Channels, c)
	end
	IRC_Started = true
	
	Main_Think_CoRoutine = coroutine.create(IRC_Think_Loop)
end

function Send_Channel(Channel, Message)
	Send(IRC_Client, "PRIVMSG " .. Channel .. " :" .. Message .. "\n")
end

function IRC_Channel_Chat(Channel, User, Message)
	IRC_Command_Chat(User, Channel, Message)			
end

function IRC_Callback(Packet)
	IRC_Last = tClock()
	IRC_PingSent = false
	local tok = GetWords(Packet)
	if tok[1] == "PING" then
		Send(IRC_Client, string.format("PONG %s\n", tok[2]))
		return
	end
	
	if tok[2] == "PRIVMSG" then
		channel = tok[3];
		message = string.match(Packet, "[^%s]+%s*[^%s]+%s*[^%s]+%s*:(.+)")
		user = string.match(tok[1], ":([^:!]+)!")
		IRC_Channel_Chat(channel, user, message)
	end
	
	if tok[2] == "001" then
		
		for i,v in pairs(IRC_Channels) do
			Send(IRC_Client, string.format("JOIN :%s\n", v.Name))
		end
		
		Send(IRC_Client, "PRIVMSG NickServ :IDENTIFY " .. IRC_NickServPass .. "\n")
	end
	
	if tok[2] == "353" then
		local channel = tok[5]
		for i,v in pairs(Channels) do
			if v.Name == channel then
				table.remove(Channels, i)
			end	
		end
		
		local c = Channel:new()
		c.Users = {}
		c.Name = channel
		local it = 6
		while tok[it] ~= nil do
			local tmp = tok[it]
			if it == 6 then
				tmp = string.match(tmp, "[%:]+(.+)")
			end
			
			local stat = string.match(tmp, "[%+%%%@%&%~]")
			local nick = string.match(tmp, "[%+%%%@%&%~%:](.+)")
			if stat == nil then
				stat = ""
				nick = tmp
			end
			
			local u = User:new()
			u.Status = stat
			u.Nick = nick
			table.insert(c.Users, u)
			it = it + 1
		end
		table.insert(Channels, c)	
	end
	
	if tok[2] == "MODE" then
		Send(IRC_Client, string.format("NAMES %s\n", tok[3]))
	end
	
	if tok[2] == "PART" then
		Send(IRC_Client, string.format("NAMES %s\n", tok[3]))
	end
	
	if tok[2] == "JOIN" and string.match(tok[1], ":([^:!]+)!") ~= IRC_Nick then
		Send(IRC_Client, string.format("NAMES %s\n", tok[3]))
	end		
end

IRC_Think_Timer = 0
function IRC_Think_Loop()
	while true do
		coroutine.yield()
		
		if IRC_Started == false then 
			coroutine.yield()
		end
		if IRC_Connected and tClock() - IRC_Last > 20000 and IRC_PingSent == false then
			IRC_PingSent = true
			Send(IRC_Client, "PING :LuaBot\n")
		end
	
		if IRC_Connected and tClock() - IRC_Last > 40000 and IRC_PingSent == true then
			print("Diconnected from IRC...reconnecting in 5 seconds.")
			IRC_Reconnect = tClock()
			IRC_Connected = false
			IRC_PingSent = false
			IRC_Last = tClock()
		
			Disconnect(IRC_Client)
			IRC_Client = -1
		end
	
		if IRC_Connected == false and tClock() - IRC_Reconnect > 5000 then
			IRC_Client = Client(IRC_Address, IRC_Port)
	
			if IRC_Client == -1 then
				print("Failed to connect.\n")
			IRC_Reconnect = tClock()
			else
				print("Connected to IRC server.\n")
				IRC_Connected = true
				Send(IRC_Client, "NICK "..IRC_Nick.."\nUSER "..IRC_Nick.." 0 * :Lua bot\n")
			end
		end
		
		if tClock() - IRC_Think_Timer > 100 then
			if IRC_Client > -1 then
				if DataAvaliable(IRC_Client) == 0 then
					if Packet_Construct == nil then
						Packet_Construct = string.char()
					end
					Packet_Construct = Packet_Construct .. Recv(IRC_Client)
				end	
			end
			
			if Packet_Construct ~= nil then
				if string.find(Packet_Construct, "\n") ~= nil then
						local tmp = string.match(Packet_Construct, "[^\n]+")
						Packet_Construct = string.match(Packet_Construct, "\n(.+)")
						local status, err = pcall(IRC_Callback, tmp)
						if err then
							IRC_Sendc("b", err)
						end 
				end
			end
			IRC_Think_Timer = tClock()
		end
	end
end

function Get_IRC_Status(Nick, Channel)
	for i,v in pairs(Channels) do
		if string.lower(v.Name) == string.lower(Channel) then	
			for k,val in pairs(v.Users) do
				if val.Nick == Nick then
					if val.Status == "" then
						return nil
					else
						return val.Status
					end
				end	
			end
		end
	end
	return nil
end	

function IRC_Sendc(cType, ...)
	local err,str = pcall(string.format, unpack(arg))
	if err ~= true then
		error(str, 2)
	end
	
	for i,v in pairs(IRC_Channels) do
		if (v.Restricted == false and cType == "p") or v.Restricted == true then
			Send(IRC_Client, string.format("PRIVMSG %s :%s\n", v.Name, str))
		end
	end
end

function IRC_Send(Channel, ...)
	local err,str = pcall(string.format, unpack(arg))
	if err ~= true then
		error(str, 2)
	end
	
	Send(IRC_Client, string.format("PRIVMSG %s :%s\n", Channel, str))
end
