-- vim:ts=4:sw=4:noet
--

DC():PrintDebug( "** Started startup.lua **" )


--/////////////////////////////////////
--// Hub manager
--/////////////////////////////////////

if not dcpp then
	dcpp = {}
	dcpp._hubs = {}
end

dcpp.addHub = function( this, hub )
	if not this._hubs[hub] then
		DC():PrintDebug( "Hub added (id = "..tostring( hub )..")" )
		this._hubs[hub] = createHub( hub )
		return this._hubs[hub]
	else
		DC():PrintDebug( "Tried to add existing hub on: "..this._hubs[hub]:getHubName()..
								" (id = "..tostring( hub )..")" )
		return nil
	end
end

dcpp.getHub = function( this, hub )
	if this._hubs[hub] then
		return this._hubs[hub]
	else
		DC():PrintDebug( "Tried to fetch an unknown hub (id = "..tostring( hub )..")" )
		return this:addHub( hub ):setPartial() -- tell the object that we missed the logon
	end
end

dcpp.removeHub = function( this, hub )
	if this._hubs[hub] then
		DC():PrintDebug( "Hub removed: "..this._hubs[hub]:getHubName().." (id = "..tostring( hub )..")" )
		this._hubs[hub]:destroy()
		this._hubs[hub] = nil
	else
		DC():PrintDebug( "Tried to remove non-existent hub (id = "..tostring( hub )..")" )
	end		
end

dcpp.listHubs = function( this )
	DC():PrintDebug( "** Listing hubs **" )
	for k,v in this._hubs do
		DC():PrintDebug( tostring( k ).." ("..v:getHubName()..")" )
	end
end	


--/////////////////////////////////////
--// Hub object
--/////////////////////////////////////

function createHub( hubid )
	local hub = {}

	hub._id = hubid
	hub._users = {}
	hub._name = nil
	hub._myNick = nil
	hub._noMyNick = nil

	hub.getId = function( this )
		return this._id
	end

	hub.getUser = function( this, nick, op )
		local r = this._users[nick]
		if r then
			return r:setOp( op )
		else
			this._users[nick] = createUser( this, nick ):setOp( op )
			return this._users[nick]
		end
	end

	hub.removeUser = function( this, nick )
		this._users[nick] = nil
	end

	hub.setPartial = function( this )
		--// we're most likely missing logon info
		this._noMyNick = 1
	end	

	hub.setHubName = function( this, msg )
		this._name = msg
	end

	hub.getHubName = function( this )
		if this._name then
			return this._name.." ("..DC():GetHubIpPort( this._id )..")"
		else
			return DC():GetHubIpPort( this._id )
		end
	end

	hub.setOwnNick = function( this, nick )
		DC():PrintDebug( this:getHubName().."> Setting own nick: "..nick )
		this._myNick = nick
	end

	hub.getOwnNick = function( this )
		if this._myNick and not this._noMyNick then
			return this._myNick
		else
			DC():PrintDebug( this:getHubName().."> Own nick not set!" )
		end
	end

	hub.hasOwnNick = function( this )
		return this._myNick
	end

	hub.destroy = function( this )
	end

	hub.sendChat = function( this, msg )
		local ownNick = this:getOwnNick()
		if ownNick then
			DC():SendHubMessage( this:getId(), "<"..ownNick.."> "..msg.."|" )
		else
			DC():PrintDebug( this:getHubName().."> Ignoring hub.sendChat, myNick unknown" )
		end
	end

	hub.sendPrivMsgTo = function( this, victim, msg )
		local ownNick = this:getOwnNick()
		if ownNick then
			DC():SendHubMessage( this:getId(), "$To: "..victim.." From: "..ownNick.." $"..msg.."|" )
		else
			DC():PrintDebug( this:getHubName().."> Ignoring hub.sendPrivMsg, myNick unknown" )
		end	
	end


	--////////////////
	--// Own functions
	--////////////////

	hub.onSearch = function( this, msg )
		--//DC():PrintDebug( this:getHubName().."> "..msg )
	end
	
	hub.onHello = function( this, user, msg )
	end
	
	hub.onMyInfo = function( this, user, msg )
		--//DC():PrintDebug( this:getHubName().."> User added: "..user:getNick() )
	end
	
	hub.onQuit = function( this, nick, msg )
		--//DC():PrintDebug( this:getHubName().."> User removed: "..nick )
	end
	
	hub.onHubName = function( this, hubname, msg )
	end
	return hub
end


--/////////////////////////////////////
--// User object
--/////////////////////////////////////

function createUser( hub, nick )
	local user = {}

	user._hub = hub
	user._nick = nick
	user._op = nil
	user._handled_messages = {} -- flood protection

	user.setOp = function( this, op )
		this._op = op
		return this
	end

	user.isOp = function( this )
		return this._op
	end
	
	user.getNick = function( this )
		return this._nick
	end

	user.sendPrivMsgFmt = function( this, msg )
		local ownNick = this._hub:getOwnNick()
		if ownNick then
			this._hub:sendPrivMsgTo( this._nick, "<"..ownNick.."> "..msg.."|" )
		else
			DC():PrintDebug( this._hub:getHubName().."> Ignoring user.sendPrivMsgFmt, myNick unknown" )
		end
		return this
	end

	user.setMsgHandled = function( this, which )
		this._handled_messages[which] = 1
		return this
	end

	user.msgHandled = function( this, which )
		return this._handled_messages[which]
	end

	return user
end


--/////////////////////////////////////
--// Handlers
--/////////////////////////////////////

function DataArrival( hub, msg )
	local h = dcpp:getHub( hub )

	local ret,c,cmd = string.find( msg, "^%$([^ ]+)" )

	if ret then



		if cmd == "Search" then
			return h:onSearch( msg )
		elseif cmd == "MyINFO" then	
			return h:onSearch( msg )
		elseif ret then
			DC():PrintDebug( msg )
		end
		

		if cmd == "Search" then
			return h:onSearch( msg )
		elseif cmd == "Hello" then
			local nick = string.sub( msg, 8 )
			if not h:hasOwnNick() then
				h:setOwnNick( nick ) -- cannot trust value of hub._myNick (check hub._noMyNick)
			end
			return h:onHello( h:getUser( nick ), msg )
		elseif cmd == "MyINFO" and string.sub( msg, 1, 13 ) == "$MyINFO $ALL " then
			local nick = string.sub( msg, 14, string.find( msg, " ", 14, 1 ) - 1 )
			return h:onMyInfo( h:getUser( nick ), msg )
		elseif cmd == "Quit" then
			local nick = string.sub( msg, 7 )
			h:removeUser( nick )
			return h:onQuit( nick, msg )
		elseif cmd == "HubName" then
			local hubname = string.sub( msg, 10 )
			h:setHubName( hubname )
			return h:onHubName( hubname, msg )
		elseif cmd == "OpList" then
			for nick in string.gfind( string.sub( msg, 9 ), "[^$]+") do
				h:getUser( nick, 1 )
			end
			return nil
		--elseif string.sub( msg, 1, 10 ) == "$NickList " then
		--	for nick in string.gfind( string.sub( msg, 9, -1), "[^$]+") do
		--		h:getUser( nick )
		--	end
		--	return nil
		elseif cmd == "To:" then
			local ret,c,to,from,fulltext = string.find( msg, "^%$To: ([^ ]+) From: ([^ ]+) %$(.*)$" )
			if ret then
				local ret,c,prefix,text = string.find( fulltext, "^(%b<> )(.*)$" )
				if ret then
					return h:onPrivateMessage( h:getUser( from ), to, prefix, text )
				else
					return h:onPrivateMessage( h:getUser( from ), to, nil, fulltext )
				end
			end
		end
	elseif string.sub( msg, 1, 1 ) == "<" then
		local ret,c,nick,text = string.find( msg, "^<([^>]+)> (.*)$" )
		if ret and h:getOwnNick() and nick ~= h:getOwnNick() then -- don't be flooding mainchat now..
			return h:onChatMessage( h:getUser( nick ), text )
		end
	end
end

function UserDataIn( user, msg )
    --DC():PrintDebug( tostring( user ).." << "..msg )
end

function UserDataOut( user, msg )
	--DC():PrintDebug( tostring( user ).." >> "..msg )
end

function OnCommandEnter( msg )
	-- macros and such can be used here, but not practically until I remove next comment :p
	-- returning true here will at some point prevent DC++ from further processing it.
end

function OnTimer()
	-- Set this up by calling Timer():SetTimer(seconds), Timer():StartTimer(),
	-- and Timer():StopTimer()
end

function OnHubAdded( hub )
	dcpp:addHub( hub )
end

function OnHubRemoved( hub )
	dcpp:removeHub( hub )
end


--/////////////////////////////////////
--// Utility functions
--/////////////////////////////////////

function SendActiveSearchResult( hub, ip_port, search_nick, filename, filesize, open_slots, total_slots )
	DC():SendUDP( ip_port, "$SR "..search_nick.." "..filename.."\005".. filesize.." "..open_slots..
			"/"..total_slots.."\005"..dcpp:getHub( hub ):getHubName() ) -- no pipe in UDP $SR
end
