--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| 	Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]


TOOL.Category = "Render"
TOOL.Name = "SubMaterial"--"#tool.material.name"
if CLIENT then
	language.Add( "tool.submaterial.name", "SubMaterial Tool" )
	language.Add( "tool.submaterial.desc", "Allow to override submaterials of model." )
	language.Add( "tool.submaterial.0", "Wheel Up/Down: Select target part, Primary: Apply material, Secondary: Set default material, Reload: Copy material" )
	language.Add( "tool.submaterial.help", "Select material here, type known material string or use HUD to copy materials" )
end
TOOL.ClientConVar[ "override" ] = "debug/env_cubemap_model"
TOOL.ClientConVar[ "index" ] = 0

--
-- Duplicator function
--
local function SetSubMaterial( Player, Entity, Data )
	
	if ( SERVER ) then
		local Mats=Entity:GetMaterials()
		local MatCount=table.Count(Mats)
		for i=0,MatCount-1 do
			local si="SubMaterialOverride_"..tostring(i)
			-- Block exploitable material in multiplayer and remove empty strings
			if Data[si] and ((!game.SinglePlayer() && string.lower(Data[si]) == "pp/copy" ) or Data[si] == "" ) then 
				Data[si]=nil
			end
			Entity:SetSubMaterial( i, Data[si] or "")			
		end
		duplicator.ClearEntityModifier( Entity, "submaterial")
		if (table.Count(Data) > 0) then duplicator.StoreEntityModifier( Entity, "submaterial", Data ) end
	end

	return true

end
duplicator.RegisterEntityModifier( "submaterial", SetSubMaterial )

local function UpdateSubMat(Player, Entity, Index, Material)
	local Mats=Entity:GetMaterials()
	local MatCount=table.Count(Mats)
	if Index < 0 or Index >= MatCount then return end
	local Data={}
	for i=0,MatCount-1 do
		local mat=Entity:GetSubMaterial(i)
		if i==Index then mat=Material end
		if mat and mat ~= "" then Data["SubMaterialOverride_"..tostring(i)]=mat end	
	end
	return SetSubMaterial(Player, Entity, Data)		
end


-- Original set material funct
local function SetMaterial( Player, Entity, Data )
	
	if ( SERVER ) then

		--
		-- Make sure this is in the 'allowed' list in multiplayer - to stop people using exploits
		--
		--if ( !game.SinglePlayer() && !list.Contains( "OverrideMaterials", Data.MaterialOverride ) && Data.MaterialOverride != "" ) then return end
		if not Data.MaterialOverride or (Data.MaterialOverride and (!game.SinglePlayer() && string.lower(Data.MaterialOverride) == "pp/copy" )) then 
			return
		end
		Entity:SetMaterial( Data.MaterialOverride )
		duplicator.StoreEntityModifier( Entity, "material", Data )
	end

	return true

end
--and we will override it because original function eats most of materials even not exploitable! :(
duplicator.RegisterEntityModifier( "material", SetMaterial )


--
-- Left click applies the current material
--
function TOOL:LeftClick( trace )
	if ( !IsValid( trace.Entity ) ) then return end

	if ( CLIENT ) then return true end
	
	local ent = trace.Entity
	if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end

	local mat = self:GetClientInfo( "override" )
	local index = self:GetClientNumber( "index" , 0)
	if index < 1 then
		SetMaterial( self:GetOwner(), ent, { MaterialOverride = mat } )
	else
		UpdateSubMat( self:GetOwner(), ent, index-1, mat )
	end	
	return true

end

--
-- Right click reverts the material
--
function TOOL:RightClick( trace )
	
	if ( !IsValid( trace.Entity ) ) then return end

	if ( CLIENT ) then return true end
	
	local ent = trace.Entity
	if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end
	local index = self:GetClientNumber( "index" , 0)
	if index < 1 then
		SetMaterial( self:GetOwner(), ent, { MaterialOverride = "" } )
	else
		UpdateSubMat( self:GetOwner(), ent, index-1, "" )
	end
	return true

end


----- Damn Dirty fix... Thx for Wire Advanced tool developer
	
local function get_active_tool(ply, tool)
	-- find toolgun
	local activeWep = ply:GetActiveWeapon()
	if not IsValid(activeWep) or activeWep:GetClass() ~= "gmod_tool" or activeWep.Mode ~= tool then return end

	return activeWep:GetToolObject(tool)
end

if game.SinglePlayer() then -- wtfgarry (these functions don't get called clientside in single player so we need this hack to fix it)
	if SERVER then
		util.AddNetworkString( "submaterial_wtfgarry" )
		local function send( ply, funcname )
			net.Start( "submaterial_wtfgarry" )
				net.WriteString( funcname )
			net.Send( ply )
		end
		
		--function TOOL:LeftClick() send( self:GetOwner(), "LeftClick" ) end
		--function TOOL:RightClick() send( self:GetOwner(), "RightClick" ) end
		function TOOL:Reload() send( self:GetOwner(), "Reload" ) end
	elseif CLIENT then
		net.Receive( "submaterial_wtfgarry", function( len )
			local funcname = net.ReadString()
			local tool = get_active_tool( LocalPlayer(), "submaterial" )
			if not tool then return end
			tool[funcname]( tool, LocalPlayer():GetEyeTrace() )
		end)
	end
end
-----------------------------
if CLIENT then


	TOOL.AimEnt = nil
	TOOL.HudData = {}
	TOOL.SelIndx = 1
	TOOL.ToolMatString = ""

	function TOOL:Reload( trace )
		
		if ( !IsValid( trace.Entity ) ) then return end
	
		--if ( CLIENT ) then return true end
	
			local ent = trace.Entity
			if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end
			
			--local index = self:GetClientNumber( "index" , 0)
			local mat=self.HudData.EntCurMatString--""

			if !mat or mat ~= "" then
				RunConsoleCommand("submaterial_override",mat)
			end
			--LocalPlayer():ChatPrint("Material ".. (((self.SelIndx < 1) and "[Global]") or tostring(self.SelIndx)).." copied: "..mat)
			--else LocalPlayer():ChatPrint("Empty material!") end
		--end
		return true
	
	end
	


	function TOOL:Scroll(trace,dir)
		if !IsValid(self.AimEnt) then return end
		local Mats=self.AimEnt:GetMaterials()
		local MatCount=table.Count(Mats)
		self.SelIndx = self.SelIndx + dir
		if(self.SelIndx<0) then self.SelIndx = MatCount end
		if(self.SelIndx>MatCount) then self.SelIndx = 0 end
		RunConsoleCommand("submaterial_index",tostring(self.SelIndx))
		return true
		--self.HudData.EntCurMat=Material(self.AimEnt:GetMaterials()[self.SelIndx])
		
	end
	function TOOL:ScrollUp(trace) return self:Scroll(trace,-1) end
	function TOOL:ScrollDown(trace) return self:Scroll(trace,1) end


	
---- Thx wire_adv dev again...
	local function hookfunc( ply, bind, pressed )
		if not pressed then return end
		if bind == "invnext" then
			local self = get_active_tool(ply, "submaterial")
			if not self then return end
			
			return self:ScrollDown(ply:GetEyeTraceNoCursor())
		elseif bind == "invprev" then
			local self = get_active_tool(ply, "submaterial")
			if not self then return end

			return self:ScrollUp(ply:GetEyeTraceNoCursor())
		end
	end
	
	if game.SinglePlayer() then -- wtfgarry (have to have a delay in single player or the hook won't get added)
		timer.Simple(5,function() hook.Add( "PlayerBindPress", "submat_tool_playerbindpress", hookfunc ) end)
	else
		hook.Add( "PlayerBindPress", "submat_tool_playerbindpress", hookfunc )
	end
--------------------------------------------------


	local function FixVertexLitMaterial(Mat)
		
		--
		-- If it's a vertexlitgeneric material we need to change it to be
		-- UnlitGeneric so it doesn't go dark when we enter a dark room
		-- and flicker all about
		--
		if not Mat then return Mat end
		local strImage = Mat:GetName()
		
		if ( string.find( Mat:GetShader(), "VertexLitGeneric" ) || string.find( Mat:GetShader(), "Cable" ) ) then
		
			local t = Mat:GetString( "$basetexture" )
			
			if ( t ) then
			
				local params = {}
				params[ "$basetexture" ] = t
				params[ "$vertexcolor" ] = 1
				params[ "$vertexalpha" ] = 1
				
				Mat = CreateMaterial( strImage .. "_hud_fx", "UnlitGeneric", params )
			
			end
			
		end
		
		return Mat
		
	end	

	function TOOL:Think( )
		local ent=LocalPlayer():GetEyeTraceNoCursor().Entity
		if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end
		if self.AimEnt ~= ent then
			
			self.AimEnt=ent
			if IsValid(self.AimEnt) then
				self.SelIndx=0
				RunConsoleCommand("submaterial_index",tostring(self.SelIndx))
				self.HudData.Mats=self.AimEnt:GetMaterials()

			end
			--print("ThinkUpdate "..tostring(self.AimEnt))
		end

			if IsValid(self.AimEnt) then
				self.HudData.CurMats=table.Copy(self.HudData.Mats)
				self.HudData.OvrMats={}
	
				local MatCount=table.Count(self.HudData.Mats)
				for i=1,MatCount do
					local mat=self.AimEnt:GetSubMaterial(i-1)
					if mat and mat ~= "" then self.HudData.OvrMats[i]=mat end	
				end
				table.Merge(self.HudData.CurMats,self.HudData.OvrMats)
				self.HudData.GlobalMat=self.AimEnt:GetMaterial()
				local EntCurMatString=self.HudData.GlobalMat
				local EntOrigMatString=self.HudData.GlobalMat
				if self.SelIndx > 0 then EntCurMatString=self.HudData.CurMats[self.SelIndx]; EntOrigMatString=self.HudData.Mats[self.SelIndx] end 
				if self.HudData.EntCurMatString~=EntCurMatString then
					self.HudData.EntCurMatString=EntCurMatString
					self.HudData.EntCurMat=FixVertexLitMaterial(Material(EntCurMatString)) 
				end
				if self.HudData.EntOrigMatString~=EntOrigMatString then
					self.HudData.EntOrigMatString=EntOrigMatString
					self.HudData.EntOrigMat=FixVertexLitMaterial(Material(EntOrigMatString)) 
				end
			end
		
		if IsValid(self.AimEnt) and self.ToolMatString~=GetConVarString("submaterial_override") then
			self.ToolMatString=GetConVarString("submaterial_override")
 			self.HudData.ToolMat=FixVertexLitMaterial(Material(self.ToolMatString))
		end
		
	end
	function TOOL:DrawHUD( )
		if IsValid(self.AimEnt) then

			---- List
			local Rg=ScrW()/2-50
			local MaxW = 0
			local TextH = 0
			surface.SetFont("ChatFont")
			local Hdr=tostring(self.AimEnt)..": "..tostring(table.Count(self.HudData.Mats)).." materials"
			MaxW,TextH=surface.GetTextSize(Hdr)
			local HdrH = TextH+5
			for _,s in pairs(self.HudData.CurMats) do
				local ts,_=surface.GetTextSize(s)
				if MaxW<ts then MaxW=ts end
			end
			local LH=4*2+HdrH+TextH*(1+table.Count(self.HudData.Mats))
			local LW=4*2+MaxW
			local LL=Rg-LW
			local LT=ScrH()/2-LH/2
			surface.SetDrawColor(Color(64,64,95,191))
			--surface.SetMaterial(self.HudData.EntCurMat)
			surface.DrawRect(LL, LT, LW, LH)
			surface.SetTextColor(Color(255,255,255,255))
			surface.SetTextPos(LL+4,LT+4)
			surface.DrawText(Hdr)
			surface.SetDrawColor(Color(255,255,255,255))
			surface.DrawLine(LL+3,LT+4+TextH+3,Rg-3,LT+4+TextH+3)

			surface.SetDrawColor(Color(0,127,0,191))
			surface.DrawRect(LL+3, LT+4+HdrH+TextH*self.SelIndx, LW-3-3, TextH)
			
			local s="<none>"
			if not self.HudData.GlobalMat or self.HudData.GlobalMat == "" then 
				surface.SetTextColor(Color(255,255,255,255)) 
			else surface.SetTextColor(Color(0,0,255,255)); s=self.HudData.GlobalMat end
		
			surface.SetTextPos(LL+4,LT+4+HdrH)
			surface.DrawText(s)



			for i,s in pairs(self.HudData.CurMats) do
				if self.HudData.OvrMats[i] then surface.SetTextColor(Color(255,0,0,255)) else surface.SetTextColor(Color(255,255,255,255)) end
				surface.SetTextPos(LL+4,LT+4+HdrH+TextH*i)
				surface.DrawText(s)
			end
			---- Info box
			
			
			--local MaxW = 0
			local StrToolInfo = "Tool material:"
			local StrOrigMatInfo = "Model original material:"
			local StrCurMatInfo = "Model current material:"
			local MaxW,_=surface.GetTextSize(StrToolInfo)
			local ts,_=surface.GetTextSize(StrOrigMatInfo)
			if MaxW<ts then MaxW=ts end
			local ts,_=surface.GetTextSize(StrCurMatInfo)
			if MaxW<ts then MaxW=ts end
			local ts,_=surface.GetTextSize(self.ToolMatString)
			if MaxW<ts then MaxW=ts end
			local ts,_=surface.GetTextSize(self.HudData.EntOrigMatString)
			if MaxW<ts then MaxW=ts end
			local ts,_=surface.GetTextSize(self.HudData.EntCurMatString)
			if MaxW<ts then MaxW=ts end
		
			local IL=ScrW()/2+50
			local IH=4*4+(64)*3
			local IT=ScrH()/2-IH/2
			surface.SetDrawColor(Color(64,64,95,191))
			surface.DrawRect(IL, IT, 76+MaxW, IH)    -- 4+64+4+MaxW+4

			surface.SetTextColor(Color(255,255,255,255))

			surface.SetDrawColor(Color(255,255,255,255))
			if self.HudData.ToolMat  then
				surface.SetMaterial(self.HudData.ToolMat)
				surface.DrawTexturedRect(IL+4, IT+4, 64, 64)
			end
			surface.SetTextPos(IL+4+64+4,IT+8)
			surface.DrawText(StrToolInfo)
			surface.SetTextPos(IL+4+64+4,IT+8+TextH)
			surface.DrawText(self.ToolMatString)
			surface.SetTextPos(IL+4+64+4,IT+8+TextH*2)
			surface.DrawText(self.SelIndx==0 and "[Global]" or "Index: "..self.SelIndx-1)
	
						

			if self.HudData.EntOrigMat  then
				surface.SetMaterial(self.HudData.EntOrigMat)	
				surface.DrawTexturedRect(IL+4, IT+4+(64+4), 64, 64)
			end
			surface.SetTextPos(IL+4+64+4,IT+8+64+4)
			surface.DrawText(StrOrigMatInfo)
			surface.SetTextPos(IL+4+64+4,IT+8+64+4+TextH)
			surface.DrawText(self.HudData.EntOrigMatString)

			if self.HudData.EntCurMat  then
				surface.SetMaterial(self.HudData.EntCurMat)
				surface.DrawTexturedRect(IL+4, IT+4+(64+4)*2, 64, 64)
			end
			surface.SetTextPos(IL+4+64+4,IT+8+(64+4)*2)
			surface.DrawText(StrCurMatInfo)
			surface.SetTextPos(IL+4+64+4,IT+8+(64+4)*2+TextH)
			surface.DrawText(self.HudData.EntCurMatString)


--			surface.SetMaterial(nil)
			

			--draw.RoundedBox( 2, ScrW()/2-50, ScrH()/2-50, 100, 100, Color(255,255,255,255) ) 
			
		--	print("DrawHUD "..tostring(self.AimEnt))
		end
	end


end

function TOOL.BuildCPanel( CPanel )

	--CPanel:AddControl( "Slider", { Label = "Index", Command = "submaterial_index", Type = "Integer", Min = 0, Max = 15} )
	CPanel:AddControl( "Header", { Description = "#tool.submaterial.help" } )

	CPanel:AddControl( "TextBox", { Label = "Mat:", Command = "submaterial_override", MaxLength = "48"} )
	CPanel:MatSelect( "submaterial_override", list.Get( "OverrideMaterials" ), true, 64, 64 )

end
