
local Window 			= nil
local Status 			= "Preview"
local Size	 			= 0.5
local Distance 			= 256
local sldDistance   	= nil
local lblDistance 		= nil
local lblSize 			= nil
local FocusGrabber  	= false
local ScreenshotTimer	= 0

local texFSB = render.GetSuperFPTex()
local matFSB = Material( "pp/motionblur" )
local matFB	 = Material( "pp/fb" )

local function RenderDoF( vOrigin, vAngle, vFocus, fAngleSize, radial_steps, passes, drawhud )

	local OldRT 	= render.GetRenderTarget();
	local view 		= {  x = 0, y = 0, w = ScrW(), h = ScrH(), drawhud = drawhud }
	local fDistance = vOrigin:Distance( vFocus )
	
	fAngleSize = fAngleSize * math.Clamp( 256/fDistance, 0.1, 1 ) * 0.5
	
	view.origin = vOrigin
	view.angles = vAngle
	
	// Straight render (to act as a canvas)
	//render.Clear( 0, 0, 0, 255, true )
	render.RenderView( view )
	render.UpdateScreenEffectTexture()
	
	render.SetRenderTarget( texFSB )
			matFB:SetMaterialFloat( "$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
			
			// Rotate around the focus point
			VA:RotateAroundAxis( vAngle:Right(), 	math.sin( i + (mul) ) * fAngleSize * mul )
			VA:RotateAroundAxis( vAngle:Up(), 		math.cos( i + (mul) ) * fAngleSize * mul )
			
			ViewOrigin = vFocus - VA:Forward() * fDistance
			
			view.origin = ViewOrigin
			view.angles = VA
			
			// Render to the front buffer
			render.SetRenderTarget( OldRT )
			render.Clear( 0, 0, 0, 255, 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:SetMaterialFloat( "$alpha", alpha  )
			

				render.SetMaterial( matFB )
				render.DrawScreenQuad()

		
		end
		
	end
	
	// Restore RT
	render.SetRenderTarget( OldRT )
	
	// Render our result buffer to the screen
	matFSB:SetMaterialFloat( "$alpha", 1 )
	matFSB:SetMaterialTexture( "$basetexture", texFSB )
		
	render.SetMaterial( matFSB )
	render.DrawScreenQuad()

end

function RenderSuperDoF( ViewOrigin, ViewAngles )

	if ( FocusGrabber ) then
	
		tr = util.TraceLine( util.GetPlayerTrace( LocalPlayer(), LocalPlayer():GetCursorAimVector() ) )
		Distance = tr.HitPos:Distance( ViewOrigin )
		Status = "Preview"
		lblDistance:PostMessage( "SetText", "text", string.format( "%.2f", Distance ) )
		sldDistance:PostMessage( "SetValue", "f", Distance )
	
	end

	local FocusPoint = ViewOrigin + ViewAngles:Forward() * Distance
	
	if ( Status == "Preview" ) then
		
		// A low quality, pretty quickly drawn rough outline
		RenderDoF( ViewOrigin, ViewAngles, FocusPoint, Size, 2, 3, 1 )
		
	elseif ( Status == "Render" ) then
		
		// A great quality render..
		RenderDoF( ViewOrigin, ViewAngles, FocusPoint, Size, 16, 8, 1 )
		Status = "ViewShot"
	
	elseif ( Status == "QuickRender" ) then
		
		// A great quality render..
		RenderDoF( ViewOrigin, ViewAngles, FocusPoint, Size, 16, 4, 1 )
		Status = "ViewShot"
		
	elseif ( Status == "ViewShot" ) then
		
		matFSB:SetMaterialFloat( "$alpha", 1 )
		matFSB:SetMaterialTexture( "$basetexture", texFSB )
		render.SetMaterial( matFSB )
		render.DrawScreenQuad()
		
	end
	
end

local function DrawInternal( ViewOrigin, ViewAngles )

	if ( ScreenshotTimer < CurTime() && (!Window || !Window:IsVisible()) ) then return end
	
	// Don't render it when the console is up
	if ( FrameTime() == 0 ) then return end
	
	RenderSuperDoF( ViewOrigin, ViewAngles );
	return true;

end


hook.Add( "RenderScene", "RenderSuperDoF", DrawInternal )






local function Render( panel, message, param1, param2 )

	if (message != "Command") then return end
	Status = "Render"

end

local function QuickRender( panel, message, param1, param2 )

	if (message != "Command") then return end
	Status = "QuickRender"

end


local function Preview( panel, message, param1, param2 )

	if (message != "Command") then return end
	Status = "Preview"

end

local function Screenshot( panel, message, param1, param2 )

	if (message != "Command") then return end
	
	ScreenshotTimer = CurTime() + 0.2
	Window:SetVisible( false )
	
	timer.Simple( 0.05, LocalPlayer().ConCommand, LocalPlayer(), "jpeg\n" )
	timer.Simple( 0.1, Window.SetVisible, Window, true )

end

local function ChangeDistance( panel, message, param1, param2 )

	if (message != "SliderMoved") then return end
	Distance = param1
	lblDistance:PostMessage( "SetText", "text", string.format( "%.2f", Distance ) )

end

local function ChangeBlurSize( panel, message, param1, param2 )

	if (message != "SliderMoved") then return end
	Size = param1
	lblSize:PostMessage( "SetText", "text", string.format( "%.2f", Size ) )

end


local function OpenWindow()

	Status = "Preview"

	if ( Window ) then Window:SetVisible( true ) return end

	Window = vgui.Create( "Frame" )
	Window:SetName( "Super Depth Of Field" )		
	
	// RENDER!
	local button = vgui.Create( "Button", Window, "Render" )
	button:SetActionFunction( Render )
	
	// QUICKRENDER!
	local button = vgui.Create( "Button", Window, "QuickRender" )
	button:SetActionFunction( QuickRender )
	
	// QUICKRENDER!
	local button = vgui.Create( "Button", Window, "Preview" )
	button:SetActionFunction( Preview )
	
	// QUICKRENDER!
	local button = vgui.Create( "Button", Window, "Screenshot" )
	button:SetActionFunction( Screenshot )
	
	sldDistance = vgui.Create( "Slider", Window, "Distance" )
	sldDistance:PostMessage( "SetInteger", "b", "0" )
	sldDistance:PostMessage( "SetLower", "f", "0" )
	sldDistance:PostMessage( "SetHigher", "f", "1024" )
	sldDistance:PostMessage( "SetValue", "f", Distance )
	sldDistance:SetActionFunction( ChangeDistance )
	
	local slider = vgui.Create( "Slider", Window, "BlurSize" )
	slider:PostMessage( "SetInteger", "b", "0" )
	slider:PostMessage( "SetLower", "f", "0" )
	slider:PostMessage( "SetHigher", "f", "5" )
	slider:PostMessage( "SetValue", "f", Size )
	slider:SetActionFunction( ChangeBlurSize )
	
	
	lblDistance = vgui.Create( "Label", Window, "FocusLbl" )
	lblSize 	= vgui.Create( "Label", Window, "BlurSizeLbl" )
	lblDistance:PostMessage( "SetText", "text", Distance )
	lblSize:PostMessage( "SetText", "text", Size )
	
	Window:LoadControlsFromFile( "resource/ui/SuperDoF.res" )
	Window:SetKeyBoardInputEnabled( false ) // Note: Enable keyboard to be able to use the vgui builder
	Window:SetMouseInputEnabled( true )
	
	Window:SetPos( (ScrW() - 400) / 2, ScrH() - 200 )
	Window:SetVisible( true )

end

concommand.Add( "pp_superdof", OpenWindow )



/*---------------------------------------------------------
   Mouse button down
---------------------------------------------------------*/   
local function MouseDown( mouse )

	if ( !Window || !Window:IsVisible() ) then return end

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

end


/*---------------------------------------------------------
   Mouse button released
---------------------------------------------------------*/   
local function MouseUp( mouse )

	if ( !Window || !Window:IsVisible() ) then return end
	
	vgui.GetWorldPanel():MouseCapture( false )
	FocusGrabber = false
	
end

hook.Add( "GUIMousePressed", "SuperDOFMouseDown", MouseDown )
hook.Add( "GUIMouseReleased", "SuperDOFMouseUp", MouseUp )


/*
// Control Panel
*/
local function BuildControlPanel( CPanel )

	CPanel:AddControl( "Header", { Text = "#Super DoF", Description = "" }  )
	CPanel:AddControl(  "Label", { Text = Localize( "SuperDoFWarning", "Warning: This is VERY experimental so it might not totally\n work on your graphics card.\n\nThis effect is not realtime. You render it and then save a \nscreenshot of your render. Also, you will have low \nfps when previewing - that's normal.") } )
	CPanel:AddControl(  "Button", { Text = "#Open", Command = "pp_superdof", Label = Localize( "SuperDoFWindow", "Open Super DoF Window:") } )	
	
end

/*
// Tool Menu
*/
local function AddPostProcessMenu()

	spawnmenu.AddToolMenuOption( "PostProcessing", "PPShader", "SuperDoF", "#Super DoF", "", "", BuildControlPanel )

end

hook.Add( "PopulateToolMenu", "AddPostProcessMenu_SuperDoF", AddPostProcessMenu )
