import org.serviio.library.metadata.*
import org.serviio.library.online.*

/**
 * SC2Cast.com RSS URL extractor plugin. 
 * 
 * Based on youtube_dl Python script (http://rg3.github.com/youtube-dl/)
 * Based on YoutubeUrlExctractor class by Petr Nejedly
 *  
 * @author Mehran "Miki" Barahmand
 *
 */
class SC2CastUrlExctractor extends FeedItemUrlExtractor {

    final VALID_PLAYER_URL = '(?m).*"((?:https?://)(?:youtu\\.be/|(?:\\w+\\.)?youtube(?:-nocookie)?\\.com/)(?:(?:(?:v|embed|e)/)|(?:(?:watch(?:_popup)?(?:\\.php)?)?(?:\\?|#!?)(?:[^"]+&)?v=)))([0-9A-Za-z_-]+)([^"]+)?".*'
    final VALID_FEEDLINK_URL = '^http(s)*://.*sc2casts\\..*$'
	final VALID_FEED_URL = '^http(s)*://.*(barahmand|mini).*/sc2casts.*$'
	/* Listed in order of quality */
    final availableFormats = ['38', '37', '22', '45', '35', '34', '43', '18', '6', '5', '17', '13']
    
    String getExtractorName() {
        return getClass().getName()
    }
    
    boolean extractorMatches(URL feedUrl) {
        return feedUrl ==~ VALID_FEED_URL
    }
    
    ContentURLContainer extractUrl(Map links, PreferredQuality requestedQuality) {
        def linkUrl = links.default
        def contentUrl
        def thumbnailUrl
		def expiryDate
		
		log("linkUrl: $linkUrl")
		def matchcast = linkUrl =~ VALID_FEEDLINK_URL
		assert matchcast != null
		
		def castWebPage = linkUrl.getText()
				
        def matcher = castWebPage =~ VALID_PLAYER_URL
        assert matcher != null
        assert matcher.hasGroup()

        def videoId = matcher[0][2]
		def videoIa = matcher[0][0]
		def videoIb = matcher[0][1]
		log("videoIa: $videoIa")
		log("videoIb: $videoIb")
		log("videoId: $videoId")

        for (elType in ['&el=embedded', '&el=detailpage', '&el=vevo', '']) {           
            def videoInfoUrl = "http://www.youtube.com/get_video_info?&video_id=$videoId$elType&ps=default&eurl=&gl=US&hl=en"
            log("Loading video info: $videoInfoUrl")
            def videoInfoWebPage = new URL(videoInfoUrl).getText()
            def parameters = [:]
        
            videoInfoWebPage.split('&').each{item -> addParameter(item, parameters, '=')}
            if(parameters.containsKey('token')) {
				
                def formatUrlMapString = parameters['fmt_url_map']    
				def urlEncodedUrlMapString = parameters['url_encoded_fmt_stream_map']
				def connString = parameters['conn']
				
				if (connString?.length() > 0 && connString.startsWith('rtmp')) {
					contentUrl =  URLDecoder.decode(connString, 'UTF-8');
				} else {
					def allFormatUrlMap = [:]
					if (formatUrlMapString != null && formatUrlMapString.length() > 0 ) {
						URLDecoder.decode(formatUrlMapString,'UTF-8').split(',').each{item -> addParameter(item, allFormatUrlMap, '\\|')}						
					} else if (urlEncodedUrlMapString != null && urlEncodedUrlMapString.length() > 0 ) { 					
						URLDecoder.decode(urlEncodedUrlMapString,'UTF-8').split(',').each{item -> 
							def streamParams = [:]
							item.split('&').each{item2 -> addParameter(item2, streamParams, '=')}
							allFormatUrlMap.put(streamParams['itag'], URLDecoder.decode(streamParams['url'],'UTF-8'))
						}
					}
				   // get available formats for requested quality, sorted by quality from highest
				   def formatUrlMap = new LinkedHashMap()
				   if(requestedQuality == PreferredQuality.HIGH) {
					   // best quality, get the first from the list
					   sortAvailableFormatUrls(availableFormats, allFormatUrlMap, formatUrlMap)
					   contentUrl = formatUrlMap.values().toList().head()
				   } else if (requestedQuality == PreferredQuality.MEDIUM) {
					   // work with subset of available formats, starting at the middle of the list and then take the best quality from there
					   sortAvailableFormatUrls(availableFormats.getAt(5..availableFormats.size-1), allFormatUrlMap, formatUrlMap)
					  contentUrl = formatUrlMap.values().toList().head()
				   } else {
					   // worst quality, take the last url
					   sortAvailableFormatUrls(availableFormats, allFormatUrlMap, formatUrlMap)
					   contentUrl = formatUrlMap.values().toList().last()
				   }
				   				  
				   if(contentUrl != null) {
					   	def contentUrlParameters = [:]
					   	contentUrl.split('&').each{item -> addParameter(item, contentUrlParameters, '=')}
						if( contentUrlParameters['expire'] != null ) {   
							
							(Long.parseLong(contentUrlParameters['expire']).toString())
							expiryDate = new Date(Long.parseLong(contentUrlParameters['expire'])*1000)
						}
				   }
				}
                
                thumbnailUrl = parameters['thumbnail_url'] != null ? URLDecoder.decode(parameters['thumbnail_url']) : null
                break
            }
        }    
        return new ContentURLContainer(fileType: MediaFileType.VIDEO, contentUrl: contentUrl, thumbnailUrl: thumbnailUrl, expiresOn: expiryDate)
    }
 
    def addParameter(parameterString, parameters, separator) {
        def values = parameterString.split(separator)
        if( values.length == 2 ) {    
             parameters.put(values[0], values[1])
        }
    }   
	
	def sortAvailableFormatUrls(List formatIds, Map sourceMap, Map targetMap) {
		formatIds.each{formatId ->
			if(sourceMap.containsKey(formatId) ) {				
				 targetMap.put(formatId, sourceMap.get(formatId))
			}
		}
	}
    
    static void main(args) {
		// this is just to test
        SC2CastUrlExctractor extractor = new SC2CastUrlExctractor()
		
		assert extractor.extractorMatches( new URL("https://gdata.youtube.com/feeds/api/standardfeeds/top_rated?time=today") )
		assert extractor.extractorMatches( new URL("http://gdata.youtube.com/feeds/api/standardfeeds/top_rated?time=today") )
		assert !extractor.extractorMatches( new URL("http://google.com/feeds/api/standardfeeds/top_rated?time=today") )
		
        Map links = ['alternate': new URL('http://www.youtube.com/watch?v=ICaRjUMYr4M&amp;feature=youtube_gdata')] 
        ContentURLContainer result = extractor.extractUrl(links, PreferredQuality.MEDIUM)
        println "Result: $result"
    }
}