local RD = {}
--local nettable = {};
--local ent_table = {};
local resourcenames = {}
local resources = {}
local status = false

local rd_cache = cache.create(1, false) --Store data for 1 second
--[[

]]

--local functions

RD_OverLay_Distance = CreateClientConVar("rd_overlay_distance", "512", false, false)
RD_OverLay_Mode = CreateClientConVar("rd_overlay_mode", "-1", false, false)
local client_chosen_number = CreateClientConVar("number_to_send", "1", false, false)
local client_chosen_hold = CreateClientConVar("number_to_hold", "0", false, false)

----------NetTable functions

local function CreateNetTable(netid)
	nettable[netid] = {}
	local index = nettable[netid]
	index.resources = {}
	index.cons = {}
end
--[[
umsg.Start("RD_AddNet")
		umsg.Short(netid)
	umsg.End()
]]
--[[local function AddNet( um )
	local netid = um:ReadShort()
	if netid > 0 then
		CreateNetTable(netid)
	end
end
usermessage.Hook("RD_AddNet", AddNet)]]

--[[
umsg.Start("RD_AddResoureToNet")
		umsg.Short(netid)
		umsg.String(resource)
		umsg.Long(maxvalue)
		umsg.Long(value)
	umsg.End()
]]

--[[local function AddResoureToNet( um )
	local netid = um:ReadShort()
	local resource = um:ReadString()
	local maxvalue = um:ReadLong()
	local value = um:ReadLong()
	if not table.HasValue(resources, resource) then
		table.insert(resources, resource)
	end
	if netid > 0 then
		if not nettable[netid] then
			CreateNetTable(netid)
		end
		nettable[netid].resources[resource] = {}
		nettable[netid].resources[resource].maxvalue = maxvalue
		nettable[netid].resources[resource].value = value
	end
end
usermessage.Hook("RD_AddResoureToNet", AddResoureToNet)]]

--[[

--Needed?
umsg.Start("RD_AddConToNet")
		umsg.Short(netid)
		umsg.Short(entid)
	umsg.End()
]]

--[[local function AddConToNet(um)
	local netid = um:ReadShort()
	local conid = um:ReadShort()
	if netid > 0 then
		if not nettable[netid] then
			CreateNetTable(netid)
		end
		table.insert(nettable[netid].cons, conid)
	end
end
usermessage.Hook("RD_AddConToNet", AddConToNet)]]

--[[
umsg.Start("RD_RemoveNetCons")
		umsg.Short(netid)
	umsg.End()
]]

--[[local function RemoveConsFromNet( um)
	local netid = um:ReadShort()
	if netid > 0 then
		if not nettable[netid] then
			CreateNetTable(netid)
		end
		nettable[netid].cons = {}
	end
end
usermessage.Hook("RD_RemoveNetCons", RemoveConsFromNet)]]
--[[
umsg.Start("RD_RemoveNet")
		umsg.Short(netid)
	umsg.End()
]]

--[[local function RemoveNet( um)
	local netid = um:ReadShort()
	if netid > 0 then
		nettable[netid] = nil
	end
end
usermessage.Hook("RD_RemoveNet", RemoveNet)]]

---------ent_table functions
--[[
umsg.Start("RD_AddEnt")
		umsg.Short(entid)
	umsg.End()
]]

--[[local function CreateEntTable(entid)
	ent_table[entid] = {}
	ent_table[entid].network = 0
	ent_table[entid].resources = {}
end

local function AddEnt( um )
	local entid = um:ReadShort()
	CreateEntTable(entid)
end
usermessage.Hook("RD_AddEnt", AddEnt)]]


--[[
umsg.Start("RD_AddResoureToEnt")
		umsg.Short(entid)
		umsg.String(resource)
		umsg.Long(maxvalue)
		umsg.Long(value)
	umsg.End()
]]

--[[local function AddResourceToEnt( um )
	local entid = um:ReadShort()
	local resource = um:ReadString()
	local maxvalue = um:ReadLong()
	local value = um:ReadLong()
	if not ent_table[entid] then
		CreateEntTable(entid)
	end
	ent_table[entid].resources[resource] = {}
	ent_table[entid].resources[resource].maxvalue = maxvalue
	ent_table[entid].resources[resource].value = value
end
usermessage.Hook("RD_AddResoureToEnt", AddResourceToEnt)]]

--[[
umsg.Start("RD_ChangeNetOnEnt")
		umsg.Short(entid)
		umsg.Short(netid)
	umsg.End()
]]
--[[local function ChangeNetOnEnt( um )
	local entid = um:ReadShort()
	local netid = um:ReadShort()
	if not ent_table[entid] then
		CreateEntTable(entid)
	end
	ent_table[entid].network = netid
end
usermessage.Hook("RD_ChangeNetOnEnt", ChangeNetOnEnt)]]

--[[
umsg.Start("RD_RemoveEnt")
		umsg.Short(entid)
	umsg.End()
]]
--[[local function RemoveEnt( um )
	local entid = um:ReadShort()
	ent_table[entid] = nil
end
usermessage.Hook("RD_RemoveEnt", RemoveEnt)]]

------ nettable and ent_table
--[[
umsg.Start("RD_ClearNets")
	umsg.End()
]]
local function ClearNets( um )
	--nettable = {}
	--ent_table = {}
	rd_cache:clear();
end
usermessage.Hook("RD_ClearNets", ClearNets)

--[[
umsg.Start("RD_Entity_Data", ply)
	umsg.Short(entid) --send key to update
	umsg.Short(rddata.network) --send network used in entity
	
	local nr_of_resources = table.Count(rddata.resources);
	umsg.Short(nr_of_resources) --How many resources are going to be send?
	if nr_of_resources > 0 then
		for l, w in pairs(rddata.resources) do
			umsg.String(l)
			umsg.Long(w.maxvalue)
			umsg.Long(w.value)
		end
	end
	
umsg.End()
]]

local function ReadBool()
   return net.ReadBit() == 1
end

local function ReadShort()
   return net.ReadInt(16);
end

local function ReadLong()
    return net.ReadInt(32);
end

local dev = GetConVar("developer")
local function AddEntityToCache( nrofbytes )
    if dev:GetBool() then print("RD_Entity_Data #", nrofbytes, " bytes received") end
	local data = {}

	data.entid = ReadShort() --Key
	local up_to_date = ReadBool();
	if up_to_date then
		rd_cache:update("entity_"..tostring(data.entid))
	end
	data.network = ReadShort() --network key
	
	data.resources = {}
	local i = 0;
	local nr_of_resources = ReadShort();
	if (nr_of_resources > 0) then
		--print("nr_of_sources", nr_of_resources)
		local resource 
		local maxvalue 
		local value
		for i = 1, nr_of_resources do
			--print(i)
			resource = net.ReadString()
			maxvalue = ReadLong()
			value = ReadLong()
			
			data.resources[resource] = {value = value, maxvalue = maxvalue}
		end
	end
	
	rd_cache:add("entity_"..tostring(data.entid), data)
end
net.Receive("RD_Entity_Data", AddEntityToCache)

--[[
umsg.Start("RD_Network_Data", ply)
		umsg.Short(netid) --send key to update
		
		local nr_of_resources = table.Count(rddata.resources);
		umsg.Short(nr_of_resources) --How many resources are going to be send?
		if nr_of_resources > 0 then
			for l, w in pairs(rddata.resources) do
				umsg.String(l)
				umsg.Long(w.maxvalue)
				umsg.Long(w.value)
			end
		end
		
		local nr_of_cons = table.Count(rddata.cons);
		umsg.Short(nr_of_cons) --How many connections are going to be send?
		if nr_of_cons > 0 then
			for l, w in pairs(rddata.cons) do
				umsg.Short(w)
			end
		end
		
	umsg.End()

]]

local function AddNetworkToCache( nrofbytes )
    if dev:GetBool() then print("RD_Network_Data #", nrofbytes, " bytes received") end
	local data = {}
	
	data.netid = ReadShort() --network key
	local up_to_date = ReadBool();
	if up_to_date then
		rd_cache:update("network_"..tostring(data.netid))
	end
	
	data.resources = {}
	local i = 0;
	local nr_of_resources = ReadShort();
	if (nr_of_resources > 0) then
		--print("nr_of_sources", nr_of_resources)
		local resource 
		local maxvalue 
		local value
		local localmaxvalue
		local localvalue
		for i = 1, nr_of_resources do
			--print(i)
			resource = net.ReadString()
			maxvalue = ReadLong()
			value = ReadLong()
			localmaxvalue = ReadLong()
			localvalue = ReadLong()
			
			data.resources[resource] = {value = value, maxvalue = maxvalue, localvalue = localvalue, localmaxvalue = localmaxvalue}
		end
	end
	
	data.cons = {}
	local nr_of_cons = ReadShort();
	if (nr_of_cons > 0) then
		--print("nr_of_cons", nr_of_cons)
		for i = 1, nr_of_cons do
			--print(i)
			con = ReadShort()
			table.insert(data.cons, con);
		end
	end
	
	rd_cache:add("network_"..tostring(data.netid), data)
end
net.Receive("RD_Network_Data", AddNetworkToCache)

--end local functions

--The Class
--[[
	The Constructor for this Custom Addon Class
]]
function RD.__Construct()
	status = true
	return true
	--return false , "No Implementation yet"
end

--[[
	The Destructor for this Custom Addon Class
]]
function RD.__Destruct()
	status = false
	return true
	--return false , "No Implementation yet"
end

--[[
	Get the required Addons for this Addon Class
]]
function RD.GetRequiredAddons()
	return {}
end

--[[
	Get the Boolean Status from this Addon Class
]]
function RD.GetStatus()
	return status
end

--[[
	Get the Version of this Custom Addon Class
]]
function RD.GetVersion()
	return 3.1, "Alpha"
end

local isuptodatecheck;
--[[
	Update check
]]
function RD.IsUpToDate(callBackfn)
	if not CAF.HasInternet then
		return
	end
	if isuptodatecheck ~= nil then
		callBackfn(isuptodatecheck);
		return
	end
	--[[http.Get("http://www.snakesvx.net/versions/rd.txt","",
		function(html,size)
			local version = tonumber(html);
			if(version) then
				local latest = version;
				if(latest > RD.GetVersion()) then
					isuptodatecheck = false;
					callBackfn(false)
				else
					isuptodatecheck = true;
					callBackfn(true)
				end
			end
		end
	); ]]
end

--[[
	Get any custom options this Custom Addon Class might have
]]
function RD.GetExtraOptions()
	return {}
end

--[[
	Gets a menu from this Custom Addon Class
]]
function RD.GetMenu(menutype, menuname) --Name is nil for main menu, String for others
	local data = {}
	return data
end

--[[
	Get the Custom String Status from this Addon Class
]]
function RD.GetCustomStatus()
	return ; --CAF.GetLangVar("Not Implemented Yet")
end

--[[
	Returns a table containing the Description of this addon
]]
function RD.GetDescription()
	return {
				"Resource Distribution",
				"",
				""
			}
end
CAF.RegisterAddon("Resource Distribution", RD, "1")

--[[ Shared stuff ]]

--TODO UPDATE FROM HERE

--RD.GetEntityTable(ent)
--RD.GetNetTable(netid)

--[[function RD.getConnectedNets(netid) -- NEEDED ANYWHERE?
	local contable = {}
	local tmpcons = { netid}
	while(table.Count(tmpcons) > 0) do
		for k, v in pairs(tmpcons) do
			if not table.HasValue(contable, v) then
				table.insert(contable, v)
				if nettable[v] and nettable[v].cons then
					if table.Count(nettable[v].cons) > 0 then
						for l, w in pairs(nettable[v].cons) do
							table.insert(tmpcons, w)
						end
					end
				end
			end
			table.remove(tmpcons, k)
		end
	end
	return contable
end]]

function RD.GetNetResourceAmount(netid, resource)
	if not resource then return 0, "No resource given" end
	local data = RD.GetNetTable(netid);
	if not data then return 0, "Not a valid network" end
	if not data.resources or (data.resources and table.Count(data.resources)==0) then return 0, "No resources available" end
	if not data.resources[resource] then return 0, "Resource not available" end

	local amount = 0;
	amount = data.resources[resource].value
	return amount
end

function RD.GetResourceAmount(ent, resource)
	if not IsValid( ent ) then return 0, "Not a valid entity" end
	if not resource then return 0, "No resource given" end
	local data=RD.GetEntityTable(ent)
	if not data.resources or (data.resources and table.Count(data.resources)==0) then return 0, "No resources available" end
	if not data.resources[resource] then return 0, "Resource not available" end

	local amount = 0
	amount = data.resources[resource].value
	return amount
end

--[[function RD.GetUnitCapacity(ent, resource)
	if not IsValid( ent ) then return 0, "Not a valid entity" end
	if not resource then return 0, "No resource given" end
	local amount = 0
	if ent_table[ent:EntIndex( )] then
		local index = ent_table[ent:EntIndex( )];
		if index.resources[resource] then
			amount = index.resources[resource].maxvalue
		end
	end
	return amount
end]]

function RD.GetNetNetworkCapacity(netid, resource)
	if not resource then return 0, "No resource given" end
	local data = RD.GetNetTable(netid);
	if not data then return 0, "Not a valid network" end
	if not data.resources or (data.resources and table.Count(data.resources)==0) then return 0, "No resources available" end
	if not data.resources[resource] then return 0, "Resource not available" end

	local amount = 0;
	amount = data.resources[resource].maxvalue
	return amount
end

function RD.GetNetworkCapacity(ent, resource)
	if not IsValid( ent ) then return 0, "Not a valid entity" end
	if not resource then return 0, "No resource given" end
	local data = RD.GetEntityTable(ent)
	if not data then return 0, "Not a valid network" end
	if not data.resources or (data.resources and table.Count(data.resources)==0) then return 0, "No resources available" end
	if not data.resources[resource] then return 0, "Resource not available" end

	local amount = 0
	if data.resources[resource] then
		amount = data.resources[resource].maxvalue
	end
	return amount
end

local requests = {}
local ttl = 0.2; --Wait 0.2 second before doing a new request

function RD.GetEntityTable(ent)
	local entid = ent:EntIndex( )
	local id = "entity_"..tostring(entid)
	local data, needs_update = rd_cache:get(id);
	if not data or needs_update then
		if not requests[id] or requests[id] < CurTime() then
			--Do (new) request
			requests[id] = CurTime() + ttl;
			RunConsoleCommand("RD_REQUEST_RESOURCE_DATA", "ENT", entid, needs_update and "UPDATE");
		end
	end
	--PrintTable(data)
	return data or {}
end

function RD.GetNetTable(netid)
	local id = "network_"..tostring(netid)
	local data, needs_update = rd_cache:get(id);
	if not data or needs_update then
		if not requests[id] or requests[id] < CurTime() then
			--Do (new) request
			requests[id] = CurTime() + ttl;
			RunConsoleCommand("RD_REQUEST_RESOURCE_DATA", "NET", netid, needs_update and "UPDATE");
		end
	end
	return data or {}
end

--TODO UPDATE TO HERE

function RD.AddProperResourceName(resource, name)
	if not resource or not name then return end
	if not table.HasValue(resources, resource) then
		table.insert(resources, resource)
	end
	resourcenames[resource] = name
end

function RD.GetProperResourceName(resource)
	if not resource then return "" end
	if resourcenames[resource] then
		return resourcenames[resource]
	end
	return resource
end

function RD.GetAllRegisteredResources()
	if not resourcenames or table.Count(resourcenames) < 0 then
		return {}
	end
	return table.Copy(resourcenames)
end

function RD.GetRegisteredResources()
	return table.Copy(resources)
end

--[[function RD.GetNetworkIDs() --NEEDED FOR SOMEONE?
	local ids = {}
	if table.Count(nettable) > 0 then
		for k, v in pairs(nettable) do
			if not v.clear then
				table.insert(ids, k)
			end
		end
	end
	return ids
end]]

function RD.PrintDebug(ent)
	if ent then
		if ent.IsNode then
			local nettable = RD.GetNetTable(ent.netid)
			PrintTable(nettable)
		elseif ent.IsValve then
			--
		elseif ent.IsPump then
			--
		else
			local enttable = RD.GetEntityTable(ent)
			PrintTable(enttable)
		end
	end
end

list.Add( "BeamMaterials", "cable/rope_icon" )
list.Add( "BeamMaterials", "cable/cable2" )
list.Add( "BeamMaterials", "cable/xbeam" )
list.Add( "BeamMaterials", "cable/redlaser" )
list.Add( "BeamMaterials", "cable/blue_elec" )
list.Add( "BeamMaterials", "cable/physbeam" )
list.Add( "BeamMaterials", "cable/hydra" )

--holds the materials
beamMat = {}

for _,mat in pairs(list.Get( "BeamMaterials" )) do
	beamMat[mat] = Material(mat)
end

-----------------------------------------
--START BEAMS BY MADDOG
-----------------------------------------

local xbeam = Material("cable/xbeam")
-- Desc: draws beams on ents
function RD.Beam_Render( ent )

	--get the number of beams to use
	local intBeams = ent:GetNWInt( "Beams" )

	--if we have beams, then create them
	if intBeams and intBeams ~= 0 then
		--make some vars we are about to use
		local i, start, scroll = 1, ent:GetNWVector( "Beam1" ), CurTime() * 0.5

		--get beam info and explode into a table
		local beamInfo = string.Explode(";", ent:GetNWString("BeamInfo"))

		--get beam info from table (1: beamMaterial 2: beamSize 3: beamR 4: beamG 5: beamB 6: beamAlpha)
		local beamMaterial, beamSize, color = (beamMat[beamInfo[1]] or xbeam), (beamInfo[2] or 2), Color( beamInfo[3] or 255, beamInfo[4] or 255, beamInfo[5] or 255, beamInfo[6] or 255 )

		-- set material
		render.SetMaterial( beamMaterial )
		render.StartBeam( intBeams )	--how many links (points) the beam has

		--loop through all beams
		for i=1,intBeams do
			--get beam data
			local beam, ent = ent:GetNWVector( "Beam" .. tostring(i) ), ent:GetNWEntity( "BeamEnt" .. tostring(i) )

			--if no beam break for statement
			if not beam or  not ent or not ent:IsValid() then
				ent:SetNWInt( "Beams", 0 )
				break
			end

			--get beam world vector
			local pos = ent:LocalToWorld(beam)

			--update scroll
			scroll = scroll - (pos-start):Length()/10

			-- add point
			render.AddBeam(pos, beamSize, scroll, color)

			--reset start postion
			start = pos
		end

		--beam done
		render.EndBeam()
	end
end
-----------------------------------------
--END BEAMS BY MADDOG
-----------------------------------------

--Alternate Use Code--

local function GenUseMenu(ent)
	local SmallFrame = vgui.Create("DFrame")
	SmallFrame:SetPos( (ScrW()/2)-110,(ScrH()/2)-100)
	SmallFrame:SetSize( 220, (#ent.Inputs*40)+90 )
	SmallFrame:SetTitle( ent.PrintName )
	
	local ypos = 30
	
	local HoldSlider = vgui.Create( "DNumSlider", SmallFrame )
		HoldSlider:SetPos( 10,ypos )
		HoldSlider:SetSize( 200, 30 )
		HoldSlider:SetText( "Time to Hold:" )
		HoldSlider:SetMin( 0 )
		HoldSlider:SetMax( 10 )
		HoldSlider:SetDecimals( 1 )
		HoldSlider:SetConVar( "number_to_hold" )
		
		ypos = ypos + 40
	for k,v in pairs(ent.Inputs) do
		local NumSliderThingy = vgui.Create( "DNumSlider", SmallFrame )
		NumSliderThingy:SetPos( 10,ypos )
		NumSliderThingy:SetSize( 120, 30 )
		NumSliderThingy:SetText( v.." :" )
		NumSliderThingy:SetMin( 0 )
		NumSliderThingy:SetMax( 10 )
		NumSliderThingy:SetDecimals( 0 )
		NumSliderThingy:SetConVar( "number_to_send" )
		
		local SendButton = vgui.Create( "DButton", SmallFrame )
		SendButton:SetPos(140,ypos)
		SendButton:SetText("Send Command")
		SendButton:SizeToContents()
		SendButton.DoClick = function()
			RunConsoleCommand("send_input_selection_to_server",ent.serverindex,v,client_chosen_number:GetInt(),client_chosen_hold:GetFloat())
		end
		
		ypos = ypos + 40
	end
	
	SmallFrame:MakePopup()
end 

local function RecieveInputs(um)
	local last = um:ReadBool()
	local input = um:ReadString()
	local index = um:ReadShort()
	local ent = ents.GetByIndex(index)
	ent.serverindex = index
	if not ent.Inputs then ent.Inputs = {} end
	if not table.HasValue(ent.Inputs,input) then table.insert(ent.Inputs,input) end
	if last and last == true then
		GenUseMenu(ent)
	end
end
usermessage.Hook("RD_AddInputToMenu", RecieveInputs)
