local base = _G
module('youtubecore')

CSignature = {}
------------------------------------------------------------------------------------------------------------------
function unescape_html(str)
  str = base.string.gsub( str, '&lt;', '<' )
  str = base.string.gsub( str, '&gt;', '>' )
  str = base.string.gsub( str, '&quot;', '"' )
  str = base.string.gsub( str, '&apos;', "'" )
  str = base.string.gsub( str, '&#(%d+);', function(n) return base.string.char(n) end )
  str = base.string.gsub( str, '&#x(%d+);', function(n) return base.string.char(base.tonumber(n,16)) end )
  str = base.string.gsub( str, '&amp;', '&' ) -- Be sure to do this after all others
  return str
end
------------------------------------------------------------------------------------------------------------------
function GetWatchAddress(inAdr, multistream)
	local session = base.m_simpleTV.WinInet.New("Opera/9.80 (Windows NT 6.1; Win64; x64) Presto/2.12.388 Version/12.17")
	if session == nil then return nil end
	
	if base.string.match(inAdr, 'https://') then
		base.m_simpleTV.WinInet.SetOpenRequestFlags(session, 0x00800000 + 0x00002000 + 0x00001000)
	end
	
	local rc, answer = base.m_simpleTV.WinInet.Get(session, inAdr)
	base.m_simpleTV.WinInet.Close(session)
	if rc ~= 200 then return end
	
	--base.debug_in_file(answer .. '\n')
	--do return end
	
	local header =  base.findpattern(answer,'<title>(.-)</title>', 1, 7, 8)
	if header == nil then header = inAdr end
	header = unescape_html(header)
	
	answer = base.m_simpleTV.Common.string_fromUTF8(answer)
	--base.debug_in_file(answer .. '\n')
	--do return end
	
	local retAdr = base.findpattern(answer, 'url_encoded_fmt_stream_map(.-)%b"",', 1, 28, 0)
	if retAdr == nil then
		base.m_simpleTV.OSD.ShowMessage('Youtube error (url_encoded_fmt_stream_map)', 255, 5)
		return nil
	end
	local streams = base.split(retAdr, ',')
	if streams == nil then
		base.m_simpleTV.OSD.ShowMessage('Youtube error (streams)', 255, 5)
		return nil
	end
	local t, count = {}, 1
	local paramarray
	for i, w in base.pairs(streams) do
		--base.debug_in_file( w .. '\n\n\n\n') 
		retAdr, paramarray = GetSingleAddress(w, answer)
		if retAdr ~= nil then 
			t[count]    = {}
			t[count].Id = count
			local ss = GetParamFromArray(paramarray, 'quality')
			local ss1 = GetParamFromArray(paramarray, 'type')
			if ss == nil then
				ss = ss1
			else
				if ss1 ~= nil then ss = ss .. '  [' .. ss1  .. ']' end
			end
			t[count].Name   = ss or 'Unknown'
			t[count].Adress = retAdr 
			
			if base.m_simpleTV.Common.GetVersion()>=600 then
			   t[count].Adress = t[count].Adress  .. '$OPT:NO-STIMESHIFT'
			end
			
			--base.debug_in_file( 'id=' .. t[count].Id  .. '  Name=' .. t[count].Name .. '   Address=' .. t[count].Adress .. '\n') 
			count = count+1
		end
	end
	if t[1] == nil then return nil end
	if multistream == nil then return t[1].Adress, header end
	return t, header
end
------------------------------------------------------------------------------------------------------------------
function trim(str)
	if str == nil then return '' end
	str = base.string.match(str,'^%s*(.-)%s*$')
	return str
end
------------------------------------------------------------------------------------------------------------------
function GetSignature(answer, swf)
	--base.debug_in_file(answer .. '\n')
	
	local Sign, f
	local path = base.m_simpleTV.MainScriptDir .. 'user\\video\\core\\'
	local sts = base.string.match(answer, '"sts":(%d+)')
	if sts == nil then 
		sts = base.string.match(answer, '"sts": (%d+)}')
	end
		
	local tmpPath = base.m_simpleTV.Common.GetTmpName()
	base.os.remove (tmpPath)
	tmpPath = base.string.match(tmpPath, '(.+)\\(.-)$')
	
	--base.debug_in_file(answer .. '\n', 'C:\\_TEMP\\debug.txt', true)
	--base.debug_in_file(answer .. '\n', tmpPath .. '\\debug.txt', true)	
	if sts == nil then		
		base.debug_in_file(answer .. '\n', tmpPath .. '\\debug.txt', true)
	else
		f = base.io.open(path .. 'timestamps.txt' , "r+")
	end  
	local new = true
	if f ~= nil then
		f:seek("set", 0)
		local lines = f:read("*a")
		base.io.close(f)
		if base.string.match(lines, sts .. '=') then
			new = false
			if Sign == nil then Sign = {} end
			local L = base.string.match(lines, sts .. '=(.-)$')
			if base.string.len(trim(L)) > 0 then
				local i = 1
				for param in base.string.gmatch(L, '(.-),') do
					Sign[i] = base.tonumber(param)
					i = i + 1
				end
			else
				lines = base.string.gsub(lines, sts .. '=' .. L .. '$', '')
				base.debug_in_file(trim(lines) .. '\n', path .. 'timestamps.txt', true)
				new = true
			end
		end
	end
	if new == true then
		local url, file
		if swf == true then
			url = base.string.match(answer, '"url": "(.-)watch_as3%.swf"')
			if url == nil then return end			
			file = 'watch_as3.swf'
			url = url .. file
		else
			--{"js":"\/\/s.ytimg.com\/yts\/jsbin\/html5player-ru_RU-vfloCHeOG\/html5player.js"
			url = base.findpattern(answer, '"js":%b""',1,6,1)
			if url == nil then return end
			url = 'http:' .. url
			file = 'html5player.js'
		end
				
		url = base.string.gsub(url, '\\', '')
		--base.debug_in_file(url .. '\n')
		
		local tmpName = base.m_simpleTV.Common.DownloadFileInTmp(url, file, false)
		--base.debug_in_file(tmpName .. '\n', 'C:\\_TEMP\\debug.txt', true)
		--base.m_simpleTV.Common.Wait(500)
		--SWF---------------------------------------------------------------------------------------------------------
		if swf == true then
			base.os.execute('cmd /c start /min %SYSTEMROOT%\\System32\\java.exe -jar "' .. path .. 'ffdec.jar" -export script "' .. tmpPath .. '\\decompiled" ' .. tmpName .. ' -selectas3class com.google.youtube.util.SignatureDecipher')
			for i = 1, 10 do
				f = base.io.open(tmpPath .. '\\decompiled\\com\\google\\youtube\\util\\SignatureDecipher.as' , "r")
				if f ~= nil then break end
				base.m_simpleTV.Common.Wait(500)
			end
			if f ~= nil then
				f:seek("set", 0)
				local lines = f:read("*a")
				local i = 1
				if Sign == nil then Sign = {} end
				for param in base.string.gmatch(lines, ' = this%.(.-);') do
					local func, p = base.string.match(param, '(.-)%(param1,(%d+)%)')      
					local _, id = base.string.match(lines, 'private function ' .. func .. '(.-)param1%.(.-)%(')      
					if id ~= nil then
						if id == 'slice' then p = '-' .. p end   
						if id == 'reverse' then p = 0 end
					end
					Sign[i] = base.tonumber(p)
					i = i + 1
				end
				base.io.close(f)
			end
			base.os.remove(tmpPath .. '\\decompiled\\com\\google\\youtube\\util\\SignatureDecipher.as')
		else
		--JS----------------------------------------------------------------------------------------------------------
			for i = 1, 10 do
				f = base.io.open(tmpPath .. '\\' .. file , "r")
				if f ~= nil then break end
				base.m_simpleTV.Common.Wait(500)
			end
			if f ~= nil then
				f:seek("set", 0)
				local lines = f:read("*a")
				local i = 1
				--*************************************************************************************************************--
				--base.debug_in_file(lines)
				local obj = base.string.match(lines, '%(a%){a=a%.split%(""%);(..).-return a%.join%(""%)};')
				if obj == nil then return end
				base.debug_in_file('xxxxx\n\n\n\n' .. obj .. '\n\n', path .. 'debug.txt', true)
				--local lines = base.string.match(lines, '%("yt%.mdx%.remote%.channelParams_"%)||{}}var (.-)return a%.join%(""%)')
				--base.debug_in_file('xxxxx\n\n\n\n' .. lines .. '\n\n', path .. 'debug.txt', true)
				local l = base.string.match(lines, 'var ' .. obj .. '={(.-)return a%.join%(""%)}')
				if l == nil then
					l = base.string.match(lines, 'var %$q=function.-return a%.join%(""%)')
				end	
				--local l = base.string.match(lines, ';function ..%(a%){return%((.+)')
				base.debug_in_file('yyyyy\n\n\n\n' .. l .. '\n\n', path .. 'debug.txt')
				--do return end
				--;NF.hQ(a,70)
				for param in base.string.gmatch(l, ';' .. obj .. '%.(.-)%)') do
				--for param in base.string.gmatch(l, ';..%.(.-)%)') do
					base.debug_in_file(param .. '\n', path .. 'debug.txt')
					local func, p = base.string.match(param, '^(..)%(a,(%d+)')
					func = base.string.match(lines, func .. ':function(.-)}')
					--base.debug_in_file(func .. '\n', path .. 'debug.txt')
					if base.string.find(func, 'a%.reverse') then p = 0 end
					if base.string.find(func, 'a%.splice') then p = '-' .. p end
					if Sign == nil then Sign = {} end
					Sign[i] = base.tonumber(p)
					i = i + 1
				end
				--*************************************************************************************************************--
				base.io.close(f)
			end
		end --swf
		base.os.remove(tmpPath .. '\\' .. file)
		if (Sign ~= nil) and (Sign[1] ~= nil) then
			base.debug_in_file(sts .. '=', path .. 'timestamps.txt')
			for i = 1, #(Sign) do
				base.debug_in_file(Sign[i] .. ',', path .. 'timestamps.txt')
			end
			base.debug_in_file('\n', path .. 'timestamps.txt')
		end
	end --new
	return Sign
end
------------------------------------------------------------------------------------------------------------------
function GetSingleAddress(retAdr, answer)
	--unescape this
	retAdr = base.unescape(retAdr)
	retAdr = base.unescape(retAdr)
	--retAdr =  base.unescape (retAdr)
	retAdr = base.string.gsub(retAdr, '\\u0026', '%&')
	if base.string.match(retAdr, 'rtmpe') then
		base.m_simpleTV.OSD.ShowMessage('Youtube: sorry, rtmpe not supported now\nstopping', 0x00FFFF, 5)
		return nil
	end
	if retAdr == nil then
		base.m_simpleTV.OSD.ShowMessage('Youtube error 2', 255, 5)
		return nil
	end
	--base.debug_in_file(retAdr .. '\n\n\n')
	local t = BuildParamArray(retAdr)
	if t == nil then return end
	local url = GetParamFromArray(t, 'url')
	local key = GetParamFromArray(t, 'key')
	local sig = GetParamFromArray(t, 'sig') or GetParamFromArray(t, 'signature')
	if sig == nil then
		sig = GetParamFromArray(t, 's')
		--base.debug_in_file('sig=' .. sig .. '\n\n')
		--if sig ~= nil then  sig =  CSignature:decipher(sig,answer) end
		local Sign = GetSignature(answer, false)
		if Sign == nil then
			base.m_simpleTV.OSD.ShowMessage('Youtube error - cant find param', 255, 5)
			return nil
		end
		if sig ~= nil then sig = CSignature:Decode(sig, Sign) end
	end
	if url == nil or key == nil or sig == nil then
		base.m_simpleTV.OSD.ShowMessage('Youtube error1 - cant find param', 255, 5)
		return nil
	end
	local sparams = GetParamFromArray(t, 'sparams')
	--base.debug_in_file( sparams .. '\n')
	local fparams = '&'
	local str
	for param in base.string.gmatch(sparams, '%w+') do
		str = GetParamFromArray(t, param)
		if str ~= nil then fparams = fparams .. param .. '=' .. str .. '&' end
	end
	retAdr = url .. '?sparams=' .. sparams .. fparams .. 'signature=' .. sig .. '&key=' .. key
	--base.debug_in_file( retAdr .. '\n')
	retAdr = base.string.gsub(retAdr, ',', '%%2C')
	--base.debug_in_file( retAdr .. '\n')
	--base.m_simpleTV.OSD.ShowMessage(retAdr,255,5)
	return retAdr,t 
end
------------------------------------------------------------------------------------------------------------------
function BuildParamArray(str)
	str = str:gsub('%?', '%&')
	str = str:gsub(',$', '')
	str = str:gsub("^%s*(.-)%s*$", "%1")
	str = str:gsub('^"', '')
	local t = base.split(str, '&')
	return t
end
------------------------------------------------------------------------------------------------------------------
function GetParamFromArray(t, Name)
	local n = '^' .. Name .. '=(.+)'
	for i, w in base.pairs(t) do
		local s = base.findpattern(w, n, 1, Name:len() + 1, 0)
		if s ~= nil then return s end
	end
	return nil
end
------------------------------------------------------------------------------------------------------------------
function CSignature:Decode(s, inst)
	local t = base.split(s)
	if t == nil then return s end
	-- + swap, - slice, 0 reverse
	for i = 1, #inst do 
		local a = inst[i]
		if inst[i] == 0 then
			t = CSignature:reverse(t)
		else
			if a > 0 then
				t = CSignature:swap(t, a)
			else
				t = CSignature:clone(t, base.math.abs(a))
			end
		end
	end
	return base.table_join(t)
end
------------------------------------------------------------------------------------------------------------------
function CSignature:reverse(t)
	return base.table_reverse(t)
end
------------------------------------------------------------------------------------------------------------------
function CSignature:swap(t, param2)
	local loc_3 = t[1]
	local loc_4 = t[(param2 % #t) + 1]
	t[1] = loc_4
	t[param2 + 1] = loc_3;
	return t
end
------------------------------------------------------------------------------------------------------------------
function CSignature:clone(t, param2) 
	return base.table_slice(t, param2 + 1)
end
------------------------------------------------------------------------------------------------------------------