import groovy.json.JsonSlurper

import org.serviio.library.metadata.*
import org.serviio.library.online.*
import org.serviio.util.*

/**
 * WebResource extractor plugin for tunein.com.
 * 
 * @author Petr Nejedly
 *
 */
class TuneIn extends WebResourceUrlExtractor {
	
	final VALID_FEED_URL = '^(?:https?://)?(?:www\\.)?tunein\\.com/(radio/|search/\\?query=).+'
				
	String getExtractorName() {
		return 'Tune In'
	}
	
	boolean extractorMatches(URL feedUrl) {
		return feedUrl ==~ VALID_FEED_URL
	}
	
	int getVersion() {
		4
	}

	WebResourceContainer extractItems(URL resourceUrl, int maxItems) {
		String html = resourceUrl.getText()
		
		def titleMatcher = html =~ '(?s)<h1.*?>(.+?)<'
		String pageTitle = titleMatcher[0][1].trim()
		
		List<WebResourceItem> items = []
		
		def stationMatcher = html =~ '(?s)<li class="clearfix play.*?">\\s*<a href="([a-zA-Z0-9-/]+)".*?title="(.*?)"'
		def itemsAdded = 0;
		for( int i = 0; i < stationMatcher.size() && (maxItems == -1 || itemsAdded < maxItems); i++ ) {
			String stationUrl = stationMatcher[i][1].trim()
			String stationTitle = stationMatcher[i][2].trim()			
			WebResourceItem item = new WebResourceItem(title: stationTitle, additionalInfo: ['stationUrl':stationUrl])
			items << item
			itemsAdded++			
		}
			
		return new WebResourceContainer(title: pageTitle, items: items)
	}
	
	ContentURLContainer extractUrl(WebResourceItem item, PreferredQuality requestedQuality) {		
		String stationUrl = item.getAdditionalInfo()['stationUrl']
		assert stationUrl != null
		
		String playerHtml = new URL("http://tunein.com$stationUrl").getText()
		
		def jsonMatcher = playerHtml =~ '(?s)TuneIn\\.payload =(.*?)TuneIn\\.'
		String jsonCode = jsonMatcher[0][1].trim()
		
		def json = new JsonSlurper().parseText(jsonCode)
		Map jsonResult = (Map) json;
		Map broadcastJson = jsonResult['Station']['broadcast']
		// thubmnail
		String thumbnailUrl = broadcastJson['SquareLogo']
		// streams, get required quality
		String streamUrl = getStreamUrl(jsonResult)
		if(streamUrl == null) {
			// try to load external stream URLs
			String extStreamsUrl = broadcastJson['StreamUrl']
			if(extStreamsUrl != null) {
				String streamsDef = new URL(extStreamsUrl).getText()
				def jsonMatcher2 = streamsDef =~ '(?s)(\\{.*?)\\);'
				String jsonCode2 = jsonMatcher2[0][1].trim()
				def json2 = new JsonSlurper().parseText(jsonCode2)
				Map jsonResult2 = (Map) json2;
				streamUrl = getStreamUrl(jsonResult2)
			} 
			if(streamUrl == null) {
				log('No suitable streams found')
				return null
			}
		}
		return new ContentURLContainer(fileType: MediaFileType.AUDIO, contentUrl: streamUrl, thumbnailUrl: thumbnailUrl, live: true)
	}
	
	private String getStreamUrl(Map jsonResult) {
		List streams = jsonResult['Streams']
		// only get streams that are represented as playlists and are live streams
		streams = streams.findAll { it -> it['Type'] == 'Live' && ['MP3','Windows','AAC'].contains(it['MediaType']) && it['Url'].indexOf('adType') == -1 }
		if( streams.size() > 0 ) {
			// ignore quality, audio bitrate is too low to make any difference, deliver the best available
			streams = streams.sort { it -> it['Bandwidth'].toInteger() }
			Map selectedStream = streams.last()
			String streamUrl = selectedStream['Url']
			boolean hasPlaylist = Boolean.valueOf (selectedStream['HasPlaylist'])
			if(hasPlaylist) {
				return getUrlFromPlaylist(new URL(streamUrl))
			} else {
				return streamUrl
			}
		} else {
			return null
		}
	}
	
	/**
	 * Supports m3u playlists ATM
	 */
	protected getUrlFromPlaylist(URL playlistUrl) {
		assert playlistUrl != null
		
		String playlist = playlistUrl.getText()
		if(playlist.toLowerCase().startsWith("<asx")) {
			// asx playlists not supported
			return null			
		} else {
			// assume m3u playlist
		    List lines = playlist.readLines()
			String url = lines[0]
			if(url.startsWith ('[playlist]') ) {
				for(String line : lines) {
					if(line.startsWith('File1')) {
						url = line.substring(line.indexOf ('=') + 1).trim()
						break
					}
				}
			}
			if (url.endsWith('.')) {
				url = url.substring(0, url.size()-1)
			}
			if(url.indexOf('.m3u') > -1 || url.indexOf('.pls') > -1) {
				return getUrlFromPlaylist(new URL(url))
			}
			return url
		}
	}
	
	static void main(args) {
		// this is just to test
		TuneIn extractor = new TuneIn()
				
		assert extractor.extractorMatches( new URL("http://tunein.com/radio/London-United-Kingdom-r100780/?qlc=1") )
		assert !extractor.extractorMatches( new URL("http://google.com/feeds/api/standardfeeds/top_rated?time=today") )
		
		//WebResourceContainer container = extractor.extractItems( new URL("http://tunein.com/radio/London-United-Kingdom-r100780/?qlc=1"), -1) // with embedded streams
		//WebResourceContainer container = extractor.extractItems( new URL("http://tunein.com/radio/search/05301/"), -1) // with external streams
		WebResourceContainer container = extractor.extractItems( new URL("http://tunein.com/radio/Trance-g403/"), -1)
		
		
		println container
		
		ContentURLContainer result = extractor.extractUrl(container.getItems()[2], PreferredQuality.MEDIUM)
		print result
		
		//println extractor.getUrlFromPlaylist (new URL("http://stream.radiotime.com/listen.stream?streamId=1045956&rti=dihyG20zJlYQXB09HBdHXE8gQV4QHBwvSwUAV11OJg0ZThZ7ZFBYDFEDJn8QRlU0CwdPUFNDRG5FEFF0Ek5dEFYSXXRqGAVEBUUEUlhRYQ0%3d%7e%7e%7e"))
	}
}