--[[----------------------------------------------------------------------------

WFCatVideoTime.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2015 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

----------------------------------------------------------------------------]]--

	-- WF.core.lua
local WFRatio = require 'WFRatio'

	-- WF.core.utils
local WFArguments = require 'WFArguments'

local check = WFArguments.check
local kNTSCFrameRate = { 30000, 1001 }

--==============================================================================

local WFCatVideoTime = {
	kNTSCFrameRate = kNTSCFrameRate,
}

--------------------------------------------------------------------------------

function WFCatVideoTime.formatSmpteVideoTime( t, frameRate )

	check( 'number', '?table' )
	
	local m, s, f
	
	if frameRate == nil
			or t == nil
			or type( frameRate[ 1 ] ) ~= 'number'
			or type( frameRate[ 2 ] ) ~= 'number'
			or frameRate[ 2 ] == 0 then
		return '--:--:--'
	end
	
	frameRate = WFRatio.reduce( frameRate )
	local frameRateNum = frameRate[ 1 ] / frameRate[ 2 ]

	if frameRate[ 1 ] == kNTSCFrameRate[ 1 ] and frameRate[ 2 ] == kNTSCFrameRate[ 2 ] then

		-- the part above ten minutes is easy:
		local rounder = 0.5 / frameRateNum
		local ltTenMin = math.mod( t + rounder, 60 * 10 )
		local gtTenMin = t + rounder - ltTenMin
		
		local frame = math.floor( frameRateNum * ltTenMin )

		local skip = math.max( 0, frame - 2 )
		skip = math.floor( skip / 1798 )
		skip = skip * 2

		assert( skip < 20 )
		frame = frame + skip
		
		m = math.floor( frame / 30 / 60 )
		s = math.floor( ( math.mod( frame, 1800 ) ) / 30 )
		f = math.mod( frame, 30 )

		-- add back the tens of minutes
		m = m + gtTenMin / 60

		return string.format( "%02d;%02d;%02d", m, s, f )

	else
		-- simpler
		local frame = math.floor( frameRateNum * t + 0.5 )
		local frameTime = { frameRate[ 2 ], frameRate[ 1 ] }
		
		m = WFRatio.multiply( frameTime, { frame, 60 } )
		m = math.floor( m[ 1 ] / m[ 2 ] )
		local offset = WFRatio.multiply( frameRate, { m * 60, 1 } )
		offset = math.floor( offset[ 1 ] / offset[ 2 ] )
		frame = frame - offset
		s = WFRatio.multiply( frameTime, { frame, 1 } )
		s = math.floor( s[ 1 ] / s[ 2 ] )
		offset = WFRatio.multiply( frameRate, { s, 1 } )
		offset = math.floor( offset[ 1 ] / offset[ 2 ] )
		frame = frame - offset
		f = frame

	end
	
	return string.format( "%02d:%02d:%02d", m, s, f )

end

--------------------------------------------------------------------------------

function WFCatVideoTime.decodeUserDecimal( values, frameRate )
	local multiplier = 3600
	local n = 0
	local value = 0
	for _, num in ipairs( values ) do
		value = value + tonumber( num ) * multiplier
		multiplier = multiplier / 60
		n = n + 1
	end
	value = value * 60 ^ ( n - 3 )
	value = math.floor( value * frameRate[ 1 ] / frameRate[ 2 ] + 0.5 )
	value = value * frameRate[ 2 ] / frameRate[ 1 ]
	return value
end

--------------------------------------------------------------------------------

function WFCatVideoTime.decodeUserSmpte( values, frameRate )
	
	if frameRate[ 1 ] == kNTSCFrameRate[ 1 ] and frameRate[ 2 ] == kNTSCFrameRate[ 2 ] then
		
		local m, s, f
		m = 0
		s = 0
		f = values[ #values ]
		if #values >= 2 then
			s = values[ #values - 1 ]
		end
		if #values >= 3 then
			m = values[ #values - 2 ]
		end

		local decade = math.floor( m / 10 )
		local ltTenMin = m - decade * 10
		local shift = decade * 18
		shift = shift + ltTenMin * 2
		local frame = f + s * 30 + m * 60 * 30 - shift

		return frame * frameRate[ 2 ] / frameRate[ 1 ]
		
	else
		
		local t = WFRatio.multiply( { frameRate[ 2 ], frameRate[ 1 ] }, { values[ #values ], 1 } )
		if #values >= 2 then
			t = WFRatio.add( t, { values[ #values - 1 ], 1 } )
		end
		if #values >= 3 then
			t = WFRatio.add( t, { values[ #values - 2 ] * 60, 1 } )
		end
		if #values >= 4 then
			t = WFRatio.add( t, { values[ #values - 3 ] * 60 * 60, 1 } )
		end
		return t[ 1 ] / t[ 2 ]
		
	end

end

--------------------------------------------------------------------------------

function WFCatVideoTime.decodeUserVideoTime( userString, frameRate, expectedMode )
	
	check( '+string', '?table', '+string' )
	
	if not frameRate then
		return nil
	end
	
	assert( type( frameRate[ 1 ] ) == 'number', 'frameRate must be a ratio' )
	assert( type( frameRate[ 2 ] ) == 'number', 'frameRate must be a ratio' )
	assert( frameRate[ 2 ] > 0, 'frameRate must be a ratio' )
	
	local values = {}
	for num in string.gfind( userString, "[0-9.]+" ) do
		table.insert( values, tonumber( num ) )
	end
	
	if #values == 0 then
		return nil, 'not enough values'
	end
	
	local hasDots = string.find( userString, "[.]" )
	local hasSemi = string.find( userString, "[;]" )
	
	if expectedMode == 'smpte' and not hasDots and frameRate then
		return WFCatVideoTime.decodeUserSmpte( values, frameRate )
	elseif expectedMode ~= 'smpte' and not hasSemi then
		return WFCatVideoTime.decodeUserDecimal( values, frameRate )
	elseif hasDots and not hasSemi then
		return WFCatVideoTime.decodeUserDecimal( values, frameRate )
	elseif hasDots and not hasSemi and frameRate then
		return WFCatVideoTime.decodeUserSmpte( values, frameRate )
	else
		return nil, 'confusing input'
			-- meh
	end

end

--==============================================================================

return WFCatVideoTime
