import java.text.SimpleDateFormat

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

/**
 * Content URL extractor plugin for BBC iPlayer (UK only) 
 *  
 * @author Petr Nejedly
 *
 */
class IPlayer extends FeedItemUrlExtractor {
    
    final VALID_FEED_URL = '^http(s)*://feeds.bbc.co.uk/iplayer/.*$'
    final SUPPORTED_APPLICATIONS = ['iplayertok','a1414/e3','a5999/e1']
	
    String getExtractorName() {
        return 'BBC iPlayer (UK only)'
    }
    
    boolean extractorMatches(URL feedUrl) {
        return feedUrl ==~ VALID_FEED_URL
    }
    
    public int getVersion() {
        return 2;
    }
    
    ContentURLContainer extractUrl(Map links, PreferredQuality requestedQuality) {
        def linkUrl = links.alternate
        def contentUrl
        def thumbnailUrl
		def live = false
		
		def matcher = linkUrl =~ '^.+/episode/(.+)/.+$'
		assert matcher != null
		assert matcher.hasGroup()

		def videoId = matcher[0][1]

		// get episode details XML
		if( links.self != null ) {
			def episodeDetailsNode = new XmlParser().parse( links.self.toString() )
			def linkNode = episodeDetailsNode.entry[0].link.find { it -> it.'@rel' == 'alternate'}
			if( linkNode != null ) {
				def selectedThumbNode
				linkNode.'media:content'[0].'media:thumbnail'.each { it -> if( selectedThumbNode == null || (it.'@width'.toInteger() > selectedThumbNode.'@width'.toInteger() && it.'@width'.toInteger() <= 160) ) selectedThumbNode = it }
				thumbnailUrl = selectedThumbNode?.'@url'
			}
		}
		
		// get playlist XML document
		String playlistUrl = "http://www.bbc.co.uk/iplayer/playlist/$videoId"		
		def playlistNode = new XmlParser().parse( playlistUrl )
		def playableItem = playlistNode.item.find { it -> it.'@duration' != null }		
		assert playableItem != null
		
		// get media selection XML document
		String identifier = playableItem.mediator[0].'@identifier'
		Integer randomToken = NumberUtils.getRandomInInterval(100000, 999999)
		String mediaSelectorUrl = "http://www.bbc.co.uk/mediaselector/4/mtis/stream/$identifier?cb=$randomToken"
		String mediaSelectorDoc = new URL(mediaSelectorUrl).getText()
		//log(mediaSelectorDoc)
		def mediaSelectionNode = new XmlParser().parseText( mediaSelectorDoc )
		// get all media items that are either video or audio and have a supported connection sub-element
		List mediaItems = mediaSelectionNode.media.findAll { it -> (it.'@kind' == 'video' || it.'@kind' == 'audio') && it.connection.any { item -> SUPPORTED_APPLICATIONS.contains(item.'@application')} }
		
		if(mediaItems.size() > 0) {
			// sort media items by bitrate, lowest first and get an item
			List sortedItems = mediaItems.sort { it.'@bitrate'.toInteger() }
			Node selectedMediaItem = findSuitableItem(sortedItems, requestedQuality)
			if(selectedMediaItem.'@live' == 'true') {
				live = true
			}
			// find first suitable connection element
			Node connectionNode = selectedMediaItem.find{ it -> SUPPORTED_APPLICATIONS.contains(it.'@application') }
			if( connectionNode != null ) {
				// build the url
				if( connectionNode.'@kind' == 'limelight') {
					contentUrl = "rtmp://${connectionNode.'@server'}:1935/ app=${connectionNode.'@application'}?${connectionNode.'@authString'} tcUrl=rtmp://${connectionNode.'@server'}:1935/${connectionNode.'@application'}?${connectionNode.'@authString'} playpath=${connectionNode.'@identifier'} swfUrl=http://www.bbc.co.uk/emp/releases/iplayer/revisions/617463_618125_4/617463_618125_4_emp.swf swfVfy=1"
				} else {
					contentUrl = "rtmp://${connectionNode.'@server'}:1935/${connectionNode.'@application'}?${connectionNode.'@authString'} playpath=${connectionNode.'@identifier'} swfUrl=http://www.bbc.co.uk/emp/releases/iplayer/revisions/617463_618125_4/617463_618125_4_emp.swf swfVfy=1"
				}
				
				MediaFileType fileType = MediaFileType.VIDEO;
				if( selectedMediaItem.'@type'.startsWith('audio')) {
					fileType = MediaFileType.AUDIO;
				}
				Date authExpiryDate = getAuthExpiresGMTDate(connectionNode.'@authExpires')
				String cacheKey = connectionNode.'@identifier'
				return new ContentURLContainer(fileType: fileType, contentUrl: contentUrl, thumbnailUrl: thumbnailUrl, expiresOn: authExpiryDate, cacheKey: cacheKey, live: live)
			}
		} 
		log('Could not find content URL')
		return null;
    }
    
	def Node findSuitableItem(List items, PreferredQuality requestedQuality) {
		if(requestedQuality == PreferredQuality.LOW || items.size() <= 1) {
			// worst quality, get the first from the list
			return items.head()
		} else if (requestedQuality == PreferredQuality.MEDIUM) {
			// get item from the middle
			return items.get(Math.round(items.size()/2).toInteger())
		} else {
			// best quality, take the last url
			return items.last()
		}
	}
	
	def Date getAuthExpiresGMTDate(String dateString) {
		// expiry is in format 2011-07-12T20:17:37+00:00 , let's assume it's always GMT
		SimpleDateFormat df = new SimpleDateFormat('yyyy-MM-dd\'T\'HH:mm:ss')
		df.setTimeZone(TimeZone.getTimeZone("GMT"))
		return df.parse(dateString)		
	}
	
    static void main(args) {
		// this is just to test
        IPlayer extractor = new IPlayer()
		
		assert extractor.extractorMatches( new URL("http://feeds.bbc.co.uk/iplayer/categories/drama/tv/list") )
		assert !extractor.extractorMatches( new URL("http://google.com/feeds/api/standardfeeds/top_rated?time=today") )
		
        Map videoLinks = ['alternate': new URL('http://www.bbc.co.uk/iplayer/episode/b02xd9tc/EastEnders_Omnibus_09_06_2013/'),
			'self' : new URL('http://feeds.bbc.co.uk/iplayer/episode/b02xd9tc')] 
        ContentURLContainer result = extractor.extractUrl(videoLinks, PreferredQuality.LOW)		
        println "Result: $result"
		 
//		Map audioLinks = ['alternate': new URL('http://www.bbc.co.uk/iplayer/episode/b012mjtc/Jarvis_Cockers_Sunday_Service_17_07_2011/'),
//			'self' : new URL('http://feeds.bbc.co.uk/iplayer/episode/b012mjtc')]
//		ContentURLContainer aResult = extractor.extractUrl(audioLinks, PreferredQuality.MEDIUM)
//		println "Result: $aResult"
    }
}