
PANEL = {}

local Distance 			= 256
local BlurSize			= 0.5
local Passes			= 12
local Steps				= 24
local Shape				= 0.5

local SuperDOFWindow	= nil
local Status 			= "Preview"

local sldDistance   	= nil
local lblDistance 		= nil
local lblSize 			= nil
local FocusGrabber  	= false
local ScreenshotTimer	= 0

function PANEL:Init()

	self:SetTitle( "#superdof_pp.title" )
	self:SetRenderInScreenshots( false )

	local Panel = vgui.Create( "DPanel", self )
	
	local lbl = Label( "#superdof_pp.settings", Panel )
	lbl:SetContentAlignment( 8 )
	lbl:Dock( TOP )
	lbl:SetDark( true )
	
	self.BlurSize = vgui.Create( "DNumSlider", Panel )
		self.BlurSize:SetMin( 0 )
		self.BlurSize:SetMax( 10 )
		self.BlurSize:SetDecimals( 3 )
		self.BlurSize:SetText( "#superdof_pp.size" )
		self.BlurSize:SetValue( BlurSize )
		function self.BlurSize:OnValueChanged( val ) BlurSize = val end
		self.BlurSize:Dock( TOP )
		self.BlurSize:DockMargin( 0, 0, 0, 16 )
		self.BlurSize:SetDark( true )
	
	self.Distance = vgui.Create( "DNumSlider", Panel )
		self.Distance:SetMin( 0 )
		self.Distance:SetMax( 4096 )
		self.Distance:SetText( "#superdof_pp.distance" )
		self.Distance:SetValue( Distance )
		function self.Distance:OnValueChanged( val ) Distance = val end
		self.Distance:Dock( TOP )
		self.Distance:SetDark( true )
		self.Distance:SetToolTip( "#superdof_pp.distance.tooltip" )
			
	Panel:SetPos( 10, 30 )
	Panel:SetSize( 300, 90 )
	Panel:DockPadding( 8, 8, 8, 8 )
	Panel:DockMargin( 0, 0, 4, 0 )
	Panel:Dock( FILL )
	
	local Panel = vgui.Create( "DPanel", self )
	
	local lbl = Label( "#superdof_pp.adv", Panel )
	lbl:SetContentAlignment( 8 )
	lbl:Dock( TOP )
	lbl:SetDark( true )
	
	local PassesCtrl = vgui.Create( "DNumSlider", Panel )
		PassesCtrl:SetMin( 1 )
		PassesCtrl:SetMax( 64 )
		PassesCtrl:SetDecimals( 0 )
		PassesCtrl:SetText( "#superdof_pp.passes" )
		PassesCtrl:SetValue( Passes )
		function PassesCtrl:OnValueChanged( val ) Passes = val end
		PassesCtrl:Dock( TOP )
		PassesCtrl:DockMargin( 0, 0, 0, 4 )
		PassesCtrl:SetDark( true )
	
	local RadialsCtrl = vgui.Create( "DNumSlider", Panel )
		RadialsCtrl:SetMin( 1 )
		RadialsCtrl:SetMax( 64 )
		RadialsCtrl:SetDecimals( 0 )
		RadialsCtrl:SetText( "#superdof_pp.radials" )
		RadialsCtrl:SetValue( Steps )
		function RadialsCtrl:OnValueChanged( val ) Steps = val end
		RadialsCtrl:Dock( TOP )
		RadialsCtrl:DockMargin( 0, 0, 0, 4 )
		RadialsCtrl:SetDark( true )
	
	local ShapeCtrl = vgui.Create( "DNumSlider", Panel )
		ShapeCtrl:SetMin( 0 )
		ShapeCtrl:SetMax( 1 )
		ShapeCtrl:SetDecimals( 3 )
		ShapeCtrl:SetText( "#superdof_pp.shape" )
		ShapeCtrl:SetValue( Shape )
		function ShapeCtrl:OnValueChanged( val ) Shape = val end
		ShapeCtrl:Dock( TOP )
		ShapeCtrl:DockMargin( 0, 0, 0, 4 )
		ShapeCtrl:SetDark( true )
		
	Panel:SetPos( 10, 30 )
	Panel:SetSize( 150, 100 )
	Panel:DockPadding( 8, 8, 8, 8 )
	Panel:Dock( RIGHT )

	local Panel = vgui.Create( "DPanel", self )
			
	self.Render = vgui.Create( "DButton", Panel )
		self.Render:SetText( "#superdof_pp.render" )
		function self.Render:DoClick() Status = "Render" end
		self.Render:Dock( RIGHT ) self.Render:SetSize( 70, 20 )
		
	self.Screenshot = vgui.Create( "DButton", Panel )
		self.Screenshot:SetText( "#superdof_pp.screenshot" )
		function self.Screenshot:DoClick() RunConsoleCommand( "jpeg" ) end
		self.Screenshot:Dock( RIGHT )
		self.Screenshot:SetSize( 120, 20 )
		self.Screenshot:DockMargin( 0, 0, 8, 0 )
		
	local Break = vgui.Create( "DButton", Panel )
		Break:SetText( "5" )
		local THIS = self
		function Break:DoClick() 
			THIS:SetVisible( false )
			timer.Simple( 5, function() THIS:SetVisible( true ) end )
		end
		Break:Dock( LEFT )
		Break:SetSize( 20, 20 )
		Break:SetTooltip( "#superdof_pp.5hint" )

	Panel:Dock( BOTTOM )
	Panel:DockPadding( 4, 4, 4, 4 )
	Panel:DockMargin( 0, 4, 0, 0 )
	Panel:SetTall( 28 );
	Panel:MoveToBack()
		
	self:SetSize( 600, 220 )
		
end

function PANEL:ChangeDistanceTo( dist )

	self.Distance:SetValue( dist )

end

local paneltypeSuperDOF = vgui.RegisterTable( PANEL, "DFrame" )
local texFSB = render.GetSuperFPTex()
local matFSB = Material( "pp/motionblur" )
local matFB	 = Material( "pp/fb" )

function RenderDoF( vOrigin, vAngle, vFocus, fAngleSize, radial_steps, passes, bSpin, inView, ViewFOV )

	local OldRT 	= render.GetRenderTarget();
	local view 		= { x = 0, y = 0, w = ScrW(), h = ScrH() }
	local fDistance = vOrigin:Distance( vFocus )
	
	fAngleSize = fAngleSize * math.Clamp( 256 / fDistance, 0.1, 1 ) * 0.5
	
	local view = inView

	if ( !view ) then

		view = { 
			x = 0, 
			y = 0, 
			w = ScrW(), 
			h = ScrH(),
			dopostprocess = true,
			origin = vOrigin,
			angles = vAngle,
			fov = ViewFOV
		}

	end

	-- Straight render (to act as a canvas)
	render.RenderView( view )
	
	render.UpdateScreenEffectTexture()
	
	render.SetRenderTarget( texFSB )
			render.Clear( 0, 0, 0, 255, true, true )
			matFB:SetFloat( "$alpha", 1  )
			render.SetMaterial( matFB )
			render.DrawScreenQuad()	
	
	
	local Radials = ( math.pi * 2 ) / radial_steps
	
	for mul=( 1 / passes), 1, ( 1 / passes ) do
	
		for i=0,( math.pi * 2 ), Radials do
		
			local VA = vAngle * 1 -- hack - this makes it copy the angles instead of the reference
			local VRot = vAngle * 1
			-- Rotate around the focus point
			VA:RotateAroundAxis( VRot:Right(), 	math.sin( i + ( mul ) ) * fAngleSize * mul * ( Shape ) * 2 )
			VA:RotateAroundAxis( VRot:Up(), 	math.cos( i + ( mul ) ) * fAngleSize * mul * ( 1 - Shape ) * 2 )

			view.origin = vFocus - VA:Forward() * fDistance
			view.angles = VA
			
			-- Render to the front buffer
			render.SetRenderTarget( OldRT )
			render.Clear( 0, 0, 0, 255, true, true )
			render.RenderView( view )
			render.UpdateScreenEffectTexture()
			
			-- Copy it to our floating point buffer at a reduced alpha
			render.SetRenderTarget( texFSB )
			local alpha = ( Radials / ( math.pi * 2 ) ) 	-- Divide alpha by number of radials
			alpha = alpha * ( 1 - mul )						-- Reduce alpha the further away from center we are
			matFB:SetFloat( "$alpha", alpha  )
			
				render.SetMaterial( matFB )
				render.DrawScreenQuad()

			-- We have to SPIN here to stop the Source engine running out of render queue space.
			if ( bSpin ) then
			
				-- Restore RT
				render.SetRenderTarget( OldRT )
	
				-- Render our result buffer to the screen
				matFSB:SetFloat( "$alpha", 1 )
				matFSB:SetTexture( "$basetexture", texFSB )
		
				render.SetMaterial( matFSB )
				render.DrawScreenQuad()
				
				cam.Start2D()
					local add = ( i / ( math.pi*2 ) ) * ( 1 / passes )
					local percent = string.format( "%.1f", ( mul - ( 1 / passes ) + add ) * 100 )
					draw.DrawText( percent .. "%", "GModWorldtip", view.w - 100, view.h - 100, Color( 0, 0, 0, 255 ), TEXT_ALIGN_CENTER )
					draw.DrawText( percent .. "%", "GModWorldtip", view.w - 101, view.h - 101, Color( 255, 255, 255, 255 ), TEXT_ALIGN_CENTER )
				cam.End2D()
			
				render.Spin()
				
			end
		
		end
		
	end

	-- Restore RT
	render.SetRenderTarget( OldRT )
	render.Clear( 0, 255, 0, 255, true, true )
	
	-- Render our result buffer to the screen
	matFSB:SetFloat( "$alpha", 1 )
	matFSB:SetTexture( "$basetexture", texFSB )
		
	render.SetMaterial( matFSB )
	render.DrawScreenQuad()

end

function RenderSuperDoF( ViewOrigin, ViewAngles, ViewFOV )

	if ( FocusGrabber ) then

		local x, y = gui.MousePos()
		local dir = util.AimVector( ViewAngles, ViewFOV, x, y, ScrW(), ScrH() )

		local tr = util.TraceLine( util.GetPlayerTrace( LocalPlayer(), dir ) )
		Distance = tr.HitPos:Distance( ViewOrigin )
		Status = "Preview"

		-- debugoverlay.Cross( tr.HitPos, 10 )
	
		SuperDOFWindow:ChangeDistanceTo( Distance )

		local effectdata = EffectData()
		effectdata:SetOrigin( tr.HitPos )
		effectdata:SetNormal( tr.HitNormal )
		effectdata:SetMagnitude( 1 )
		effectdata:SetScale( 1 )
		effectdata:SetRadius( 16 )

		util.Effect( "Sparks", effectdata )
	
	end

	local FocusPoint = ViewOrigin + ViewAngles:Forward() * Distance
	
	if ( Status == "Preview" ) then
		
		-- A low quality, pretty quickly drawn rough outline
		RenderDoF( ViewOrigin, ViewAngles, FocusPoint, BlurSize, 2, 2, false, nil, ViewFOV )
		
	elseif ( Status == "Render" ) then
		
		-- A great quality render..
		RenderDoF( ViewOrigin, ViewAngles, FocusPoint, BlurSize, Steps, Passes, true, nil, ViewFOV )
		Status = "ViewShot"
	
	elseif ( Status == "ViewShot" ) then
		
		matFSB:SetFloat( "$alpha", 1 )
		matFSB:SetTexture( "$basetexture", texFSB )
		render.SetMaterial( matFSB )
		render.DrawScreenQuad()
		
	end
	
end

local function RenderSceneHook( ViewOrigin, ViewAngles, ViewFOV )

	if ( !ValidPanel( SuperDOFWindow ) ) then return end
	
	-- Don't render it when the console is up
	if ( FrameTime() == 0 ) then return end
	
	RenderSuperDoF( ViewOrigin, ViewAngles, ViewFOV )
	return true;

end
hook.Add( "RenderScene", "RenderSuperDoF", RenderSceneHook )

local function OpenWindow()

	Status = "Preview"
	
	if ( ValidPanel( SuperDOFWindow ) ) then
		SuperDOFWindow:Remove()
	end
	
	SuperDOFWindow = vgui.CreateFromTable( paneltypeSuperDOF )
	
	SuperDOFWindow:InvalidateLayout( true )
	SuperDOFWindow:MakePopup()
	SuperDOFWindow:AlignBottom( 50 )
	SuperDOFWindow:CenterHorizontal()
	SuperDOFWindow:SetKeyboardInputEnabled( true )

end
concommand.Add( "pp_superdof", OpenWindow )

--[[---------------------------------------------------------
   Mouse button down
-----------------------------------------------------------]]   
local function MouseDown( mouse )

	if ( !ValidPanel( SuperDOFWindow ) ) then return end

	vgui.GetWorldPanel():MouseCapture( true )
	FocusGrabber = true

end
hook.Add( "GUIMousePressed", "SuperDOFMouseDown", MouseDown )

--[[---------------------------------------------------------
   Mouse button released
-----------------------------------------------------------]]   
local function MouseUp( mouse )

	if ( !ValidPanel( SuperDOFWindow ) ) then return end
	
	vgui.GetWorldPanel():MouseCapture( false )
	FocusGrabber = false
	
end
hook.Add( "GUIMouseReleased", "SuperDOFMouseUp", MouseUp )

list.Set( "PostProcess", "#superdof_pp", {

	icon		= "gui/postprocess/superdof.png",
	category	= "#effects_pp",
	onclick		= function() RunConsoleCommand( "pp_superdof" ) end
	
} )


--
-- We don't want people using weapons when they click on the screen
--
hook.Add( "PreventScreenClicks", "SuperDOFPreventClicks", function()

	if ( ValidPanel( SuperDOFWindow ) ) then return true end

end )
