
local ActiveVideo = nil

VideoSettings = nil

local stats = {
	reset = 0,
	encodetime = 0,

	starttime = 0,
	last_encodetime = 0
}

concommand.Add( "gm_demo_to_video", function( ply, cmd, args )

	local demoname = args[ 1 ]
	if ( !demoname ) then return end

	local settings = {
		name		= "filled_in_later",
		container	= "webm",
		video		= "vp8",
		audio		= "vorbis",
		bitrate		= 25000,
		quality		= 1,
		width		= 640,
		height		= 480,
		fps			= 25,
		frameblend	= 1,
		fbshutter	= 0.5,
		dofsteps	= 0,
		dofpasses	= 0,
		doffocusspeed	= 1.0,
		dofsize		= 1.0,
		viewsmooth	= 0.0,
		possmooth	= 0.0
	}

	local Window = vgui.Create( "DFrame" )
	Window:SetTitle( "Render Video" )
	Window:SetSize( 600, 400 )
	Window:LoadGWENFile( "resource/ui/DemoToVideo.gwen" )
	Window:Center()
	Window:MakePopup()

	local inCodec		= Window:Find( "inCodec" )
	local inQuality		= Window:Find( "inQuality" )
	local inSize		= Window:Find( "inSize" )
	local btnStart		= Window:Find( "btnStart" )
	local inBitRate		= Window:Find( "inBitRate" )
	local inFPS			= Window:Find( "inFPS" )
	local inFrameBlend	= Window:Find( "inFrameBlend" )
	local inFBShutter	= Window:Find( "inFBShutter" )
	local inDOF			= Window:Find( "inDepthOfField" )
	local inDOFSpeed	= Window:Find( "inDOFFocusSpeed" )
	local inDOFSize		= Window:Find( "inDOFBlurSize" )
	local inViewSmooth	= Window:Find( "inViewSmooth" )
	local inPosSmooth	= Window:Find( "inPosSmooth" )

	inFPS.OnChange = function() settings.fps = inFPS:GetInt() end
	inFPS:SetText( settings.fps )

	inBitRate.OnChange = function() settings.bitrate = inBitRate:GetInt() end
	inBitRate:SetText( settings.bitrate )

	-- TODO!!!
	inCodec.OnSelect = function( _, index, value, data ) settings.container = data[1]; settings.video = data[2]; settings.audio = data[3] end
	inCodec:AddChoice( "webm", { "webm", "vp8", "vorbis" }, true )
	inCodec:AddChoice( "ogg", { "ogg", "theora", "vorbis" } )

	inQuality.OnSelect = function( _, index, value, data ) settings.quality = data end
	inQuality:AddChoice( "0.0 - Low (but fast)", 0 )
	inQuality:AddChoice( "0.5 - Medium", 0.5 )
	inQuality:AddChoice( "1.0 - Highest (but slow)", 1, true )

	-- TODO!!!
	inSize.OnSelect = function( _, index, value, data ) settings.width = data[1] settings.height = data[2] end
	local sw, sh = ScrW(), ScrH()
	inSize:AddChoice( sw .. " x " .. sh .. " (highest)", { sw, sh }, true )

	sw, sh = sw * 0.66666, sh * 0.66666
	inSize:AddChoice( math.ceil( sw ) .. " x " .. math.ceil( sh ), { sw, sh }, true )

	sw, sh = sw * 0.5, sh * 0.5
	inSize:AddChoice( math.ceil( sw ) .. " x " .. math.ceil( sh ), { sw, sh }, true )

	inFrameBlend.OnSelect = function( _, index, value, data ) settings.frameblend = data end
	inFrameBlend:AddChoice( "Off", 1, true )
	inFrameBlend:AddChoice( "Draft (8 Samples)", 8 )
	inFrameBlend:AddChoice( "Good (16 Samples)", 16 )
	inFrameBlend:AddChoice( "Great (32 Samples)", 32 )
	inFrameBlend:AddChoice( "Overkill (64 Samples)", 64 )
	inFrameBlend:AddChoice( "OverOverKill (128 Samples)", 128 )

	inFBShutter.OnSelect = function( _, index, value, data ) settings.fbshutter = data end
	inFBShutter:AddChoice( "90", 0.75 )
	inFBShutter:AddChoice( "180", 0.5, true )
	inFBShutter:AddChoice( "240", 0.25 )
	inFBShutter:AddChoice( "360", 0.0 )

	--
	-- DOF
	--
	inDOF.OnSelect = function( _, index, value, data ) settings.dofsteps = data[1]; settings.dofpasses = data[2] end
	inDOF:AddChoice( "Off",	{ 0, 0 }, true )
	inDOF:AddChoice( "Draft (21 Samples)", { 6, 3 } )
	inDOF:AddChoice( "Good (72 Samples)", { 12, 6 } )
	inDOF:AddChoice( "Best (288 Samples)", { 24, 12 } )

	inDOFSpeed.OnChange = function() settings.doffocusspeed = inDOFSpeed:GetFloat() end
	inDOFSpeed:SetText( "1.0" )

	inDOFSize.OnChange = function() settings.dofsize = inDOFSize:GetFloat() end
	inDOFSize:SetText( "1.0" )

	--
	-- Smoothing
	--
	inViewSmooth.OnSelect = function( _, index, value, data ) settings.viewsmooth = data end
	inViewSmooth:AddChoice( "Off",			0.0, true )
	inViewSmooth:AddChoice( "Minimal",		0.2 )
	inViewSmooth:AddChoice( "Low",			0.4 )
	inViewSmooth:AddChoice( "Medium",		0.7 )
	inViewSmooth:AddChoice( "High",			0.8 )
	inViewSmooth:AddChoice( "Lots",			0.9 )
	inViewSmooth:AddChoice( "Too Smooth",	0.97 )

	inPosSmooth.OnSelect = function( _, index, value, data ) settings.possmooth = data end
	inPosSmooth:AddChoice( "Off",			0.0, true )
	inPosSmooth:AddChoice( "Minimal",		0.2 )
	inPosSmooth:AddChoice( "Low",			0.4 )
	inPosSmooth:AddChoice( "Medium",		0.7 )
	inPosSmooth:AddChoice( "High",			0.8 )
	inPosSmooth:AddChoice( "Lots",			0.9 )
	inPosSmooth:AddChoice( "Too Smooth",	0.97 )

	btnStart.DoClick = function()

		-- Fill in the name here, or we'll be overwriting the same video!
		local cleanname = string.GetFileFromFilename( demoname )
		cleanname = cleanname:Replace( ".", "_" )
		cleanname = cleanname .. " " .. util.DateStamp()
		settings.name = cleanname

		PrintTable( settings )
		ActiveVideo, error = video.Record( settings )

		if ( !ActiveVideo ) then
			Derma_Message( "Couldn't record video: \n" .. error, "Make Video Error", "OK" )
			return
		end

		RunConsoleCommand( "sv_cheats",			1 )
		RunConsoleCommand( "host_framerate",	settings.fps * settings.frameblend )
		RunConsoleCommand( "snd_fixed_rate",	1 )
		RunConsoleCommand( "progress_enable",	1 )
		RunConsoleCommand( "playdemo",			demoname )

		VideoSettings = table.Copy( settings )

		Window:Remove()

	end

end, nil, "", { FCVAR_DONTRECORD } )

local function FinishRecording()

	VideoSettings = nil
	ActiveVideo:Finish()
	ActiveVideo = nil

	RunConsoleCommand( "host_framerate", 0 )
	RunConsoleCommand( "sv_cheats", 0 )
	RunConsoleCommand( "snd_fixed_rate", 0 )

	MsgN( "Rendering Finished - Took ", SysTime() - stats.starttime, " seconds" )

end



local function UpdateFrame()

	if ( !engine.IsPlayingDemo() ) then

		if ( !VideoSettings.started ) then return end
		FinishRecording()
		return

	end

	if ( !VideoSettings.started ) then

		if ( gui.IsGameUIVisible() ) then return end

		VideoSettings.started = true
		VideoSettings.framecount = 0
		stats.starttime = SysTime()

	end

end


local function DrawOverlay()

	if ( !VideoSettings ) then return end

	local complete = engine.GetDemoPlaybackTick() / engine.GetDemoPlaybackTotalTicks()

	local x = ScrW() * 0.1
	local y = ScrH() * 0.8
	local w = ScrW() * 0.8
	local h = ScrH() * 0.05

	surface.SetFont( "DermaDefault" )
	surface.SetTextColor( 255, 255, 255, 255 )

	-- Static text
	local info = "Rendering " .. math.floor( VideoSettings.width ) .. "x" .. math.floor( VideoSettings.height ) .. " at " .. math.floor( VideoSettings.fps ) .. "fps "

	local with = {}
	if ( VideoSettings.dofsteps > 0 ) then table.insert( with, "DOF" ) end
	if ( VideoSettings.frameblend > 1 ) then table.insert( with, "Frame Blending" ) end
	if ( VideoSettings.viewsmooth > 0 ) then table.insert( with, "View Smoothing" ) end
	if ( VideoSettings.possmooth > 0 ) then table.insert( with, "Position Smoothing" ) end

	if ( #with > 0 ) then
		local withS = table.concat( with, ", " )
		info = info .. "with " .. withS
	end

	info = info .. " (rendering " .. ( VideoSettings.frameblend * math.max( VideoSettings.dofsteps, 1 ) * math.max( VideoSettings.dofpasses, 1 ) ) .. " frames per frame)"

	local tw, th = surface.GetTextSize( info )
	surface.SetTextPos( x, y - th - 10 )
	surface.DrawText( info )

	-- The box
	surface.SetDrawColor( 0, 0, 0, 50 )
	surface.DrawRect( x-3, y-3, w + 6, h + 6 )

	surface.SetDrawColor( 255, 255, 255, 200 )
	surface.DrawRect( x -2, y-2, w + 4, 2 )
	surface.DrawRect( x - 2, y, 2, h )
	surface.DrawRect( x + w, y, 2, h )
	surface.DrawRect( x-2, y + h, w + 4, 2 )

	surface.SetDrawColor( 255, 255, 100, 150 )
	surface.DrawRect( x + 1, y + 1, w * complete - 2, h - 2 )

	-- Demo length
	local demolength = "Demo Length: " .. string.FormattedTime( engine.GetDemoPlaybackTotalTicks() * engine.TickInterval(), "%2i:%02i" )
	local tw, th = surface.GetTextSize( demolength )
	surface.SetTextPos( x + w - tw, y - th - 10 )
	surface.DrawText( demolength )

	if ( !VideoSettings.started ) then return end

	-- Timers
	surface.SetTextPos( x, y + h + 10 )
	surface.DrawText( "Time Taken: " .. string.NiceTime( SysTime() - stats.starttime ) )

	local tw, th = surface.GetTextSize( "Time Left: " .. string.NiceTime( stats.timeremaining ) )
	surface.SetTextPos( x + w - tw, y + h + 10 )
	surface.DrawText( "Time Left: " .. string.NiceTime( stats.timeremaining ) )


	local demotime = string.FormattedTime( engine.GetDemoPlaybackTick() * engine.TickInterval(), "%2i:%02i" )
	local tw, th = surface.GetTextSize( demotime )
	if ( w * complete > tw + 20 ) then
		surface.SetTextColor( 0, 0, 0, 200 )
		surface.SetTextPos( x + w * complete - tw - 10, y + h * 0.5 - th * 0.5 )
		surface.DrawText( demotime )
	end

	local demotime = string.FormattedTime( ( engine.GetDemoPlaybackTotalTicks() - engine.GetDemoPlaybackTick() ) * engine.TickInterval(), "%2i:%02i" )
	local tw, th = surface.GetTextSize( demotime )
	if ( w - w * complete > tw + 20 ) then
		surface.SetTextColor( 255, 255, 255, 200 )
		surface.SetTextPos( x + w * complete + 10, y + h * 0.5 - th * 0.5 )
		surface.DrawText( demotime )
	end

end

hook.Add( "CaptureVideo", "CaptureDemoFrames", function()

	if ( !ActiveVideo ) then return end
	if ( !VideoSettings ) then return end

	UpdateFrame()

	DrawOverlay()

	if ( stats.reset < SysTime() ) then

		stats.reset = SysTime() + 1

		stats.last_encodetime = stats.encodetime
		stats.encodetime = 0

		local timetaken = SysTime() - stats.starttime
		local fractioncomplete = engine.GetDemoPlaybackTotalTicks() / engine.GetDemoPlaybackTick()

		stats.timeremaining = ( timetaken * fractioncomplete ) - timetaken
		if ( stats.timeremaining < 0 ) then stats.timeremaining = 0 end

	end

end )

function RecordDemoFrame()

	if ( !ActiveVideo ) then return end
	if ( !VideoSettings ) then return end
	if ( !VideoSettings.started ) then return end

	ActiveVideo:AddFrame( 1 / VideoSettings.fps, true )
	VideoSettings.framecount = VideoSettings.framecount + 1

end
